最简Java聊天室基于ServerSocket/Socket阻塞I/O与多线程:服务端accept后立即启新线程处理客户端,用BufferedReader/PrintWriter收发文本(PrintWriter需auto-flush),ConcurrentHashMap管理在线客户端PrintWriter,客户端需双线程分别收发消息,并妥善处理连接中断与资源关闭。

用 ServerSocket 和 Socket 搭建基础服务端-客户端通信
Java 聊天室最简模型不依赖任何框架,核心就是阻塞式 I/O 配合多线程。服务端靠 ServerSocket 监听端口,每个新连接由独立线程处理;客户端用 Socket 连接并收发字符串。
关键点在于:不能让一个客户端阻塞整个服务端。所以 accept() 后必须立刻丢给新线程,而不是在主线程里读写。
ServerSocket server = new ServerSocket(8080);
while (true) {
Socket client = server.accept(); // 阻塞直到有连接
new Thread(() -> handleClient(client)).start(); // 立刻交出去
}
常见错误是把 handleClient 写成同步阻塞读(比如只调一次 readLine()),结果一个用户发一条消息后线程就卡死,后续消息收不到。
BufferedReader + PrintWriter 处理文本消息的坑
聊天室传的是纯文本,用 BufferedReader.readLine() 和 PrintWriter.println() 最直接。但必须注意三件事:
立即学习“Java免费学习笔记(深入)”;
-
PrintWriter构造时要传true开启自动 flush,否则消息卡在缓冲区不出去 -
readLine()遇到流关闭或异常会返回null,不是空字符串,别用== ""判空 - 客户端和服务端都要各自维护一对
BufferedReader/PrintWriter,输入输出不能混用同一个流
示例片段(服务端处理单个客户端):
void handleClient(Socket s) throws IOException {
BufferedReader in = new BufferedReader(new InputStreamReader(s.getInputStream()));
PrintWriter out = new PrintWriter(s.getOutputStream(), true); // 注意 true
String msg;
while ((msg = in.readLine()) != null) {
System.out.println("收到:" + msg);
out.println("[已接收] " + msg); // 广播逻辑这里先省略
}
s.close();
}
用 ConcurrentHashMap 管理在线客户端列表
要实现“群聊”,服务端得记住所有活跃的 PrintWriter(即每个客户端的输出通道)。不能用 HashMap —— 多线程并发遍历时会抛 ConcurrentModificationException。
ConcurrentHashMap 是安全选择,但注意它不保证迭代过程中的强一致性(比如遍历时有人退出,可能漏发或重复发,对简单聊天室可接受)。
典型用法:
- 键用自增 ID 或客户端地址(
s.getRemoteSocketAddress()) - 值存
PrintWriter,别存Socket或BufferedReader,避免资源误关 - 每次发广播前,用
map.values().forEach(out -> out.println(...))
退出清理必须做:在 handleClient 的 finally 块里从 map 中移除对应项,并显式 close() 流。
客户端也要开两个线程:一收一发
如果客户端只用一个线程,要么只能发、要么只能收,交互卡顿。标准解法是:
- 主线程负责从控制台读输入,通过
PrintWriter发送 - 另起一个线程,用
BufferedReader.readLine()持续监听服务端消息并打印
容易忽略的是:当服务端断开时,客户端的 readLine() 会返回 null,此时收消息线程应自然退出,同时通知主线程停止发送(比如设个 volatile boolean connected = false 标志位)。
没有心跳机制时,网络闪断无法及时感知,只能靠下一次读/写操作触发异常 —— 这是简单模型的固有限制。
真正难的不是写通路,而是异常分支:连接中断、流关闭、线程中断、资源泄漏。每个 Socket 对应的输入输出流,只要打开就必须在明确时机关闭,且不能重复关 —— 这部分逻辑一旦疏忽,跑几分钟后就会出现“能连不能聊”或者“发消息没反应”的现象。










