Java Socket通信阻塞问题:客户端发送后服务端未接收
问题原因与解决办法
你的程序出现双向阻塞的核心问题是数据传输格式不匹配,同时存在两处逻辑错误,具体分析和修复方案如下:
一、核心问题分析
1. 客户端发送与服务端接收的格式不兼容
客户端用DataOutputStream.writeChars()发送字符串:
- 该方法会把每个字符按UTF-16编码(每个字符占2字节)输出,且不会添加换行符;
而服务端用BufferedReader.readLine()接收: - 该方法会一直阻塞,直到读取到换行符(
\n或\r\n),同时它是按系统默认编码将字节转成字符串的。
两者编码规则和终止符要求完全不匹配,服务端永远等不到换行符,陷入阻塞。
2. 客户端接收与服务端发送的格式也不兼容
客户端用DataInputStream.readUTF()接收:
- 该方法要求数据必须是
DataOutputStream.writeUTF()写入的格式(先写2字节的长度标识,再写UTF-8编码的字节);
而服务端用PrintStream.println()发送的是普通字符串加换行符,不符合readUTF的读取规范,客户端同样会阻塞。
3. 服务端循环逻辑错误
- 循环条件
received != "Exit" || received != "exit"逻辑永远为true:无论received是什么值,总有一个条件成立,循环无法终止; - 字符串比较用
!=是错误的:Java中!=比较的是对象地址,应该用!equals()或!equalsIgnoreCase()比较内容。
二、修复后的代码示例
推荐统一使用字符流传输文本,逻辑更清晰,避免编码格式冲突:
修复后的客户端代码
public void Client(int port, String ip) { try { sock = new Socket(ip, port); System.out.println("Found the server."); // 统一用字符流接收服务端数据 BufferedReader reader = new BufferedReader(new InputStreamReader(sock.getInputStream())); // 用PrintWriter发送数据,开启自动刷新缓冲区 PrintWriter writer = new PrintWriter(sock.getOutputStream(), true); // 发送字符串并自动添加换行符,匹配服务端readLine()规则 writer.println("Client Begining Conversation"); // 读取服务端返回的一行数据 System.out.println(reader.readLine()); // 手动关闭资源(实际项目推荐用try-with-resources自动关闭) reader.close(); writer.close(); sock.close(); } catch (UnknownHostException u) { System.out.println(u); } catch (IOException i) { System.out.println(i); } }
修复后的服务端代码
public Server(int port) throws Exception { ServerSocket gameServer = new ServerSocket(port); Socket gameSocket = gameServer.accept(); System.out.println("Client has connected"); BufferedReader reader = new BufferedReader(new InputStreamReader(gameSocket.getInputStream())); PrintWriter writer = new PrintWriter(gameSocket.getOutputStream(), true); Play(reader, writer); // 关闭资源 reader.close(); writer.close(); gameSocket.close(); gameServer.close(); } public void Play(BufferedReader reader, PrintWriter writer) throws Exception { String received, textSent; System.out.println("Waiting for response."); received = reader.readLine(); System.out.println("Client has responded"); // 修正循环条件:用equalsIgnoreCase忽略大小写,逻辑改为&& while (received != null && !"exit".equalsIgnoreCase(received)) { System.out.println(received); textSent = received + " received"; // 发送数据并自动添加换行符 writer.println(textSent); // 读取下一行数据,避免死循环 received = reader.readLine(); } }
三、关键修复点说明
- 统一使用
BufferedReader和PrintWriter:基于字符流传输文本,编码规则一致,println()自动添加换行符,完美匹配readLine()的读取逻辑; - 修正服务端循环条件:用
equalsIgnoreCase()忽略大小写判断退出指令,同时每次循环结束重新读取下一行数据,避免死循环; - 补充资源关闭逻辑:避免Socket资源泄漏(生产环境推荐使用
try-with-resources语法自动管理资源)。
内容的提问来源于stack exchange,提问作者Nehemiah Newell
相关产品推荐
相关产品推荐

