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

Java Socket服务端读取输入阻塞无法正确输出客户端内容问题

Java Socket服务端输出异常问题排查

问题现象

服务端预期效果为控制台输出客户端发送的输入内容,实际运行时程序阻塞在len = in.read(bytes);语句处,无法执行集合清空逻辑,每次输出的内容存在偏差、不准确。

原实现代码

import java.io.IOException;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.List;

public class Server {
    public static void main(String[] args) {
        try {
            ServerSocket sc = new ServerSocket(8000);
            while(true) {
                Socket socket = sc.accept();
                InputStream in = socket.getInputStream();
                int len = 0;
                String str = null;
                byte[] bytes = new byte[8];
                List<Byte> byteL = new ArrayList<>();
                do {
                    len =in.read(bytes);
                    for (int i = 0;i < len;i++) {
                        byteL.add(bytes[i]);
                    }
                    byte[] byteLs = new byte[byteL.size()];
                    for(int j = 0;j < byteL.size();j++) {
                        byteLs[j] = byteL.get(j);
                    }
                    str = new String(byteLs,0,byteLs.length);
                    System.out.println(str);
                    if (len == -1) {
                        byteL.clear();
                        str = null;
                    }
                } while (len != -1);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

根因分析

  • InputStream.read()本身是阻塞方法:TCP是面向流的协议,只要客户端保持连接不主动断开、也没有新数据发送,read()方法会一直挂起等待,不会返回-1。原代码把集合清空逻辑放在len == -1的分支里,长连接场景下根本触发不了这个条件,缓冲区会一直累积历史数据。
  • 消息边界处理逻辑缺失:原代码设置的缓冲区只有8字节,只要读到数据就立刻把当前累计的所有字节转成字符串打印,既没有判断单次发送的消息是否接收完整,也没有在单条消息处理完成后清空缓冲区,只要消息长度超过8字节就会被拆成多段重复输出,内容自然不准确。
  • 资源未正常释放:原代码接收到Socket连接、处理完数据后从未关闭Socket和对应的输入流,长时间运行会耗尽系统文件句柄,导致新连接无法接入。

修复方案

TCP流本身没有天然的消息分界,必须在业务层自定义消息边界规则,常见的实现有两种:一是约定固定分隔符(比如换行符\n作为单条消息的结束标记),二是在消息头部固定位置传递消息体总长度,读取到对应长度的字节后再完成解析。
以下是基于换行符分隔的可运行修复版本,适合普通文本消息传输场景,使用try-with-resources语法自动释放资源,不会出现阻塞和内容拼接错误:

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.ServerSocket;
import java.net.Socket;
import java.nio.charset.StandardCharsets;

public class Server {
    public static void main(String[] args) {
        try (ServerSocket serverSocket = new ServerSocket(8000)) {
            while (true) {
                // 接受客户端连接,自动管理连接和输入流的资源释放
                try (Socket socket = serverSocket.accept();
                     BufferedReader reader = new BufferedReader(
                             new InputStreamReader(socket.getInputStream(), StandardCharsets.UTF_8))) {
                    String message;
                    // 逐行读取单条消息,读到null代表客户端主动断开连接
                    while ((message = reader.readLine()) != null) {
                        System.out.println("收到客户端消息:" + message);
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

如果业务场景传输的是二进制内容、不适合用分隔符,把边界判断逻辑替换为「先读固定长度的消息头获取内容总长度,再读取对应长度的字节解析」即可,注意单条消息解析完成后立刻清空对应缓冲区,不要等连接断开再做清理。


内容的提问来源于stack exchange,提问作者刘凯远

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.29 22:39:25