You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.27 21:48:09