【Java課程設計】仿QQ多人聊天室(基於TCP協議與JavaSwing)附下載

仿QQ多人聊天室

下載地址:鏈接:https://pan.baidu.com/s/1dLFjSxwTA4gL5lI0B4UGuQ 
提取碼:2qs0

有兩個項目,分別是服務器chatterServer和客戶端chatter,先開啓服務器,再開啓客戶端,默認8888端口。

先上圖(迴環測試,即自己和自己聊天):

實現的主要功能:

1、一對一聊天:連接到服務器的所有客戶端和另一個客戶端“一對一”聊天

2、多對多聊天:連接到服務器的所有客戶端在同一聊天室內聊天

使用到的Java技術:

1、Java圖形界面編程(JFrame和各種組件的使用)。還包括內部類,事件監聽。

2、Java IO。主要是字節流,聊天信息通過字節流編碼併發送、接收。

3、Java TCP通信。

4、Java多線程。

5、Java集合。其中一個重點是重寫equals方法和hashCode方法

6、Java異常處理

代碼結構:

客戶端

代碼分爲4個包:

com.chatter.GUI 和圖形界面相關的類,主要是GUI類
com.chatter.client 和客戶端後臺有關的類,主要是Client類
com.chatter.user 和用戶信息存儲有關的類,包含Message類(數據的打包和解包),User類(用戶信息,最後沒用),PersonRecord類(用於記錄和某個特定客戶端的聊天記錄),Record類(用於記錄用戶和每個其他用戶的聊天記錄)
com.chatter.main main方法

項目類圖(客戶端):

服務器只有一個類,和從客戶端複製過去的一些類

主要代碼如下:

UI模塊:

package com.chatter.gui;

import java.awt.*;
import java.awt.event.*;
import java.util.Collection;
import java.util.HashSet;

import javax.swing.*;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;

import com.chatter.client.Client;
import com.chatter.user.Message;
import com.chatter.user.PersonRecord;
import com.chatter.user.User;

public class GUI {
	Client client;
	
	JFrame frame;
	Container pane;
	JPanel window, up, left, center, down;
	
	//上方的軟件名
	private JLabel label;
	//左側選擇聊天對象的列表
	JList<String> list;
	DefaultListModel<String> dlm;
	//中間的聊天消息顯示
	JTextArea text;
	//下方輸入文本的區域
	public JTextField words;
	//發送按鈕
	JButton button;
	
	
	final int WINDOW_WIDTH = 480;
	final int WINDOW_HEIGHT = 640;
	
	final double CUT_RATE_LEFT = 0.30;
	
	
	public GUI() {
		frame = new JFrame();
	
		pane = frame.getContentPane();
		window = new JPanel(new BorderLayout());
		up = new UpperPanel();
		left = new JPanel();
		center = new JPanel();
		down = new JPanel(new BorderLayout());
		
		//上方的軟件圖標和名稱
		label = new JLabel();
		label.setPreferredSize(new Dimension(150,50));
		label.setFont(new Font("楷體", Font.PLAIN, 30));
		label.setText("GG聊天室");
		up.add(label);
		pane.add(BorderLayout.NORTH, up);
		
		//左側的聊天對象選擇欄
		dlm = new DefaultListModel<String>();
		list = new JList<String>(dlm);
		list.setPreferredSize(new Dimension((int)(CUT_RATE_LEFT*WINDOW_WIDTH), 400));
		
		list.setModel(dlm);
		dlm.clear();
		dlm.addElement("0000000000008888");
		list.setSelectedIndex(0);
		list.addListSelectionListener((ListSelectionListener) new ListListener());;
		left.add(list);
		pane.add(BorderLayout.WEST, left);
		
		//中心的聊天消息顯示
		text = new JTextArea();
		//text.setPreferredSize(new Dimension(280,2000));
		text.setEditable(true);
		text.setLineWrap(true);
		JScrollPane scrollpane1 = new JScrollPane(text);
		scrollpane1.setPreferredSize(new Dimension(300, 400));
		scrollpane1.createVerticalScrollBar().setVisible(false);
		scrollpane1.createHorizontalScrollBar().setVisible(true);
		center.add(scrollpane1);
		pane.add(BorderLayout.CENTER, center);
		
		//下方的聊天內容輸入
		words = new JTextField();
		//words.setText("你好,我已經加你爲好友\n,我們可以開始聊天了");
		words.setEditable(true);

		//words.setLineWrap(true);
		//words.setWrapStyleWord(true);
		JScrollPane scrollpane2= new JScrollPane(words);
		scrollpane2.setPreferredSize(new Dimension(350, 100));
		scrollpane2.createVerticalScrollBar().setVisible(false);
		scrollpane2.createHorizontalScrollBar().setVisible(true);
		//scrollpane2.setAutoscrolls(true);
		
		down.add(BorderLayout.WEST, scrollpane2);
		
		//發送按鈕
		button = new JButton("發送");
		BottonListener bl = new BottonListener();
		//bl.setTextField(words);
		button.addActionListener(bl);
		button.setPreferredSize(new Dimension(100, 30));
		down.add(BorderLayout.EAST, button);
		
		pane.add(BorderLayout.SOUTH, down);
		
		frame.setSize(WINDOW_WIDTH, WINDOW_HEIGHT);
		frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		frame.setVisible(true);
	}
	
