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

无法通过Socket正确传输三个字符串,请求技术解决方案

TCP Socket消息粘包问题修复

问题现象

通过Socket从服务器发送三条独立消息,客户端接收时所有内容被合并到第一条接收结果中,后两次接收返回空字节串:

  • 预期输出:
b'Hello1'
b'Hello2'
b'Hello3'
  • 当前输出:
b'Hello1Hello2Hello3'
b''
b''

问题原因

TCP是流式协议,它不维护消息边界,会把连续发送的小数据合并成一个数据包传输(Nagle算法优化),导致客户端一次读取到多条消息的内容。

修复方案

需要在应用层定义消息边界,常见实现方式有三种:

方案1:使用分隔符(如换行符)

在每条消息末尾添加分隔符,客户端读取到分隔符时判定为一条完整消息。

修改后的服务端代码:

import socket

def main():
    with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock:
        sock.bind(("127.0.0.1", 9898))
        sock.listen(1)
        client_sock, addr = sock.accept()
        # 每条消息末尾添加换行符作为分隔
        client_sock.sendall("hello1\n".encode())
        client_sock.sendall("hello2\n".encode())
        client_sock.sendall("hello3\n".encode())

if __name__ == "__main__":
    main()

修改后的客户端代码:

import socket

def recv_until_newline(sock):
    buffer = b""
    while True:
        chunk = sock.recv(1024)
        if not chunk:
            break
        buffer += chunk
        if b"\n" in buffer:
            # 拆分出第一条带换行的消息
            msg, buffer = buffer.split(b"\n", 1)
            return msg
    return buffer

def main():
    with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock:
        sock.connect(("127.0.0.1", 9898))
        hello1 = recv_until_newline(sock)
        hello2 = recv_until_newline(sock)
        hello3 = recv_until_newline(sock)
        print(hello1)
        print(hello2)
        print(hello3)

if __name__ == "__main__":
    main()

方案2:发送消息长度前缀(更可靠)

先发送消息的长度(用固定字节数打包),再发送消息内容,客户端先读取长度,再读取对应字节数的内容。

修改后的服务端代码:

import socket
import struct

def main():
    with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock:
        sock.bind(("127.0.0.1", 9898))
        sock.listen(1)
        client_sock, addr = sock.accept()
        
        def send_msg(sock, msg):
            # 先打包消息长度为4字节大端整数
            msg_len = struct.pack(">I", len(msg))
            sock.sendall(msg_len)
            sock.sendall(msg)
        
        send_msg(client_sock, b"hello1")
        send_msg(client_sock, b"hello2")
        send_msg(client_sock, b"hello3")

if __name__ == "__main__":
    main()

修改后的客户端代码:

import socket
import struct

def recv_msg(sock):
    # 先读取4字节的长度前缀
    len_data = sock.recv(4)
    if not len_data:
        return b""
    msg_len = struct.unpack(">I", len_data)[0]
    # 读取对应长度的消息内容
    return sock.recv(msg_len)

def main():
    with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock:
        sock.connect(("127.0.0.1", 9898))
        hello1 = recv_msg(sock)
        hello2 = recv_msg(sock)
        hello3 = recv_msg(sock)
        print(hello1)
        print(hello2)
        print(hello3)

if __name__ == "__main__":
    main()

方案3:固定长度消息

如果所有消息长度固定,客户端每次读取固定字节数即可,适合消息长度统一的场景。

说明

方案2的长度前缀方式是工业级应用中最常用的方式,能适配任意长度的消息,避免分隔符冲突问题(比如消息内容本身包含换行符时方案1会失效)。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.17 03:25:19