Java TCP客户端-服务器双向通信陷入无限循环排查求助
Java TCP双向通信无限循环/服务器卡
ss.accept()问题排查与解决 嘿,我来帮你搞定这个头疼的问题!从你描述的情况来看,原本单向通信正常,加了双向回复后就陷入死循环,服务器还卡在ss.accept(),大概率是两个核心原因导致的:
问题根源扒一扒
- 单线程忙不过来:你的服务器主线程可能既在做
ServerSocket.accept()监听新连接,又要处理已连接客户端的读写操作。一旦开始处理某个客户端的双向通信,主线程就被读写循环卡住了,根本没法回到ss.accept()继续等新连接,同时客户端那边如果也在等服务器回复,两边就互相耗着了。 - 读写顺序搞反了:可能客户端和服务器的代码都先跑去执行
read()操作(等着对方发消息),谁都不先主动发第一条数据,结果就是两边都卡在“等消息”的状态,直接死锁。
解决方案:分线程干活 + 明确读写逻辑
1. 服务器端:给每个客户端单独开线程
服务器的正确姿势是:主线程只专心监听新连接,每收到一个客户端连接,就启动一个独立线程去处理这个客户端的双向通信。这样主线程不会被阻塞,能继续接新连接,每个客户端的通信也互不干扰。
修改后的服务器代码示例:
import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.io.OutputStreamWriter; import java.net.ServerSocket; import java.net.Socket; public class TCPServer { public static void main(String[] args) { try (ServerSocket serverSocket = new ServerSocket(8888)) { System.out.println("服务器启动,蹲守客户端连接中..."); // 主线程循环蹲守新连接 while (true) { Socket clientSocket = serverSocket.accept(); // 现在不会被长期卡着了,因为处理逻辑在子线程 System.out.println("新客户端上线:" + clientSocket.getInetAddress()); // 开新线程伺候这个客户端 new Thread(new ClientHandler(clientSocket)).start(); } } catch (IOException e) { e.printStackTrace(); } } // 专门处理单个客户端的类,实现Runnable接口 static class ClientHandler implements Runnable { private final Socket clientSocket; public ClientHandler(Socket socket) { this.clientSocket = socket; } @Override public void run() { try (BufferedReader in = new BufferedReader(new InputStreamReader(clientSocket.getInputStream())); OutputStreamWriter out = new OutputStreamWriter(clientSocket.getOutputStream())) { String clientMsg; // 循环读客户端消息,直到连接断开 while ((clientMsg = in.readLine()) != null) { System.out.println("收到客户端消息:" + clientMsg); // 给客户端回消息 String response = "服务器已查收:" + clientMsg; out.write(response + "\n"); // 必须加换行!因为客户端用readLine()读 out.flush(); // 一定要刷缓冲区,不然消息会憋在里面发不出去 // 收到exit就断开 if ("exit".equalsIgnoreCase(clientMsg)) { break; } } System.out.println("客户端下线:" + clientSocket.getInetAddress()); } catch (IOException e) { e.printStackTrace(); } finally { try { clientSocket.close(); } catch (IOException e) { e.printStackTrace(); } } } } }
2. 客户端:分开处理发送和接收
客户端这边要避免主线程被读取服务器回复的操作卡住,最好单独开一个线程来监听服务器的回复,主线程专心负责输入消息发送给服务器,这样就不会出现两边都等的情况。
修改后的客户端代码示例:
import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.io.OutputStreamWriter; import java.net.Socket; import java.util.Scanner; public class TCPClient { public static void main(String[] args) { try (Socket socket = new Socket("localhost", 8888); BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream())); OutputStreamWriter out = new OutputStreamWriter(socket.getOutputStream()); Scanner scanner = new Scanner(System.in)) { System.out.println("成功连接服务器!"); // 开个线程专门读服务器的回复,别让主线程被卡 new Thread(() -> { try { String serverResponse; while ((serverResponse = in.readLine()) != null) { System.out.println("服务器回复:" + serverResponse); } } catch (IOException e) { System.out.println("和服务器断开连接啦"); } }).start(); // 主线程负责发消息给服务器 String input; while (true) { System.out.print("请输入要发送的消息(输入exit退出):"); input = scanner.nextLine(); out.write(input + "\n"); out.flush(); if ("exit".equalsIgnoreCase(input)) { break; } } } catch (IOException e) { e.printStackTrace(); } } }
几个必须注意的细节
- 刷缓冲区!刷缓冲区!刷缓冲区!:用
write()发消息后一定要调用flush(),不然数据会留在输出缓冲区里,对方根本收不到,还以为没发。 - 换行符要配对:如果用
readLine()读取数据,发送消息的时候必须在末尾加\n,不然readLine()会一直等换行符,直接阻塞住。 - 资源要关好:用try-with-resources语法自动关闭Socket、流这些资源,避免内存泄漏。
- 线程要分开:服务器的监听和通信要分离,客户端的发送和接收要分离,单线程很容易出阻塞问题。
内容的提问来源于stack exchange,提问作者Le Svr
相关产品推荐
相关产品推荐