	public void receive(Message msg) {
		System.out.println("收到消息,準備更新. msg Type: " + msg.getHead());
		if (msg.getHead() == 1) {
			String[] s = msg.getContent().split("[|]");
			Collection<PersonRecord> tmp_c = new HashSet<PersonRecord>();
			for(String str:s) {
				tmp_c.add(new PersonRecord(Long.parseLong(str)));
				System.out.println(str);
			}
			tmp_c.add(new PersonRecord(8888));
			System.out.println("新建列表內容");
			for (Object o:tmp_c.toArray()) {
				System.out.println(((PersonRecord)o).personId);
			}
			System.out.println("_______ENC");
			client.record.c.addAll(tmp_c);
			client.record.c.retainAll(tmp_c);
			System.out.println("加入後原列表內容");
			for (Object o:client.record.c.toArray()) {
				System.out.println(((PersonRecord)o).personId);
			}
			System.out.println("_______ENC2");
			
			//System.out.println("系統原有集合:"+(PersonRecord[])(client.record.c.toArray()));

			//System.out.println("新創建集合:"+ (PersonRecord[])(tmp_c.toArray()));
			
			
			for(PersonRecord pr: client.record.c) {
				if (dlm.contains(String.format("%016d" ,pr.personId)) == false)
					dlm.addElement(String.format("%016d" ,pr.personId));
			}
		}
		else if (msg.getHead() == 0)
			for(PersonRecord pr:client.record.c) {
				if(pr.personId == msg.getTransmitter())
					pr.chatRecord.append("對方:");
					pr.chatRecord.append(msg.getContent());
					pr.chatRecord.append("\n");
					text.setText(pr.chatRecord.toString());
			}
		else if (msg.getHead() == 99) {
			for(PersonRecord pr:client.record.c) {
				pr.chatRecord.append("對方:");
				pr.chatRecord.append(msg.getContent());
				pr.chatRecord.append("\n");
				text.setText(pr.chatRecord.toString());
			}
		}
	}
	public void send(String content) {
		System.out.println("正在發送給"+list.getSelectedValue()+" 內容:" + content);
		Message m = new Message();
		content.replaceAll("\\n", "");
		m.setContent(content);
		
		//0:普通消息
		if (Long.parseLong(list.getSelectedValue()) == 8888) {
			m.setHead(99);
			m.setReceiver(Long.parseLong(list.getSelectedValue()));
		}
		else {
			m.setHead(0);
			m.setReceiver(Long.parseLong(list.getSelectedValue()));
		}
		System.out.println("發送與接收者:" +m.getTransmitter() +" "+m.getReceiver());
		for(PersonRecord pr:client.record.c) {
			System.out.println("現有用戶id:"+pr.personId);
			if(pr.personId == m.getReceiver()) {
				System.out.println("正在添加聊天記錄");
				pr.chatRecord.append("你:");
				pr.chatRecord.append(m.getContent());
				pr.chatRecord.append("\n");
				text.setText(pr.chatRecord.toString());
			}
		}
		System.out.println("-------ready to send msg--------");
		client.send(m);
	}
	
