Java Socket客户端与服务端如何实现不受收发顺序限制的字符串读写
问题根因
当前代码采用单线程串行执行逻辑,两类阻塞操作(读取本地控制台输入的BufferedReader.readLine()、读取Socket对端消息的DataInputStream.readUTF())在同一线程中耦合执行:
- 客户端循环逻辑为:读控制台输入→发消息→阻塞等待对端返回→打印返回,只要没输入新内容,就永远没法处理对端推送的消息
- 服务端循环逻辑为:阻塞等待对端消息→打印→读控制台输入→发返回,只要没收到对端消息,就永远没法主动发消息给客户端
这就导致了必须发一条才能收一条的时序异常。
解决方案
将「收消息」和「发消息」拆分为两个独立线程,各自执行阻塞逻辑互不干扰:
- 主线程负责读取本地控制台输入,发送消息到对端
- 单独启动一个子线程,死循环监听Socket输入流,收到消息直接打印,检测到
end指令时关闭资源终止程序。
修改后的客户端代码
import java.io.*; import java.net.Socket; public class Client { public static void main(String[] args) { try { Socket socket = new Socket("127.0.0.1", 1201); DataInputStream dInputStream = new DataInputStream(socket.getInputStream()); DataOutputStream dataOutputStream = new DataOutputStream(socket.getOutputStream()); BufferedReader bReader = new BufferedReader(new InputStreamReader(System.in)); // 子线程:专门收服务端消息 new Thread(() -> { try { String msgin; while ((msgin = dInputStream.readUTF()) != null) { System.out.println("\n收到服务端消息:" + msgin); if ("end".equals(msgin)) { System.out.println("服务端已断开连接"); socket.close(); System.exit(0); } } } catch (IOException e) { e.printStackTrace(); } }).start(); // 主线程:专门发消息 String msgout; while ((msgout = bReader.readLine()) != null) { dataOutputStream.writeUTF(msgout); dataOutputStream.flush(); if ("end".equals(msgout)) { Thread.sleep(100); // 等待end消息发送完成再关闭 socket.close(); System.exit(0); } } } catch (Exception e) { e.printStackTrace(); } } }
修改后的服务端代码
import java.io.BufferedReader; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.InputStreamReader; import java.net.ServerSocket; import java.net.Socket; public class Server { public static void main(String[] args) { try { ServerSocket ss = new ServerSocket(1201); Socket s = ss.accept(); DataInputStream dInputStream = new DataInputStream(s.getInputStream()); DataOutputStream dataOutputStream = new DataOutputStream(s.getOutputStream()); BufferedReader bReader = new BufferedReader(new InputStreamReader(System.in)); // 子线程:专门收客户端消息 new Thread(() -> { try { String msgin; while ((msgin = dInputStream.readUTF()) != null) { System.out.println("\n收到客户端消息:" + msgin); if ("end".equals(msgin)) { System.out.println("客户端已断开连接"); s.close(); System.exit(0); } } } catch (IOException e) { e.printStackTrace(); } }).start(); // 主线程:专门发消息 String msgout; while ((msgout = bReader.readLine()) != null) { dataOutputStream.writeUTF(msgout); dataOutputStream.flush(); if ("end".equals(msgout)) { Thread.sleep(100); s.close(); System.exit(0); } } } catch (Exception e) { e.printStackTrace(); } } }
逻辑说明
- 两端都可以随时输入内容发送,不需要等待对端返回,收到对端消息会立刻打印到控制台,不会被本地输入操作阻塞
- 任意一端输入
end都会触发两端连接关闭、程序退出 - 新增的100ms休眠是为了保证
end指令能正常发送到对端再关闭连接,避免消息丢失。
内容的提问来源于stack exchange,提问作者user16743592
相关产品推荐
相关产品推荐

