Socket连接聊天室必须设setSoTimeout()防阻塞,用BufferedReader/PrintWriter处理UTF-8文本协议,收发分线程,关闭前先发/quit并flush。

用 Socket 连接聊天室服务端,别漏掉 setSoTimeout()
Java 客户端连聊天室,核心就是 Socket。但很多人一上来就写 new Socket("localhost", 8080),然后卡死在 readLine() —— 因为服务端没发消息,客户端就一直等,线程彻底挂住。
必须主动设超时:
Socket socket = new Socket();
socket.connect(new InetSocketAddress("localhost", 8080), 5000);
socket.setSoTimeout(30000); // 读操作最多等30秒,超时抛 IOException否则网络抖动、服务端崩了、防火墙拦截,客户端都毫无反应,用户以为程序卡死。
用 BufferedReader + PrintWriter 处理文本协议,别用 DataInputStream
聊天室通常走纯文本协议(比如一行一条 JSON 或纯文字),DataInputStream.readLine() 已废弃且对换行符敏感;Scanner 在多线程下不安全,还容易吃掉换行导致粘包。
稳妥组合是:
BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream(), StandardCharsets.UTF_8)); PrintWriter out = new PrintWriter(socket.getOutputStream(), true); // auto-flush 关键
-
true参数开启自动 flush,否则发不出消息 - 显式指定
UTF_8,避免 Windows 默认编码(GBK)收不到中文 - 别在循环里反复 new 这两个对象,复用即可
收发逻辑必须分线程,主线程不能阻塞在 readLine()
一个线程负责接收(in.readLine()),一个线程负责发送(out.println()),否则输入框打字时界面冻结,或收到新消息时无法响应键盘输入。
典型结构:
new Thread(() -> {
try {
String line;
while ((line = in.readLine()) != null) {
System.out.println("[SERVER] " + line); // 或更新 UI
}
} catch (IOException e) {
System.err.println("连接断开: " + e.getMessage());
}
}).start();
- 循环里别用
while (true)加空sleep,浪费 CPU -
readLine()返回null表示服务端关闭连接,应退出循环并清理资源 - Swing/JavaFX 中更新 UI 必须回到 EDT/AWT 线程,用
SwingUtilities.invokeLater()
关连接前先发退出指令,再 close(),否则消息可能丢失
直接调 socket.close(),最后几条 out.println() 可能还在缓冲区没发出去,服务端收不到“用户下线”通知。
正确顺序:
out.println("/quit"); // 假设服务端识别该命令做清理
out.flush(); // 强制刷出缓冲区
try {
Thread.sleep(100); // 给服务端一点处理时间(可选,但保险)
} catch (InterruptedException ignored) {}
socket.close();
- 服务端是否响应
/quit是另一回事,但客户端必须发 -
PrintWriter的auto-flush对println生效,但对print不生效,别混用 - 如果服务端用
readLine()读,你发的每条消息末尾必须带\n(println自带)










