TCP粘包:短连接没暴露的问题,长连接上炸了

举报
yd_264617019 发表于 2026/11/14 11:55:06 2026/11/14
【摘要】 游戏网关从HTTP短连接换成TCP长连接后,客户端偶发「协议解析错乱」:一条消息的尾部粘着下一条的开头,或者消息被拦腰截断。查了两天的结论是教科书级的一句话:TCP是字节流协议,没有消息边界。短连接一连接一条消息,问题被天然掩盖了。粘包/拆包的真相发送端三次write各100字节,Nagle算法可能攒成一段发出;接收端一次read可能拿到260字节(前两条加第三条的一部分),也可能只拿到50...

游戏网关从HTTP短连接换成TCP长连接后,客户端偶发「协议解析错乱」:一条消息的尾部粘着下一条的开头,或者消息被拦腰截断。查了两天的结论是教科书级的一句话:TCP是字节流协议,没有消息边界。短连接一连接一条消息,问题被天然掩盖了。

粘包/拆包的真相

发送端三次write各100字节,Nagle算法可能攒成一段发出;接收端一次read可能拿到260字节(前两条加第三条的一部分),也可能只拿到50字节。TCP保证字节有序可靠,不保证「写几次读几次」。所谓粘包拆包,本质是应用层没有自己定义边界。

三种解法

方案一:定长消息。每条固定1024字节,不足补齐。简单浪费,适合内部低频协议。

方案二:分隔符。用\n或特殊字节分隔,readline式解析。要处理转义(正文里出现分隔符),文本协议常用。

方案三:长度前缀,工业标准做法。头部固定字节写明正文长度:

import struct

MAGIC = 0xAB

def encode(msg: bytes) -> bytes:
    # 头: 魔数1B + 版本1B + 长度2B(大端)
    head = struct.pack('!BBH', MAGIC, 1, len(msg))
    return head + msg

class Decoder:
    """流式解码器:喂任意长度的字节,吐完整消息"""
    HEAD = 4

    def __init__(self):
        self.buf = bytearray()

    def feed(self, data: bytes):
        self.buf += data
        out = []
        while True:
            if len(self.buf) < self.HEAD:
                break                       # 头都没收齐
            magic, ver, length = struct.unpack(
                '!BBH', self.buf[:self.HEAD])
            if magic != MAGIC:
                raise ValueError('bad magic, 流错位')
            if len(self.buf) < self.HEAD + length:
                break                       # 正文没收齐
            out.append(bytes(self.buf[self.HEAD:self.HEAD+length]))
            del self.buf[:self.HEAD + length]
        return out

dec = Decoder()
assert dec.feed(encode(b'hello') + encode(b'wor')) == [b'hello']
assert dec.feed(encode(b'ld')) == [b'world']

核心是缓冲区加状态机:无论字节怎么被切,凑齐一条吐一条。长度字段要校验上限(示例2字节最大65535,超限直接断连),否则恶意长度头能把内存吃穿。

协议里的现成例子

HTTP/1.1的Content-Length就是长度前缀,chunked编码是「长度+数据」的分块版;Redis RESP用行结尾\r\n加分隔符;WebSocket有自己的帧头;gRPC直接上HTTP/2帧。大家都在各自的层里解决同一个问题。

迁移长连接前,把「消息边界」四个字写进设计文档,能省掉一整类返工。

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0)

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。