	class BottonListener implements ActionListener{
		public void actionPerformed(ActionEvent e) {
			String str = words.getText();
			if(str.trim().length() == 0) return;
			words.setText("");
			send(str);
		}	
	}
	class ListListener implements ListSelectionListener{

		public void actionPerformed(ActionEvent e) {
			
			
		}

		public void valueChanged(ListSelectionEvent e) {
			for(PersonRecord pr:client.record.c) {
				if(pr.personId == Long.parseLong(list.getSelectedValue()))
					text.setText(pr.chatRecord.toString());
			}
			
		}
		
	}
	
	public void setClient(Client c) {
		this.client = c;
	}
}

客戶端後臺Client:

package com.chatter.client;

import java.io.*;
import java.io.IOException;
import java.net.*;

import com.chatter.gui.GUI;
import com.chatter.user.Message;
import com.chatter.user.PersonRecord;
import com.chatter.user.Record;

public class Client {
	private String ip;
	private int port;
	private long localId;
	private Socket socket;
	private PrintWriter pw;
	private BufferedReader br;
	private GUI gui;
	public Record record;
	public Client(String ip, int port) {
		this.ip = ip;
		this.port = port;
		
		try {
			socket = new Socket(ip, port);
			OutputStream os = socket.getOutputStream();
			OutputStreamWriter osw = new OutputStreamWriter(os, "utf-8");
			BufferedWriter bw = new BufferedWriter(osw);
			pw = new PrintWriter(bw, true);
			
			InputStream is = socket.getInputStream();
			InputStreamReader isr = new InputStreamReader(is, "utf-8");
			br = new BufferedReader(isr);
			localId = getLocalId(ip, port); 
			
			record = new Record();
			//record.c.add(new PersonRecord(8888));
			
		}catch (Exception e) {
			System.out.println("連接服務器失敗");
			System.exit(0);
		}
		
	}
	
	public boolean setGUI(GUI g) {
		if (g == null)
			return false;
		this.gui = g;
		return true;
	}
	
	
	public void start() {
		Thread t_receive = new Thread() {
			String line;
			public void run() {
				try {
					while((line = br.readLine()) != null) {
						try {
							System.out.print("收到新消息");
							System.out.println("\t" + line);
							Message msg = new Message(line);
							System.out.println("Before calling gui.receive: ");
							System.out.println(msg.getContent());
							gui.receive(msg);
						}catch(Exception e) {
							e.printStackTrace();
							System.out.println("Message encoding error");
						}
					}
				} catch (Exception e) {
					e.printStackTrace();
					System.out.println("服務器主動斷開連接。");
					System.exit(0);
				}
				
			}
		};
		t_receive.start();
	}
	static public long getLocalId(String IP, int port) {
		String[] ipSplit = IP.split("\\.");
		long[] arr = new long[5];
		for (int i=0; i<ipSplit.length; i++) {
			arr[i] = Integer.parseInt(ipSplit[i]);
		}
		arr[4] = port;
		return arr[4]+arr[3]*10000+arr[2]*10000000+arr[1]*1000000000L+arr[0]*10000000000000L;
	}
	
	
	public void send(Message msg) {
		if (msg.getHead() == 0) {
			System.out.println("Sending message to server...");
			msg.setTransmitter(localId);
			String str = msg.toString();
			System.out.println("\tmessage content: "+msg.getContent());
			pw.println(str);
		}
		else if (msg.getHead() == 99) {
			msg.setTransmitter(localId);
			
		}
	}
	
