Java套接字编程-简单功能聊天室(群聊,私聊)

大致设计思路

客户端新建建socket与服务端建立连接,服务端每接收到一个连接都新建一个输入流和输出流,分别存储到对应的list列表里,使用多线程接收来自客户端的消息并处理,然后循环遍历所有连接广播消息。客户端则接收消息判断发送方和接收方,从而显示到对应的窗口。

图形界面设计

//最大的是一个
JTabbedPane tabbedPane;//默认添加了一个群聊的JTextArea,私聊的时候根据list监听动态添加
//左侧用了一个JList,为了动态添加数据,给他设置了一个动态模型
JList list;//显示用户列表,根据服务器发送的用户更新
DefaultListModel listmodel;//列表模型
//底部就是一个标签+文本框+俩按钮

组件定义

JFrame frame;//窗口
	JTextArea msg;//群聊对话框
	JTextField text_str_send;//消息发送框
	JButton sendBtn;//消息发送按钮
	JButton exitBtn;//退出按钮
	String user="";//用户名
	Socket socket;//套接字
	BufferedReader br;//输入流
	BufferedWriter bw;//输出流
	JTabbedPane tabbedPane;//对话列表
	JList list;//显示用户列表
	DefaultListModel listmodel;//列表模块
	ArrayList<oneForone> oneForones=new ArrayList<oneForone>();//存储私聊对象
	public static boolean user_is_alive=false;//判断用户是否还在

在这里插入图片描述

JScrollPane jsp=new JScrollPane(msg);//滚动条
		tabbedPane = new JTabbedPane(JTabbedPane.TOP);
		tabbedPane.setBounds(20, 35, 410, 368);
		tabbedPane.add("群聊",jsp);
		frame.getContentPane().add(tabbedPane);
		
		listmodel = new DefaultListModel();//动态模型
		list = new JList(listmodel);
		list.setBounds(440, 60, 92, 340);
		list.addMouseListener(new MouseAdapter() {//list的监听,当有双击事件就添加一个私聊窗口
	        public void mouseClicked(MouseEvent evt) {
	            if (evt.getClickCount() == 2) {//鼠标双击事件
	                String name=(String) list.getSelectedValue();//获取双击的名字
	                JTextArea msg=new JTextArea();
	                tabbedPane.add(name,msg);
	                oneForone oneforone=new oneForone(msg, name);
	                oneForones.add(oneforone);
	            }
	        }
	    });

系统消息格式

在这里插入图片描述
当用户进入聊天室和退出聊天室时,系统会发通知提醒

进入聊天室时

当用户进入聊天室会调用方法public void setConnection(Socket socket)
用于创建套接字连接这时会向服务器发送上线的 消息,服务器将这个消息处理转发给全部用户
客户端:

