Java Socket 多人聊天室(私聊+群聊)技术笔记(V4版本)

发布时间:2026/7/29 1:24:21
Java Socket 多人聊天室(私聊+群聊)技术笔记(V4版本) 一、项目整体架构本项目是基于 BIO 阻塞式 Socket 实现的桌面多人聊天室支持用户登录、群聊、一对一私聊功能搭配Swing实现可视化客户端界面。整体分为服务端、客户端、用户实体、UI界面四大核心模块采用 服务端中转消息 的通信模式所有客户端消息统一发送至服务端由服务端分发转发。1.1 核心模块分工Server服务端监听端口、接收客户端连接、处理用户登录、解析消息类型、转发私聊/群聊消息、维护在线用户列表Client客户端建立服务端连接、用户账号登录、封装私聊/群聊消息、发送消息、接收服务端转发的消息ClientUser用户实体封装在线用户信息账号、Socket连接服务端核心存储对象ClientUI可视化界面基于Swing实现聊天窗口、输入框、发送按钮绑定消息收发事件1.2 通信核心原理基于TCP长连接通信客户端与服务端建立永久Socket连接全程流式传输字节数据通过 自定义消息协议 区分私聊、群聊消息服务端统一调度转发实现多客户端通信。二、自定义通信协议核心重点项目无第三方通信框架完全自定义消息格式通过固定字节顺序解析消息类型、发送目标、消息内容是实现私聊/群聊区分的核心。2.1 消息类型定义1私聊消息定向发送指定用户2群聊消息广播给所有在线用户除发送者自身2.2 完整消息协议格式私聊消息格式消息类型(1字节) 好友名称长度(1字节) 好友名称(字节数组) 消息长度(1字节) 消息内容(字节数组)群聊消息格式消息类型(1字节) 消息长度(1字节) 消息内容(字节数组)登录消息格式账号长度(1字节) 账号名称(字节数组)三、用户实体类ClientUser服务端用于存储所有在线用户的核心实体绑定用户账号与Socket连接实现精准消息转发。packageV4;importjava.net.Socket;/** * 在线用户实体类封装单个客户端的连接信息与账号 * 服务端集合通过该对象维护所有在线用户 */publicclassClientUser{Stringaccount;// 客户端登录账号Socketsocket;// 客户端对应的Socket连接对象// 构造方法登录成功后创建用户对象publicClientUser(Socketsocket,Stringaccount){this.accountaccount;this.socketsocket;}}四、客户端核心实现Client客户端核心功能初始化连接、账号登录、封装发送消息、接收服务端返回消息。所有IO操作基于字节流传输严格遵循自定义协议。packageV4;importjava.io.IOException;importjava.io.InputStream;importjava.io.OutputStream;importjava.net.Socket;importjava.util.Scanner;/** * 聊天室客户端核心类 * 负责连接服务端、登录、消息发送、消息接收 */publicclassClient{Socketsocketnull;// 客户端Socket连接对象// 初始化连接服务端 执行登录逻辑publicvoidinit(){try{// 连接本地服务端端口与服务端一致54321socketnewSocket(127.0.0.1,54321);login(socket.getOutputStream());}catch(IOExceptione){thrownewRuntimeException(e);}}// 用户登录向服务端发送账号信息publicvoidlogin(OutputStreamos){ScannerscnewScanner(System.in);System.out.println(请输入账号:);Stringaccountsc.nextLine();byte[]accountsaccount.getBytes();try{// 先发送账号长度再发送账号内容服务端按长度读取避免粘包os.write(accounts.length);os.write(accounts);os.flush();// 强制刷新流确保数据立即发送}catch(IOExceptione){thrownewRuntimeException(e);}}/** * 消息发送核心方法区分私聊/群聊封装协议数据 * 规则消息以开头为私聊否则为群聊 */publicvoidsend(Stringmessage){try{OutputStreamossocket.getOutputStream();// 判断消息类型首字符 代表私聊if(message.charAt(0)){// 拆分私聊格式好友名 消息内容intindexmessage.indexOf( );Stringfriendmessage.substring(1,index);// 截取目标好友账号Stringmsgmessage.substring(index1);// 截取消息内容// 按照私聊协议封装数据os.write(1);// 1私聊类型标识intfidlenfriend.getBytes().length;os.write(fidlen);// 好友名称字节长度os.write(friend.getBytes());// 好友名称字节数据intmsglenmsg.getBytes().length;os.write(msglen);// 消息内容字节长度os.write(msg.getBytes());// 消息内容字节数据}else{// 群聊消息封装os.write(2);// 2群聊类型标识byte[]msgsmessage.getBytes();os.write(msgs.length);// 消息长度os.write(msgs);// 消息内容}os.flush();// 强制刷新完成消息发送}catch(IOExceptione){thrownewRuntimeException(e);}}// 读取服务端转发的消息publicStringreadMsg(){try{InputStreaminssocket.getInputStream();// 先读长度再读内容解决TCP粘包问题intlenins.read();byte[]msgsnewbyte[len];ins.read(msgs);returnnewString(msgs);}catch(IOExceptione){thrownewRuntimeException(e);}}staticvoidmain(){}}4.1 客户端核心关键点备注粘包解决核心所有消息传输遵循「先传长度、再传内容」服务端根据长度精准读取字节避免TCP流式传输导致的粘包、半包问题消息区分规则通过消息首字符判定私聊空格分割好友名称与消息内容流刷新机制每次write后必须执行flush()否则数据会滞留缓冲区无法及时发送五、服务端核心实现Server服务端是整个聊天室的中枢负责监听连接、处理登录、解析消息、转发消息、维护在线用户列表通过多线程支持多客户端同时在线。packageV4;importjava.io.IOException;importjava.io.InputStream;importjava.io.OutputStream;importjava.net.ServerSocket;importjava.net.Socket;importjava.util.ArrayList;/** * 聊天室服务端核心类 * 端口监听、客户端连接管理、消息解析、消息转发 */publicclassServer{ServerSocketssnull;// 全局在线用户列表存储所有已登录的客户端连接ArrayListClientUsersocketlist();// 初始化服务端绑定固定端口54321publicvoidinit(){try{ssnewServerSocket(54321);}catch(IOExceptione){thrownewRuntimeException(e);}}// 监听客户端连接阻塞方法等待客户端接入publicSocketlisten(){try{returnss.accept();}catch(IOExceptione){thrownewRuntimeException(e);}}// 统一消息发送工具服务端向客户端转发消息publicvoidsend(Stringmessage,OutputStreamos){try{// 遵循长度内容协议发送消息intlenmessage.getBytes().length;os.write(len);os.write(message.getBytes());os.flush();}catch(IOExceptione){thrownewRuntimeException(e);}}/** * 消息解析方法读取客户端发送的原始字节流 * 根据消息类型1/2分别解析私聊、群聊数据 * 返回带标识的字符串用于后续转发逻辑处理 */publicStringreceive(InputStreamin){try{inttypein.read();// 读取消息类型if(type1){// 解析私聊消息intfidLenin.read();// 好友名称长度byte[]fidnewbyte[fidLen];in.read(fid);StringfidStrnewString(fid);// 目标好友账号intmlenin.read();// 消息长度byte[]msgsnewbyte[mlen];in.read(msgs);StringmsgnewString(msgs);// 消息内容// 自定义分隔符# 拼接数据方便后续拆分处理return1#fidStr#msg;}else{// 解析群聊消息intlenin.read();byte[]msgsnewbyte[len];in.read(msgs);StringmsgnewString(msgs);return2#msg;}}catch(IOExceptione){thrownewRuntimeException(e);}returnnull;}/** * 处理客户端所有消息的核心方法 * 每个客户端连接单独开启一个子线程实现多客户端并发 */publicvoidproClientMsg(Socketsocket){newThread(()-{// 1. 处理用户登录获取登录账号Stringaccountprologin(socket);// 2. 将在线用户存入集合ClientUserclientUsernewClientUser(socket,account);socketlist.add(clientUser);// 3. 循环监听当前客户端的所有消息while(true){try{InputStreaminclientUser.socket.getInputStream();Stringmsgreceive(in);String[]datasmsg.split(#);// 拆分解析后的消息数据Stringtypedatas[0];// 私聊消息转发逻辑if(type.equals(1)){Stringfiddatas[1];// 目标好友账号Stringmsg1datas[2];// 消息内容// 遍历在线用户列表精准匹配目标用户for(ClientUserotCU:socketlist){if(otCU.account.equals(fid)){// 拼接发送者信息转发给目标用户msg1clientUser.account说msg1;send(msg1,otCU.socket.getOutputStream());break;}}}else{// 群聊消息转发逻辑广播给所有在线用户排除自己Stringmsg1datas[1];msg1clientUser.account说:msg1;for(ClientUserotCU:socketlist){Socketsocket1otCU.socket;if(socket1!clientUser.socket){send(msg1,socket1.getOutputStream());}}}}catch(IOExceptione){thrownewRuntimeException(e);}}}).start();}// 处理用户登录读取客户端发送的账号信息publicStringprologin(Socketsocket){try{InputStreaminsocket.getInputStream();intlenin.read();byte[]msgsnewbyte[len];in.read(msgs);StringaccountnewString(msgs);System.out.println(用户名:account 上线了 );returnaccount;}catch(IOExceptione){thrownewRuntimeException(e);}}// 服务端启动入口staticvoidmain(){ServerservernewServer();server.init();// 循环监听客户端连接主线程阻塞单独开启线程newThread(()-{while(true){System.out.println(监听连接中~);Socketsserver.listen();System.out.println(s.getInetAddress():s.getPort()连接成功);server.proClientMsg(s);// 处理该客户端的所有后续逻辑}}).start();}}5.1 服务端核心关键点备注多线程并发每个客户端连接单独创建子线程处理消息避免单线程阻塞支持多用户同时在线消息转发机制所有消息不直接点对点传输必须经过服务端中转实现统一管理私聊精准匹配通过遍历在线用户集合匹配账号名称实现定向消息发送群聊广播过滤转发群聊消息时排除发送者自身避免自己收到自己的消息数据分隔处理使用#作为自定义分隔符拆分消息类型、目标账号、消息内容解析简单高效六、可视化UI界面实现ClientUI基于Swing实现桌面可视化窗口包含聊天记录展示区、消息输入框、发送按钮同时开启子线程实时接收服务端消息避免UI阻塞。packageV4;importjavax.swing.*;importjava.awt.*;/** * 聊天室可视化客户端界面 * 基于Swing实现绑定消息收发事件 */publicclassClientUIextendsJFrame{ClientclientnewClient();// 绑定客户端核心通信对象publicstaticvoidmain(){ClientUIclientUInewClientUI();clientUI.showUI();}// 初始化展示UI窗口publicvoidshowUI(){// 启动客户端连接登录client.init();// 窗口基础配置JFramejfnewJFrame(小小聊天室);jf.setSize(500,550);jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);jf.setLocationRelativeTo(null);// 窗口居中// 聊天记录展示区域带滚动条JTextAreamsgAreanewJTextArea();JScrollPanescrollPanenewJScrollPane(msgArea);scrollPane.setPreferredSize(newDimension(0,470));// 消息输入框、发送按钮JTextFieldmsgFieldnewJTextField();msgField.setBackground(Color.pink);JButtonsendBtnnewJButton(发送);msgField.setPreferredSize(newDimension(370,0));sendBtn.setPreferredSize(newDimension(100,0));// 组件布局添加jf.add(scrollPane,BorderLayout.NORTH);jf.add(msgField,BorderLayout.WEST);jf.add(sendBtn,BorderLayout.EAST);jf.setVisible(true);// 绑定发送按钮点击事件sendBtn.addActionListener(e-sendMsg(msgArea,msgField));// 开启子线程实时接收服务端消息刷新UI防止主线程阻塞newThread(()-{while(true){Stringmsgclient.readMsg();msgArea.append(msg\n);// 追加消息到聊天界面}}).start();}// 发送消息事件点击按钮触发publicvoidsendMsg(JTextAreamsgArea,JTextFieldmsgField){StringmsgmsgField.getText();// 本地先展示自己发送的消息msgArea.append(我说msg\n);// 调用客户端方法发送消息到服务端client.send(msg);msgField.setText();// 清空输入框}}6.1 UI核心关键点备注子线程接收消息消息接收是阻塞循环操作必须放在子线程否则会卡死Swing主线程UI刷新线程本地消息回显自己发送的消息本地直接展示服务端只转发他人消息提升体验滚动文本域使用JScrollPane包裹文本域实现聊天记录滚动查看七、项目启动流程总结启动 Server服务端监听54321端口等待客户端连接启动 ClientUI客户端输入账号完成登录服务端记录在线用户客户端输入消息无为群聊好友空格内容为私聊客户端封装协议消息发送至服务端服务端解析消息类型转发至对应目标用户客户端子线程接收转发消息刷新UI展示聊天内容