Socket连接聊天室必须设setSoTimeout()防阻塞,用BufferedReader/PrintWriter处理UTF-8文本协议,收发分线程,关闭前先发/quit并flush。
setSoTimeout()
Java 客户端连聊天室,核心就是 Socket。但很多人一上来就写 new Socket("localhost", 8080),然后卡死在 readLine() —— 因为服务端没发消息,客户端就一直等,线程彻底挂住。
必须主动设超时:
Socket socket = new Socket(); socket.connect(new InetS否则网络抖动、服务端崩了、防火墙拦截,客户端都毫无反应,用户以为程序卡死。ocketAddress("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)收不到中文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,浪费 CPUreadLine() 返回 null 表示服务端关闭连接,应退出循环并清理资源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 自带)