	public void setRecord(Record r) {
		this.record = r;
		record.c.add(new PersonRecord(8888));
	}
	
	
}


服務器Server:

package com.chatter.server;

import java.io.*;
import java.net.*;
import java.util.Collection;
import java.util.HashSet;

import com.chatter.user.Message;
import com.chatter.user.User;

public class Server {
	ServerSocket serverSocket;
	Collection<User> c = new HashSet<User>();
	
	public Server() {
		try {
			serverSocket = new ServerSocket(8888);
			
			while(true) {
				Socket socket = serverSocket.accept();
				long remoteID = getIDFromIP(socket.getInetAddress().getHostAddress());
				System.out.println(socket.getInetAddress() + "已連接");
				Thread thread = new Thread() {
					public void run() {
						try {
							OutputStream os = socket.getOutputStream();
							OutputStreamWriter osw = new OutputStreamWriter(os, "utf-8");
							BufferedWriter bw = new BufferedWriter(osw);
							PrintWriter pw = new PrintWriter(osw, true);
							
							InputStream is = socket.getInputStream();
							InputStreamReader isr = new InputStreamReader(is, "utf-8");
							BufferedReader br = new BufferedReader(isr);
							
							User u = new User(socket, getIDFromIP(socket.getInetAddress().getHostAddress()), br, pw);
						
							c.add(u);
							System.out.println(u.id + "已連接");
							sendUpdateMsg();
							//System.exit(0);
							
							String line;
							while((line = br.readLine()) != null) {
								Message msg = new Message(line);
								System.out.println("服務器收到消息: " + msg.toString());
								if (msg.getHead() == 0) {
									long id = msg.getReceiver();
									System.out.println("Receiver from raw msg: "+msg.getReceiver());
									//id = id*10000+8888;
									
									System.out.println("Sending to Client: "+id);
									for (User tmp: c) {
										System.out.println("服務器現有用戶: "+tmp.id);
										if (tmp.id == id) {
											System.out.println("Sending to Client: "+tmp.socket.getInetAddress());
											tmp.pw.println(msg.toString());
											pw.flush();
										}
									}
								}
								else if (msg.getHead() == 99) {
									for (User tmp: c) {
										tmp.pw.println(msg.toString());
									}
								}
							} 
						}catch(Exception e) {
							for (User u: c) {
								if (u.id == remoteID) {
									c.remove(u);
									sendUpdateMsg();
								}
							}
							e.printStackTrace();;
						}
					}					
				};
				thread.start();
			}
			
		} catch (Exception e) {
			e.printStackTrace();
			System.out.println("服務器開啓失敗");
			System.exit(0);
		}
		
	}
	
	private void sendUpdateMsg() {
		System.out.println("發送在線列表更新信息中...");
		Message msg = new Message();
		msg.setHead(1);
		msg.setTransmitter(0);
		StringBuffer s = new StringBuffer();
		for (User tmp:c) {
			s.append(String.format("%016d", tmp.id));
			s.append('|');
		}
		msg.setContent(s.toString());
		for (User tmp:c) {
			System.out.println("Sending to "+tmp.id);
			msg.setReceiver(tmp.id);
			tmp.pw.println(msg.toString());
		}
	}
	private long getIDFromIP(String IP) {
		try {
			String[] ipSplit = IP.split("\\.");
			long[] arr = new long[5];
			for (int i=0; i<ipSplit.length; i++) {
				arr[i] = Integer.parseInt(ipSplit[i]);
			}
			arr[4] = 8888;
			return arr[4]+arr[3]*10000+arr[2]*10000000+arr[1]*1000000000L+arr[0]*10000000000000L;
			
		}catch(Exception e) {
			e.printStackTrace();
		}
		return 0;
	}
}

個人作品,請勿轉載。

 

 

發佈了86 篇原創文章 · 獲贊 59 · 訪問量 4萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章