public void setConnection(Socket socket) {
		this.socket=socket;
		try {
			br=new BufferedReader(new 
					InputStreamReader(socket.getInputStream()));
			bw=new BufferedWriter(new
					OutputStreamWriter(socket.getOutputStream()));
			user_is_alive=true;
//			msg.append(another+"上线"+"\n");
			bw.write("join:"+user+"\n");
			bw.flush();
		
		Thread thread=new Thread(this);
		thread.start();
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

服务器:

if(strMsg.indexOf("join")==0) {//有人加入聊天室
						String user_=strMsg.split(":")[1];
						chatserver.user.addFirst(user_);//增加用户列
						String User="USER";
						for(int i=0;i<chatserver.user.size();i++)
								User+=(":"+chatserver.user.get(i));
						chatserver.msgList.add(User);
						strMsg="系统通知-"+strMsg.split(":")[1]+"-加入聊天室\n";
						System.out.println(user_+"join");
					}
退出聊天室时

用户会调用shutdown方法void shutdown()
依旧会向服务器发送退出的消息,服务器处理后转发,然后用户关闭进程
用户:

//退出
		void shutdown() {
			user_is_alive=false;
			try {
				bw.write("exit:"+user);
				bw.flush();
				bw.close();
				socket.close();
				this.dispose();
				System.exit(0);
			}catch (Exception e) {
				// TODO: handle exception
			}
			
		}

服务器:

if(strMsg.indexOf("exit")==0) {//有人退出聊天室
						chatserver.ins.remove(in);
						chatserver.outs.remove(out);
						String user_=strMsg.split(":")[1];
						//删除用户列
						for(int i=0;i<chatserver.user.size();i++)
							if(chatserver.user.get(i).equals(user_))
								chatserver.user.remove(i);
						String User="USER";
						for(int i=0;i<chatserver.user.size();i++)
							User+=(":"+chatserver.user.get(i));
						chatserver.msgList.add(User);
						strMsg="系统通知-"+strMsg.split(":")[1]+"-退出聊天室\n";
						System.out.println(user_+"exit");
						chatserver.msgList.addFirst(strMsg);
						break;
					}

用户列表的更新

当有用户进入聊天室和退出聊天室时右侧用户列表会跟新
在这里插入图片描述
服务器:
新建一个用户列表
当服务器接收到来自用户的上线请求时,添加到列表里,然后向客户端传输所有用户信息,格式USER:user1:user2:user3...

public static LinkedList<String> user=new LinkedList<String>();//当前在线用户列表
if(strMsg.indexOf("join")==0) {//有人加入聊天室
						String user_=strMsg.split(":")[1];
						chatserver.user.addFirst(user_);//增加用户列
						String User="USER";
						for(int i=0;i<chatserver.user.size();i++)
								User+=(":"+chatserver.user.get(i));
						chatserver.msgList.add(User);//向转发列表添加这条消息

用户端:
当接收到USER:开头的消息时根据消息体跟新在线用户

content_recv=br.readLine();//获取消息体
				//有人加入或者退出聊天室更新在线成员
				if(content_recv.indexOf("USER")==0) {
					String msString[]=content_recv.split(":");
					listmodel.removeAllElements();
					for(int i=1;i<msString.length;i++) {
						listmodel.addElement(msString[i]);//使用list的动态模型更新
					}
				}

消息格式和处理方式

消息格式:from|values|tofrom为发送方,values为消息体,to是接收方,群聊的to默认为“群聊”

发送

方法:获取当前标签页的标题,确定接收方,然后增加自身姓名和发送的消息为一个完整的消息体,然后发送

	//发送消息
		void sendmessage() {
			try {
				String to = tabbedPane.getTitleAt(tabbedPane.getSelectedIndex());//获取当前页
				String value=text_str_send.getText();
				String send_msg=user+"|"+value+"|"+to+"\n";
//				String content_send=user+":"+text_str_send.getText()+"\n";//消息体
				
				//当不是群聊时,在当前页显示自己发送的消息
				if(!to.equals("群聊")) {
					for(int i=0;i<oneForones.size();i++)
						if(oneForones.get(i).getName().equals(to)) {
							oneForones.get(i).getMsg().append(user+":"+value+"\n");
						}
				}
				
				bw.write(send_msg);
				bw.flush();
//				msg.append(content_send);
				text_str_send.setText("");
			}catch (Exception e) {
				// TODO: handle exception
			}
		}
接收

将消息按照|分割,确定消息类型,群聊则显示在群聊JTextarea里私聊则在私聊对象的列表里寻找

私聊对象

class oneForone extends JFrame{
	JTextArea msg;//私聊对象的对话框
	String name;//私聊对象姓名
	public oneForone(JTextArea msg,String name) {
		this.name=name;
		this.msg=msg;
	}
	public JTextArea getMsg() {
		return msg;
	}
	public String getName() {
		return name;
	}
}

处理消息体

				content_recv=br.readLine();
				//有人加入或者退出聊天室更新在线成员
				if(content_recv.indexOf("USER")==0) {
					String msString[]=content_recv.split(":");
					listmodel.removeAllElements();
					for(int i=1;i<msString.length;i++) {
						listmodel.addElement(msString[i]);
					}
				}else {
					if(content_recv.indexOf("系统通知")==0) {
						msg.append(content_recv+"\n");
					}else {
						String Temp[]=content_recv.split("\\|");//使用|作为分隔符要用\\转义
						if(Temp.length==3) {
							if(Temp[2].equals("群聊"))
								msg.append(Temp[0]+":"+Temp[1]+"\n");
							else if(Temp[2].equals(user)){//私聊
								boolean flag=false;
								for(int i=0;i<oneForones.size();i++) {
									if(oneForones.get(i).getName().equals(Temp[0])) {
										oneForones.get(i).getMsg().append(Temp[0]+":"+Temp[1]+"\n");
										flag=true;
										break;
									}
								}
								if(!flag) {//没有打开私聊窗口则打开
									JTextArea msg=new JTextArea();
					                tabbedPane.add(Temp[0],msg);
					                oneForone oneforone=new oneForone(msg, Temp[0]);
					                oneForones.add(oneforone);
					                msg.append(Temp[0]+":"+Temp[1]+"\n");
								}
								System.out.print(Temp[0]+":"+Temp[1]+"\n");
							}
						}
					}
				}

完整代码:
chatclient.java

public class chatclient extends JFrame{
	private JTextField id;
	private JTextField pasword;
	private JFrame frame;
	public chatclient() {
		frame=new JFrame("用户登录");
		frame.getContentPane().setLayout(null);
		
		JLabel lblNewLabel = new JLabel("姓名:");
		lblNewLabel.setBounds(50, 41, 99, 23);
		frame.getContentPane().add(lblNewLabel);
		
		id = new JTextField();
		id.setBounds(131, 42, 179, 21);
		frame.getContentPane().add(id);
		id.setColumns(10);
		
		JLabel lblNewLabel_1 = new JLabel("密码:");
		lblNewLabel_1.setBounds(50, 91, 99, 23);
		frame.getContentPane().add(lblNewLabel_1);
		
		pasword = new JTextField();
		pasword.setBounds(131, 92, 179, 21);
		frame.getContentPane().add(pasword);
		pasword.setColumns(10);
		
		JButton btnNewButton = new JButton("登录");
		btnNewButton.setBackground(Color.WHITE);
		btnNewButton.setBounds(152, 168, 93, 23);
		btnNewButton.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent arg0) {
				// TODO Auto-generated method stub
				String name=id.getText();
				try {
					Socket socket=new Socket("localhost",8888);
					chatting_interface_multi clInterface_multi=new chatting_interface_multi(name);
					clInterface_multi.setConnection(socket);
				} catch (Exception e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
				frame.dispose();
			}
		});
		
		frame.getContentPane().add(btnNewButton);
		frame.setVisible(true);
		frame.setSize(410,298);
		frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		
	}
	
	public static void main(String[] args) {
		new chatclient();
	}
}

chatserver.java

package 聊天室2_多人聊天室;

import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.*;


public class chatserver {
	public static ArrayList<BufferedReader> ins=new ArrayList<BufferedReader>();
	public static ArrayList<BufferedWriter> outs=new ArrayList<BufferedWriter>();
	public static LinkedList<String> msgList=new LinkedList<String>();
	public static LinkedList<String> user=new LinkedList<String>();//当前在线用户列表
	public static Thread t_send,t_accept;
	public static ServerSocket serverSocket;
	public chatserver() {
		
		try {
			serverSocket=new ServerSocket(8888);
			AcceptSocket as=new AcceptSocket();
			SendSocket ss=new SendSocket();
//			Socket socket=serverSocket.accept();
//			chatting_interface_multi srvif=new
//					chatting_interface_multi("服务器端");
//			srvif.setConnection(socket);
			t_accept=new Thread(as);
			t_accept.start();
			
			t_send=new Thread(ss);
			t_send.start();
			
			System.out.println("服务器启动...........");
			System.out.println("启动完成");
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	public static void main(String[]args) {
		new chatserver();
	}
}

class AcceptSocket implements Runnable{
	public void run() {
		while(chatserver.t_accept.isAlive()) {
			try {
				Socket socket=chatserver.serverSocket.accept();
				if(socket!=null) {
					BufferedReader in=new
							BufferedReader(new InputStreamReader(socket.getInputStream()));
					chatserver.ins.add(in);
					BufferedWriter out=new
							BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
					chatserver.outs.add(out);
					new GetMsgFromClient(in,out).start();
				}
			}catch(Exception e) {
				
			}
		}
	}
}

class GetMsgFromClient extends Thread{
	BufferedReader in;BufferedWriter out;
	public GetMsgFromClient(BufferedReader in,BufferedWriter out) {
		this.in=in;this.out=out;
	}
	public void run() {
		while(this.isAlive()) {
			try {
				String strMsg=in.readLine();
				if(strMsg!=null) {
					if(strMsg.indexOf("exit")==0) {//有人退出聊天室
						chatserver.ins.remove(in);
						chatserver.outs.remove(out);
						String user_=strMsg.split(":")[1];
						//删除用户列
						for(int i=0;i<chatserver.user.size();i++)
							if(chatserver.user.get(i).equals(user_))
								chatserver.user.remove(i);
						String User="USER";
						for(int i=0;i<chatserver.user.size();i++)
							User+=(":"+chatserver.user.get(i));
						chatserver.msgList.add(User);
						strMsg="系统通知-"+strMsg.split(":")[1]+"-退出聊天室\n";
						System.out.println(user_+"exit");
						chatserver.msgList.addFirst(strMsg);
						break;
					}
					
					if(strMsg.indexOf("join")==0) {//有人加入聊天室
						String user_=strMsg.split(":")[1];
						chatserver.user.addFirst(user_);//增加用户列
						String User="USER";
						for(int i=0;i<chatserver.user.size();i++)
								User+=(":"+chatserver.user.get(i));
						chatserver.msgList.add(User);
						strMsg="系统通知-"+strMsg.split(":")[1]+"-加入聊天室\n";
						System.out.println(user_+"join");
					}
					
					chatserver.msgList.addFirst(strMsg);
				}
			} catch (Exception e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
				break;
			}
			
		}
	}
}
class SendSocket implements Runnable{

	@Override
	public void run() {
		// TODO Auto-generated method stub
		while(chatserver.t_send.isAlive()) {
			try {
				if(!chatserver.msgList.isEmpty()) {
					String string=chatserver.msgList.removeLast();
					for(int i=0;i<chatserver.outs.size();i++) {
						chatserver.outs.get(i).write(string+"\n");
						chatserver.outs.get(i).flush();
//						System.out.println(chatserver.outs.get(i).toString());
					}
				}
			}catch (Exception e) {
				// TODO: handle exception
			}
		}
	}
}

chatting_interface_multi.java

package 聊天室2_多人聊天室;

import java.awt.BorderLayout;
import java.awt.EventQueue;
import java.awt.FontFormatException;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.border.EmptyBorder;
import javax.swing.event.CaretEvent;
import javax.swing.event.CaretListener;
import javax.swing.JTextArea;
import javax.swing.JLabel;
import javax.swing.JTextField;
import javax.swing.DefaultListModel;
import javax.swing.JButton;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.Socket;
import java.util.ArrayList;
import java.awt.event.ActionEvent;
import javax.swing.JTabbedPane;
import javax.swing.JList;

public class chatting_interface_multi extends JFrame implements ActionListener,Runnable{
	JFrame frame;//窗口
	JTextArea msg;//群聊对话框
	JTextField text_str_send;//消息发送框
	JButton sendBtn;//消息发送按钮
	JButton exitBtn;//退出按钮
	String user="";//用户名
	Socket socket;//套接字
	BufferedReader br;//输入流
	BufferedWriter bw;//输出流
	JTabbedPane tabbedPane;//对话列表
	JList list;//显示用户列表
	DefaultListModel listmodel;//列表模块
	ArrayList<oneForone> oneForones=new ArrayList<oneForone>();//存储私聊对象
	public static boolean user_is_alive=false;//判断用户是否还在
	public chatting_interface_multi(String user) {
		this.user=user;
		frame=new JFrame("用户:"+user);
		
		frame.getContentPane().setLayout(null);
		
		msg = new JTextArea();
		
		
		JLabel label = new JLabel("输入消息");
		label.setBounds(20, 413, 85, 15);
		frame.getContentPane().add(label);
		
		text_str_send = new JTextField();
		text_str_send.setBounds(78, 413, 297, 21);
		frame.getContentPane().add(text_str_send);
		text_str_send.setColumns(10);
		text_str_send.addActionListener(this);
		
		
		sendBtn = new JButton("发送");
		sendBtn.setBounds(385, 412, 70, 23);
		frame.getContentPane().add(sendBtn);
		sendBtn.addActionListener(this);
		
		
		exitBtn = new JButton("退出");
		exitBtn.setBounds(465, 413, 65, 23);
		frame.getContentPane().add(exitBtn);
		
		JScrollPane jsp=new JScrollPane(msg);//滚动条
		tabbedPane = new JTabbedPane(JTabbedPane.TOP);
		tabbedPane.setBounds(20, 35, 410, 368);
		tabbedPane.add("群聊",jsp);
		frame.getContentPane().add(tabbedPane);
		
		listmodel = new DefaultListModel();
		list = new JList(listmodel);
		list.setBounds(440, 60, 92, 340);
		list.addMouseListener(new MouseAdapter() {
	        public void mouseClicked(MouseEvent evt) {
	            if (evt.getClickCount() == 2) {//鼠标双击事件
	                String name=(String) list.getSelectedValue();//获取双击的名字
	                JTextArea msg=new JTextArea();
	                tabbedPane.add(name,msg);
	                oneForone oneforone=new oneForone(msg, name);
	                oneForones.add(oneforone);
	            }
	        }
	    });

		frame.getContentPane().add(list);
		exitBtn.addActionListener(this);
		
		frame.setVisible(true);
		frame.setSize(558,487);
		frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
	}

	public void setConnection(Socket socket) {
		this.socket=socket;
		try {
			br=new BufferedReader(new 
					InputStreamReader(socket.getInputStream()));
			bw=new BufferedWriter(new
					OutputStreamWriter(socket.getOutputStream()));
			user_is_alive=true;
//			msg.append(another+"上线"+"\n");
			bw.write("join:"+user+"\n");
			bw.flush();
		
		Thread thread=new Thread(this);
		thread.start();
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	@Override
	public void run() {
		// TODO Auto-generated method stub
		try {
			String content_recv;
			while(user_is_alive) {
				content_recv=br.readLine();
				//有人加入或者退出聊天室更新在线成员
				if(content_recv.indexOf("USER")==0) {
					String msString[]=content_recv.split(":");
					listmodel.removeAllElements();
					for(int i=1;i<msString.length;i++) {
						listmodel.addElement(msString[i]);
					}
				}else {
					if(content_recv.indexOf("系统通知")==0) {
						msg.append(content_recv+"\n");
					}else {
						String Temp[]=content_recv.split("\\|");//使用|作为分隔符要用\\转义
						if(Temp.length==3) {
							if(Temp[2].equals("群聊"))
								msg.append(Temp[0]+":"+Temp[1]+"\n");
							else if(Temp[2].equals(user)){//私聊
								boolean flag=false;
								for(int i=0;i<oneForones.size();i++) {
									if(oneForones.get(i).getName().equals(Temp[0])) {
										oneForones.get(i).getMsg().append(Temp[0]+":"+Temp[1]+"\n");
										flag=true;
										break;
									}
								}
								if(!flag) {//没有打开私聊窗口则打开
									JTextArea msg=new JTextArea();
					                tabbedPane.add(Temp[0],msg);
					                oneForone oneforone=new oneForone(msg, Temp[0]);
					                oneForones.add(oneforone);
					                msg.append(Temp[0]+":"+Temp[1]+"\n");
								}
								System.out.print(Temp[0]+":"+Temp[1]+"\n");
							}
						}
					}
				}
			}
		}catch(Exception e) {
//			e.printStackTrace();
			return;
		}
	}

	public void actionPerformed(ActionEvent e) {
		// TODO Auto-generated method stub
		if(e.getSource()==sendBtn||e.getSource()==text_str_send) {
			//发送消息
			sendmessage();
		}else if(e.getSource()==exitBtn) {
			//退出
			shutdown();
		}
	}

	//发送消息
		void sendmessage() {
			try {
				String to = tabbedPane.getTitleAt(tabbedPane.getSelectedIndex());//获取当前页
				String value=text_str_send.getText();
				String send_msg=user+"|"+value+"|"+to+"\n";
//				String content_send=user+":"+text_str_send.getText()+"\n";//消息体
				
				if(!to.equals("群聊")) {
					for(int i=0;i<oneForones.size();i++)
						if(oneForones.get(i).getName().equals(to)) {
							oneForones.get(i).getMsg().append(user+":"+value+"\n");
						}
				}
				
				bw.write(send_msg);
				bw.flush();
//				msg.append(content_send);
				text_str_send.setText("");
			}catch (Exception e) {
				// TODO: handle exception
			}
		}
		
		//退出
		void shutdown() {
			user_is_alive=false;
			try {
				bw.write("exit:"+user);
				bw.flush();
				bw.close();
				socket.close();
				this.dispose();
				System.exit(0);
			}catch (Exception e) {
				// TODO: handle exception
			}
			
		}
}

class oneForone extends JFrame{
	JTextArea msg;
	String name;
	public oneForone(JTextArea msg,String name) {
		this.name=name;
		this.msg=msg;
	}
	public JTextArea getMsg() {
		return msg;
	}
	public String getName() {
		return name;
	}
}
  • 8
    点赞
  • 63
    收藏
    觉得还不错? 一键收藏
  • 7
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值