【问题标题】:How to send reply from TCP server to TCP client?如何从 TCP 服务器向 TCP 客户端发送回复?
【发布时间】:2025-12-31 16:55:02
【问题描述】:

我有以下服务器和客户端代码。客户端能够成功发送数据到服务器,但是当客户端尝试从服务器读取数据时,代码继续运行而没有任何输出。

服务器:

public static void main(String[] args) throws Exception {

    ServerSocket s = new ServerSocket(9898);

    while(true) {

        Socket recv = s.accept();

        InputStream inp = recv.getInputStream();
        OutputStream out = recv.getOutputStream();

        String data = null;
        BufferedReader in = new BufferedReader(new InputStreamReader(inp));
        while ( (data = in.readLine()) != null ) {
            System.out.println("\r" + data);
        }

        out.write("sampleSendToClient".getBytes());
    }

}

客户:

public static void main(String[] args) throws Exception{
    Socket clientSocket = new Socket("localhost", 9898);
    OutputStream out = clientSocket.getOutputStream();
    InputStream inp = clientSocket.getInputStream();

    out.write("sampleSendToServer".getBytes());

    String data = null;
    //if I dont write next 4 lines then client exits gracefully 
    //but now it is stuck infinitely
    BufferedReader in = new BufferedReader(new InputStreamReader(inp));
    while ( (data = in.readLine()) != null ) {
        System.out.println("\r" + data);
    }

    clientSocket.close();
}

我无法理解上面的代码有什么问题。

【问题讨论】:

    标签: java sockets tcp


    【解决方案1】:

    您的服务器通过调用OutputStream#write(byte[] data) 直接在输出流上快速而轻松地写入,但您的客户端使用BufferedReader 读取,而BufferedReader#readLine() 是一个阻塞 I/O 操作,它将暂停他的线程直到\n\r 字符到达InputStream(或者流关闭,在这种情况下方法被中断并返回null)。

    您的服务器发送的字符串不以\n\r 结尾,因此数据可能已经发送并停留在客户端BufferedReader 的缓冲区中,但没有这些终止字符readLine() 将永远不会返回。

    简单的改变

    out.write("sampleSendToClient".getBytes());
    

    out.write("sampleSendToClient\n".getBytes());
    

    应该可以解决问题。

    更新:

    while ( (data = in.readLine()) != null ) {
            System.out.println(data);
        }
    

    这个while 循环只有在readLine() 返回null 时才会退出,只有在流被关闭时才会出现这种情况。但是,由于您无法在不丢失连接的情况下关闭流,您将不得不找到另一种方法来终止循环,因为现在它将永远运行。

    即使在添加\n 之后,您的服务器也不会响应,因为此时他将无限循环。


    附带说明:对于每个新连接,您都在覆盖以前的套接字而不关闭它的资源(主要关注数据流和套接字本身),因此会造成资源泄漏。考虑在不再需要时关闭每个对象。

    【讨论】:

    • 这是否也意味着在 out.write() 之后写 out.close() 可以正常工作(不需要 \n 或 \r)?
    • @LoneCuriousWolf 我在查看您的代码时犯了一个小错误。更新了我的答案。不,你不能调用out.wirte() 会在out.close() 调用之后抛出异常。另外我建议使用PrintWriter,因为它们提供了一组与System.out.println()类似的方法,在这种情况下,您不再需要关心\n\r