感謝大家對IT十八掌大數據的支持,今天的作業如下:
按照老師講的,自己練習項目
--------------------------------------------------------------------------------------------------------
項目源碼:
package com.it18zhang.client;
import java.net.Socket;
/**
* 通信類,單例類
*/
public class Comm {
private String ip = "192.168.12.2";
private int port = 1234 ;
private static Comm instance ;
private Socket sock ;
private ReceiverThread receiver ;
private SenderThread sender ;
public Socket getSock() {
return sock;
}
public void setSock(Socket sock) {
this.sock = sock;
}
/**
* 返回單例對象
*/
public static Comm getInstance(MyWindow window){
if(instance == null){
instance = new Comm(window);
}
return instance ;
}
private Comm(MyWindow window) {
try {
sock = new Socket(ip, port);
//啓動後臺接收線程
receiver = new ReceiverThread(sock, window);
receiver.start();
//啓動後臺發送線程
sender = new SenderThread(sock);
sender.start();
}
catch (Exception e) {
}
}
public String getIp() {
return ip;
}
public void setIp(String ip) {
this.ip = ip;
}
public int getPort() {
return port;
}
public void setPort(int port) {
this.port = port;
}
/**
* 發送消息
*/
public void sendText(String txt) {
sender.setTxt(txt);
}
}
package com.it18zhang.client;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JList;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.JTextArea;
import javax.swing.table.AbstractTableModel;
import javax.swing.table.TableModel;
/**
* 窗體
*/
public class MyWindow extends JFrame {
private static final long serialVersionUID = -6944831795769317874L;
// 歷史記錄
private JTextArea taHistory;
//滾動面板
private JScrollPane spHistory ;
// 內容
private JTextArea taContent;
// 發送按鈕
private JButton btnSend;
// 聯繫人列表
private JList listContacts;
private JTable table;
public MyWindow() {
ini();
}
// 初始化
private void ini() {
//
this.setSize(600, 400);
this.setLocation(200, 200);
// 絕對佈局
this.setLayout(null);
//聯繫人表格
table = new JTable();
table.setBounds(500, 0, 100, 400);
this.add(table);
//聊天記錄區
taHistory = new JTextArea();
taHistory.setEditable(false);
taHistory.setBounds(0, 0, 480, 300);
//
spHistory = new JScrollPane(taHistory);
spHistory.setBounds(0, 0, 480, 300);
this.add(spHistory);
//輸入區
taContent = new JTextArea();
taContent.setBounds(0, 310,450, 90);
this.add(taContent);
//發送按鈕
btnSend = new JButton("發送");
btnSend.setBounds(460,350 , 50, 50);
btnSend.addMouseListener(new MouseAdapter() {
public void mouseClicked(MouseEvent e) {
//發送消息
String txt = taContent.getText();
Comm comm = Comm.getInstance(MyWindow.this);
comm.sendText(txt);
//清空輸入區
taContent.setText("");
}
});
this.add(btnSend);
this.setVisible(true);
// 事件
this.addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e) {
System.exit(-1);
}
});
}
/**
* 刷新聯繫人
*/
public void refreshFriendsUI(final List<String> friends) {
//去重
Set<String> set = new HashSet<String>(friends);
//
final List<String> newclients = new ArrayList<String>(set);
//
TableModel dataModel = new AbstractTableModel() {
public int getColumnCount() {
return 1;
}
public int getRowCount() {
return newclients.size();
}
public Object getValueAt(int row, int col) {
return newclients.get(row);
}
};
table.setModel(dataModel);
}
/**
* 更新聊天記錄
*/
public void updateHistory(String addr, String line) {
//原文
String old = taHistory.getText();
StringBuilder builder = new StringBuilder();
builder.append(old);
builder.append("\r\n");
builder.append(addr);
builder.append("\r\n");
builder.append(line);
builder.append("\r\n");
//更新聊天區域
taHistory.setText(builder.toString());
}
/**
* 追加內容到聊天區域
*/
public void appendCont2History(String cont) {
String old = taHistory.getText() ;
StringBuilder builder = new StringBuilder();
builder.append(old);
builder.append("\r\n");
builder.append(cont);
String newCont = builder.toString();
taHistory.setText(newCont);
}
}
package com.it18zhang.client;
public class QQ {
public static void main(String[] args) {
MyWindow window = new MyWindow();
Comm comm = Comm.getInstance(window);
}
}
package com.it18zhang.client;
import java.io.InputStream;
import java.net.Socket;
import java.util.List;
import com.it18zhang.common.Message;
import com.it18zhang.common.MessageFactory;
import com.it18zhang.server.LatestFriendsServerMessage;
import com.it18zhang.server.TalkServerMessage;
import com.it18zhang.util.SocketUtil;
/**
* 接收者線程
*/
public class ReceiverThread extends Thread {
//客戶端UI
private MyWindow window ;
//sock
private Socket sock ;
//輸入流,讀取服務器回傳的信息
private InputStream is ;
public ReceiverThread(Socket sock,MyWindow window){
try {
this.sock = sock ;
this.window = window ;
is = sock.getInputStream() ;
}
catch (Exception e) {
e.printStackTrace();
}
}
public void run() {
try {
//讀取消息類型
//讀取文件長度
//讀取消息內容
while(true){
processMessage();
}
}
catch (Exception e) {
}
}
/**
* 處理消息
*/
private void processMessage() {
Message msg = MessageFactory.parsePack(is);
int type = msg.getType();
switch(type){
//刷新列表
case Message.MESSAGE_TYPE_SERVER_REFRESHFRIENDS:
List<String> list = (List<String>)((LatestFriendsServerMessage)msg).getData();
window.refreshFriendsUI(list);
break ;
//服務器對話
case Message.MESSAGE_TYPE_SERVER_TALK:
String str = (String) ((TalkServerMessage)msg).getData();
window.appendCont2History(str);
break ;
}
}
}
package com.it18zhang.client;
import com.it18zhang.common.Message;
/**
* 客戶端請求刷新好友列表消息
*/
public class RequestFriendsClientMessage extends Message {
public RequestFriendsClientMessage() {
this.setType(Message.MESSAGE_TYPE_CLIENT_REQUESTFRIENDS);
}
/**
* 重寫該方法,該消息沒有消息長度和消息內容。
*/
public byte[] genMessagePack() {
byte[] bytes = new byte[1];
bytes[0] = (byte)this.getType();
return bytes ;
}
/**
* 無需實現
*/
public Object getData() {
return null;
}
}
package com.it18zhang.client;
import java.io.IOException;
import java.io.OutputStream;
import java.net.Socket;
import com.it18zhang.util.SocketUtil;
/**
* 發送者線程
*/
public class SenderThread extends Thread {
private String txt ;
private OutputStream os ;
public SenderThread(Socket sock){
try {
this.os = sock.getOutputStream() ;
}
catch (IOException e) {
e.printStackTrace();
}
}
public String getTxt() {
return txt;
}
public void setTxt(String txt) {
this.txt = txt;
}
public void run() {
while(true){
if(txt != null){
//發送
SocketUtil.writeMessage(os, txt);
txt = null ;
}
}
}
}
package com.it18zhang.client;
import com.it18zhang.common.Message;
/**
* 客戶端輸入的話語消息
*/
public class TalkClientMessage extends Message {
public TalkClientMessage(String text) {
//處理消息
this.setType(Message.MESSAGE_TYPE_CLIENT_TALK) ;
byte[] bytes = text.getBytes() ;
this.setContent(bytes);
}
/**
* 無需實現
*/
public Object getData() {
return null;
}
}
package com.it18zhang.common;
import com.it18zhang.util.SocketUtil;
/**
* 消息
*/
public abstract class Message {
//客戶端發送talk
public static final int MESSAGE_TYPE_CLIENT_TALK = 0 ;
//客戶端請求好友列表(手動刷新)
public static final int MESSAGE_TYPE_CLIENT_REQUESTFRIENDS = 1 ;
//服務端推送刷新好友列表
public static final int MESSAGE_TYPE_SERVER_REFRESHFRIENDS = 2 ;
//推送某個客戶端的說的話
public static final int MESSAGE_TYPE_SERVER_TALK = 3 ;
//消息類型
private int type ;
//消息長度
private int length ;
//消息內容
private byte[] content ;
//原生報文
private byte[] pack ;
public Message(){
}
public int getType() {
return type;
}
public void setType(int type) {
this.type = type;
}
public int getLength() {
return length;
}
public void setLength(int length) {
this.length = length;
}
public byte[] getContent() {
return content;
}
public void setContent(byte[] content) {
this.content = content;
this.length = content.length ;
}
public byte[] getPack() {
return pack;
}
public void setPack(byte[] pack) {
this.pack = pack;
}
/**
* 生成整個報文,含有頭信息 + 消息本身
*/
public byte[] genMessagePack(){
pack = new byte[1 + 4 + length];
//類型
pack[0] = (byte)type ;
//數組複製
byte[] lenArr = SocketUtil.int2ByteArr(length);
System.arraycopy(lenArr, 0, pack, 1, 4);
//內容複製
System.arraycopy(content, 0, pack, 5, length);
return pack ;
}
public abstract Object getData() ;
}
package com.it18zhang.common;
import java.io.InputStream;
import com.it18zhang.server.LatestFriendsServerMessage;
import com.it18zhang.server.MessageServer;
import com.it18zhang.server.TalkServerMessage;
import com.it18zhang.util.SocketUtil;
/**
* 消息工廠
*/
public class MessageFactory {
/**
* 轉換報文,返回服務器端的消息對象,該方法只給服務器使用
*/
public static Message tranformPack(InputStream is){
Message msg = null ;
try {
//消息類型
int type = is.read();
int length = 0 ;
byte[] cont = null ;
switch(type){
//客戶端talk消息
case Message.MESSAGE_TYPE_CLIENT_TALK:
//消息長度
length = SocketUtil.readMsglength(is);
cont = SocketUtil.readMessage(is, length);
//轉換成服務器talk
msg = new TalkServerMessage(length,cont);
break ;
//客戶端刷新好友
case Message.MESSAGE_TYPE_CLIENT_REQUESTFRIENDS:
msg = new LatestFriendsServerMessage(MessageServer.friends);
break ;
}
//返回消息
return msg ;
}
catch (Exception e) {
}
return null ;
}
/**
* 解析報文,提取消息內容,只給客戶端使用
*/
public static Message parsePack(InputStream is){
Message msg = null;
try {
//消息類型
int type = is.read();
int length = 0 ;
byte[] cont = null ;
//消息長度
length = SocketUtil.readMsglength(is);
cont = SocketUtil.readMessage(is, length);
switch(type){
//客戶端talk消息
case Message.MESSAGE_TYPE_SERVER_TALK:
msg = new TalkServerMessage(length, cont);
break ;
//客戶端刷新好友
case Message.MESSAGE_TYPE_SERVER_REFRESHFRIENDS:
msg = new LatestFriendsServerMessage(cont);
break ;
}
//返回消息
return msg ;
}
catch (Exception e) {
}
return null ;
}
}
package com.it18zhang.server;
import java.io.ByteArrayOutputStream;
import java.io.ObjectOutputStream;
import java.util.List;
import com.it18zhang.common.Message;
import com.it18zhang.util.SocketUtil;
/**
* 服務器端消息,最新的好友列表
*/
public class LatestFriendsServerMessage extends Message {
public LatestFriendsServerMessage(List<String> list){
this.setType(MESSAGE_TYPE_SERVER_REFRESHFRIENDS);
this.setContent(serialFriends(list));
}
public LatestFriendsServerMessage(byte[] content){
this.setType(MESSAGE_TYPE_SERVER_REFRESHFRIENDS);
this.setContent(content);
}
/**
* 串行好友列表
*/
private byte[] serialFriends(List<String> list){
try {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos);
oos.writeObject(list);
oos.close();
baos.close();
return baos.toByteArray();
}
catch (Exception e) {
e.printStackTrace();
}
return null ;
}
/**
* 取得消息中的數據部分,反串行化。
*/
public Object getData() {
return SocketUtil.deserializeFriends(getContent());
}
}
package com.it18zhang.server;
import java.io.IOException;
import java.net.Socket;
import com.it18zhang.common.Message;
import com.it18zhang.util.SocketUtil;
/**
* 服務器消息推送器
*/
public class MessagePusher {
/**
* 推送消息
*/
public static void push(final Message msg){
//遍歷所有socket
for(final Socket s : MessageServer.allSockets){
new Thread(){
public void run() {
try {
SocketUtil.writeMessage(s.getOutputStream(), msg);
}
catch (IOException e) {
e.printStackTrace();
}
}
}.start();
}
}
}
package com.it18zhang.server;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.List;
import com.it18zhang.util.SocketUtil;
/**
*
*/
public class MessageServer {
//客戶端集合
public static List<String> friends = new ArrayList<String>();
//存放所有socket
public static List<Socket> allSockets = new ArrayList<Socket>();
//啓動
public void start(int port){
try {
ServerSocket ss = new ServerSocket(port);
System.out.println("服務器啓動了!!");
while(true){
Socket sock = ss.accept();
//添加好友
friends.add(SocketUtil.getAddr(sock));
//維護socket集合
allSockets.add(sock);
MessagePusher.push(new LatestFriendsServerMessage(friends));
//分配線程,單獨處理
new ServerReceiverThread(sock).start();
}
}
catch (Exception e) {
}
}
}
package com.it18zhang.server;
import java.io.IOException;
import java.io.InputStream;
import java.net.Socket;
import com.it18zhang.common.Message;
import com.it18zhang.common.MessageFactory;
import com.it18zhang.util.SocketUtil;
import sun.awt.windows.ThemeReader;
/**
* 服務器接收線程.
*/
public class ServerReceiverThread extends Thread {
private Socket sock ;
private InputStream is ;
public ServerReceiverThread(Socket sock) {
try {
this.sock = sock ;
this.is = sock.getInputStream() ;
}
catch (Exception e) {
e.printStackTrace();
}
}
public void run() {
while(true){
//
try {
//轉成服務器消息
Message msg = MessageFactory.tranformPack(is);
//
int type = msg.getType();
switch(type){
case Message.MESSAGE_TYPE_SERVER_TALK :
MessagePusher.push(msg);
break ;
case Message.MESSAGE_TYPE_SERVER_REFRESHFRIENDS:
MessagePusher.push(msg);
break ;
}
}
catch (Exception e) {
e.printStackTrace();
}
}
}
}
package com.it18zhang.server;
public class StartServer {
public static void main(String[] args) {
//消息服務器
MessageServer server = new MessageServer();
//啓動消息服務器
server.start(1234);
}
}
package com.it18zhang.server;
import com.it18zhang.common.Message;
/**
* 服務器端推送給客戶端的talk消息
*/
public class TalkServerMessage extends Message {
public TalkServerMessage(String text){
//處理消息
this.setType(Message.MESSAGE_TYPE_SERVER_TALK) ;
byte[] bytes = text.getBytes() ;
this.setContent(bytes);
}
/**
* 直接通過原生報文構造消息對象
*/
public TalkServerMessage(byte[] pack){
this.setPack(pack);
pack[0] = Message.MESSAGE_TYPE_SERVER_TALK ;
}
/**
* 直接通過原生報文構造消息對象
*/
public TalkServerMessage(int length,byte[] content){
this.setType(Message.MESSAGE_TYPE_SERVER_TALK) ;
this.setContent(content);
}
public byte[] genMessagePack() {
if(getPack() != null){
return getPack();
}
else{
return super.genMessagePack();
}
}
public Object getData() {
return new String(getContent());
}
}
package com.it18zhang.util;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.net.Socket;
import java.util.List;
import com.it18zhang.common.Message;
/**
* 工具類
*/
public class SocketUtil {
/**
* 返回地址串
*/
public static String getAddr(Socket sock){
String ip = sock.getInetAddress().getHostName() ;
String ip0 = ip.substring(ip.lastIndexOf(".") + 1);
return ip0 + ":" + sock.getPort() ;
}
/**
* 從輸入流is的當前讀取byte,處理成消息類型.
* 0-好友列表 1-文本內容
*/
public static int readMsgType(InputStream is){
try {
return is.read() ;
}
catch (IOException e) {
e.printStackTrace();
}
return -1 ;
}
/**
* 讀取消息的長度
*/
public static int readMsglength(InputStream is){
try {
byte[] buf = new byte[4];
is.read(buf);
//最高位
int s3 = buf[0] << 24 ;
//次高位
int s2 = (buf[1] & 0xFF) << 16 ;
//次低位
int s1 = (buf[2] & 0xFF) << 8 ;
//最低位
int s0 = buf[3] & 0xFF ;
return s3 | s2 | s1 | s0 ;
}
catch (Exception e) {
e.printStackTrace();
}
return -1 ;
}
/**
* 讀取指定消息長度的內容
*/
public static byte[] readMessage(InputStream is,int len){
try {
byte[] bytes = new byte[len];
is.read(bytes);
return bytes ;
}
catch (IOException e) {
e.printStackTrace();
}
return null ;
}
/**
* 反序列化字節數組成好友列表
*/
public static List<String> deserializeFriends(byte[] bytes){
try {
ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(bytes));
List<String> list= (List<String>)ois.readObject();
ois.close();
return list ;
}
catch (Exception e) {
e.printStackTrace();
}
return null ;
}
/**
* 讀取字符串
*/
public static String readString(byte[] bytes){
return new String(bytes);
}
/**
* 使用指定的字符集讀取字符串
*/
public static String readString(byte[] bytes,String charset){
try {
return new String(bytes,charset);
}
catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
return null ;
}
/**
* 發送消息到os中
*/
public static void writeMessage(OutputStream os , String txt){
try {
byte[] bytes = txt.getBytes();
//先寫入字節數組的長度
os.write(int2ByteArr(bytes.length));
//再寫入字節
os.write(bytes);
}
catch (Exception e) {
e.printStackTrace();
}
}
/**
* 發送Message的原始報文到os中
*/
public static void writeMessage(OutputStream os , Message msg){
try {
os.write(msg.genMessagePack());
os.flush();
}
catch (Exception e) {
e.printStackTrace();
}
}
/**
* 整型轉換成字節數組
*/
public static byte[] int2ByteArr(int i){
byte[] bytes = new byte[4] ;
bytes[0] = (byte)(i >> 24) ;
bytes[1] = (byte)(i >> 16) ;
bytes[2] = (byte)(i >> 8) ;
bytes[3] = (byte)(i >> 0) ;
return bytes ;
}
}