Java TCP/IP Socket实现客户端服务端双向消息收发方法
问题描述
当前基于Java开发TCP/IP Socket请求响应系统,已完成基础客户端/服务端框架搭建,可实现客户端向服务端发送消息、服务端将接收消息打印到控制台的基础功能,需要扩展实现服务端向客户端返回响应、两端双向互发消息的能力。
现有客户端、服务端原始代码如下:
原始客户端代码
import java.io.*; import java.net.ServerSocket; import java.net.Socket; public class Main { public static void main(String[] args) throws IOException { String localhost = "127.0.0.1"; int port = 8091; sendMessage(localhost, port); } public static void sendMessage(String server, int port) throws IOException { // Client Socket socket = new Socket(server, port); DataOutputStream output = new DataOutputStream(socket.getOutputStream()); BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(System.in)); while(true){ String input = bufferedReader.readLine(); output.writeUTF(input); System.out.println("message sent " + input ); // receiveResponse(port); if(input.equalsIgnoreCase("exit")) break; // if(output.writeUTF(input)){} } socket.close(); } public static void receiveResponse(int port) { try { ServerSocket serverSocket = new ServerSocket(port); Socket as = serverSocket.accept(); DataInputStream in = new DataInputStream(as.getInputStream()); String yoo = in.readUTF(); System.out.println("Client: " + yoo); if(!yoo.isEmpty()){ System.out.println("message received"); } if(yoo.equalsIgnoreCase("exit")){ System.out.println("Hello world"); } } catch (Exception ex) { ex.printStackTrace(); } } }
原始服务端代码
import java.io.*; import java.net.ServerSocket; import java.net.Socket; public class Server { public static void main(String[] args) throws IOException { int port = 8091; receiveMessage(port); } public static void receiveMessage(int port) throws IOException { ServerSocket serverSocket = new ServerSocket(port); Socket as = serverSocket.accept(); System.out.println("Connected"); System.out.println("incoming = " + as.getInputStream()); DataInputStream in = new DataInputStream(as.getInputStream()); System.out.println(in.readUTF()); // ========================== while(true){ String yoo = in.readUTF(); System.out.println("Client: " + yoo); if(!yoo.isEmpty()){ // sendResult(); System.out.println("message received"); } if(yoo.equalsIgnoreCase("exit")) break; } as.close(); } public static void sendResult( ) { try { System.out.println("HELLO WORLD"); System.out.println("Connected to client"); // ObjectOutputStream os = new ObjectOutputStream(as.getOutputStream()); // // os.writeObject(result); // System.out.println("Result sent"); String str = "Hello World"; OutputStreamWriter osw; Socket socket = null; socket = new Socket("localhost", 8090); osw =new OutputStreamWriter(socket.getOutputStream(), "UTF-8"); osw.write(str, 0, str.length()); } catch (Exception ex) { ex.printStackTrace(); } } }
原始代码核心问题
- 连接逻辑错误:TCP Socket连接本身是全双工通道,同一条已建立的连接可同时支持双向数据读写,不需要客户端额外开ServerSocket监听端口、服务端额外新建Socket连其他端口来做反向通信,原始代码里的
receiveResponse、sendResult方法的连接逻辑完全冗余,还会触发端口占用、连接目标错误问题。 - 流使用不匹配:原始代码用
DataOutputStream.writeUTF()发送数据,反向发送时却混用OutputStreamWriter等其他类型流,会导致数据协议不匹配,出现乱码、读不到数据的问题。 - 单线程阻塞:原始代码仅用单线程循环处理控制台输入发送,IO操作是阻塞模式,线程卡在等待控制台输入时,完全没有机会读取对方发来的消息,自然无法实现双向通信。
- 缺少缓冲区刷新:写数据后没有调用
flush()方法,数据可能滞留在本地缓冲区,不会真正发送到网络,导致对方收不到消息。
实现方案
核心逻辑:基于TCP连接的全双工特性,连接建立后双方各自持有同一个Socket实例的输入流(接收对方消息)、输出流(向对方发消息),拆分两个独立线程分别处理发送、接收逻辑,避免IO阻塞,即可实现双向互发。
修正后客户端代码
import java.io.*; import java.net.Socket; public class ClientMain { public static void main(String[] args) throws IOException { String serverHost = "127.0.0.1"; int serverPort = 8091; // 建立和服务端的唯一通信连接 Socket socket = new Socket(serverHost, serverPort); DataOutputStream outputStream = new DataOutputStream(socket.getOutputStream()); DataInputStream inputStream = new DataInputStream(socket.getInputStream()); BufferedReader consoleReader = new BufferedReader(new InputStreamReader(System.in)); System.out.println("已连接服务端,可输入消息发送,输入exit退出"); // 独立线程:循环接收服务端消息,不被控制台输入阻塞 new Thread(() -> { try { String serverMsg; while ((serverMsg = inputStream.readUTF()) != null) { System.out.println("\n[服务端] " + serverMsg); if ("exit".equalsIgnoreCase(serverMsg)) { System.out.println("服务端已断开连接"); System.exit(0); } } } catch (IOException e) { System.out.println("与服务端的连接已中断"); } }).start(); // 主线程:读取控制台输入,发送给服务端 String input; while ((input = consoleReader.readLine()) != null) { outputStream.writeUTF(input); outputStream.flush(); System.out.println("[已发送] " + input); if ("exit".equalsIgnoreCase(input)) { break; } } // 释放资源 outputStream.close(); inputStream.close(); consoleReader.close(); socket.close(); } }
修正后服务端代码
import java.io.*; import java.net.ServerSocket; import java.net.Socket; public class ServerMain { public static void main(String[] args) throws IOException { int listenPort = 8091; ServerSocket serverSocket = new ServerSocket(listenPort); System.out.println("服务端已启动,监听端口" + listenPort + ",等待客户端连接..."); // 接收客户端连接,获取双向通信的Socket实例 Socket clientSocket = serverSocket.accept(); DataInputStream inputStream = new DataInputStream(clientSocket.getInputStream()); DataOutputStream outputStream = new DataOutputStream(clientSocket.getOutputStream()); BufferedReader consoleReader = new BufferedReader(new InputStreamReader(System.in)); System.out.println("客户端已接入,可输入消息发送,输入exit退出"); // 独立线程:循环接收客户端消息 new Thread(() -> { try { String clientMsg; while ((clientMsg = inputStream.readUTF()) != null) { System.out.println("\n[客户端] " + clientMsg); if ("exit".equalsIgnoreCase(clientMsg)) { System.out.println("客户端已断开连接"); System.exit(0); } } } catch (IOException e) { System.out.println("与客户端的连接已中断"); } }).start(); // 主线程:读取控制台输入,发送给客户端 String input; while ((input = consoleReader.readLine()) != null) { outputStream.writeUTF(input); outputStream.flush(); System.out.println("[已发送] " + input); if ("exit".equalsIgnoreCase(input)) { break; } } // 释放资源 outputStream.close(); inputStream.close(); consoleReader.close(); clientSocket.close(); serverSocket.close(); } }
运行说明
- 先启动服务端,再启动客户端,两端控制台输入任意内容按回车即可发送给对方,对方控制台会实时打印收到的消息
- 任意一端输入
exit即可断开连接,两端程序会自动退出
内容的提问来源于stack exchange,提问作者Abelkelly
相关产品推荐
相关产品推荐

