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

Java Socket长连接首次回显后读写速度下降问题排查

Socket长连接耗时异常高于短连接问题分析

原本预期Socket长连接在多次消息交互时性能更优,但实际测试中,长连接首次消息后读写平均耗时达80ms,而每次新建并关闭Socket的短连接耗时始终为0ms,以下是测试代码及现象:

长连接代码示例

public class StackOverflow {
    public static void main(String[] args) throws IOException, InterruptedException {
        new Thread() {
            public void run() {
                try {
                    ServerSocket server = new ServerSocket(8080);
                    Socket socket = server.accept();
                    while (true) {
                        byte data[] = read(socket);
                        write(socket, data);
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }.start();
        
        InetSocketAddress sockaddr = new InetSocketAddress("localhost", 8080);
        Socket socket = new Socket();
        socket.connect(sockaddr, 60000);
        
        byte data[] = new byte[100];
        new Random().nextBytes(data);
        while (true) {
            long start = System.currentTimeMillis();
            write(socket, data);
            read(socket);
            long end = System.currentTimeMillis();
            System.out.printf("Time: %d\n", end - start);
        }
    }
    
    public static void write(Socket socket, byte data[]) throws IOException {
        OutputStream out = socket.getOutputStream();
        ByteBuffer buffer = ByteBuffer.allocate(Integer.BYTES);
        buffer.putInt(data.length);
        out.write(buffer.array());
        out.write(data);
        out.flush();
    }
    
    public static byte[] read(Socket socket) throws IOException {
        InputStream in = socket.getInputStream();
        byte head[] = new byte[Integer.BYTES];
        in.read(head);
        ByteBuffer buffer = ByteBuffer.wrap(head);
        int dataSize = buffer.getInt();
        byte data[] = new byte[dataSize];
        in.read(data);      
        return data;
    }
}

输出结果:

Time: 0
Time: 96
Time: 88
Time: 87
Time: 88
Time: 88
Time: 91
Time: 92
Time: 92
Time: 87
Time: 91
...

短连接代码示例

public static void main(String[] args) throws IOException, InterruptedException {
    new Thread() {
        public void run() {
            try {
                ServerSocket server = new ServerSocket(8080);
                while (true) {
                    Socket socket = server.accept();
                    byte data[] = read(socket);
                    write(socket, data);
                    socket.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
            
        }
    }.start();
    
    InetSocketAddress sockaddr = new InetSocketAddress("localhost", 8080);
    
    byte data[] = new byte[100];
    new Random().nextBytes(data);
    while (true) {
        Socket socket = new Socket();
        socket.connect(sockaddr, 60000);
        long start = System.currentTimeMillis();
        write(socket, data);
        read(socket);
        long end = System.currentTimeMillis();
        System.out.printf("Time: %d\n", end - start);
        socket.close();
    }
}

输出结果:

Time: 0
Time: 0
Time: 0
Time: 0
Time: 0
Time: 0
Time: 0
Time: 0
Time: 0
Time: 0
Time: 0
Time: 0
Time: 0
Time: 0
Time: 0
Time: 0
Time: 0
...

原因分析

核心原因是TCP的Nagle算法与延迟确认(Delayed ACK)机制的交互冲突:

  • 长连接场景:客户端首次发送数据后,服务器回写响应,客户端读取响应后立刻发送下一条数据。由于这条数据体积较小(仅104字节),TCP的Nagle算法会等待:要么积累更多数据一起发送,要么等待服务器对前一次数据的ACK。而服务器收到客户端的第二条数据后,会触发延迟确认机制(默认延迟约80-200ms),不会立即回复ACK。Nagle算法等待ACK超时后才发送数据,最终导致每次交互出现80ms左右的延迟。
  • 短连接场景:每次交互完成后客户端主动关闭Socket,TCP在关闭连接时会发送FIN包,服务器收到FIN后会立即回复ACK,同时回写的数据会被强制发送,不会触发延迟确认;此外,短连接的TCP握手会触发快速启动机制,加上关闭连接时的缓冲区强制刷新,完全规避了Nagle算法的等待逻辑,因此耗时始终为0ms。

另外需要注意:代码中的in.read(head)和in.read(data)没有处理读取不完整的情况(InputStream.read()可能只返回部分字节),这在生产环境中会导致数据解析错误,需要通过循环读取直到获取完整的目标字节数来修复,但这不是本次耗时异常的直接原因。

内容的提问来源于stack exchange,提问作者Rodrigo Vale

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.19 12:42:05