Skip to content

长度前缀分帧与粘包拆包练习 ​

前置:理解字节流、字节序、客户端与服务端;Python 3.10+ 用于四字节长度前缀解码模型,不要求真实网络。教学范围为TCP RFC9293;分层与字节流;Python 3.10+。核对日期2026-10-17。示例未执行,本轮没有安装依赖、生成协议代码、启动服务或浏览器预览。完整源码和输入文件见下方「同源完整文件」;推演结论只适用于写明的模型。

完整长度前缀解码器 ​

缓存循环每次只消费完整的一帧,之后立即检查是否还有下一帧。等待头部和等待负载是不同状态;超长声明应在读出长度后立即拒绝,不能先尝试分配声明大小的内存。

查看图表代码

完整长度前缀解码器接收离线十六进制分块并维护缓存,头部不足四字节时等待,负载足够才消费整帧。结束时剩余字节表示截断,超过1024字节的声明提前拒绝,避免恶意长度让接收端无限保留内存。

分帧手推预期 ​

分帧手推预期中大端头00000003代表三个字节,负载616263解为abc。切成0000和0003616263仍得到同一消息;中文“你”占三个UTF-8字节。有效头00000000允许空消息,非法UTF-8应独立报错。

粘包拆包练习 ​

粘包拆包练习把两帧合成一块,再从第二帧头部中间拆开,检查每次消费后残余缓存。增加消息长度上限可以限制单帧,但若把所有输入一次性读入内存,仍存在整体输入规模问题;这里的离线工具不假装生产流式服务器。

同源完整文件 ​

以下README含前置、输入、读者自跑步骤、预期结果、失败、恢复与清理;只做静态核对,没有执行。

model.py ​

python
import json
import sys

def decode(chunks):
    buffer = bytearray()
    messages = []
    for chunk in chunks:
        if not isinstance(chunk, str):
            raise ValueError('hex strings required')
        buffer.extend(bytes.fromhex(chunk))
        while len(buffer) >= 4:
            size = int.from_bytes(buffer[:4], 'big')
            if size > 1024:
                raise ValueError('frame too large')
            if len(buffer) < 4 + size:
                break
            messages.append(bytes(buffer[4:4 + size]).decode('utf-8'))
            del buffer[:4 + size]
    if buffer:
        raise ValueError('truncated frame')
    return messages

if __name__ == '__main__':
    try:
        data = json.load(sys.stdin)
        if not isinstance(data, list):
            raise ValueError('array of hex chunks required')
        print(json.dumps(decode(data), ensure_ascii=False))
    except (ValueError, TypeError) as error:
        print(str(error), file=sys.stderr)
        sys.exit(2)

requirements.txt ​

text
# Python 3.10+ standard library only; no third-party dependency.

README.md ​

md
# 计算机网络原理:完整示例

示例未执行。核对日期2026-10-17。教学版本族:TCP RFC9293;分层与字节流;Python 3.10+。

## 文件与依赖

文件:model.py, requirements.txt。requirements.txt为同源依赖声明;所有代码和配置都随本文展示。本例使用 Python 3.10+ 标准库,无额外运行依赖。

## 输入与前置

前置:理解字节流、十六进制字节和四字节大端长度头。使用 Python 3.10+,只依赖标准库;输入是表示分块字节流的十六进制字符串数组,不建立真实 TCP 连接。具体输入和分帧预期见下节。

读者将来自跑(本轮未执行):在本目录以Python 3.10+读取input.json,PowerShell示意`Get-Content -Raw input.json | python model.py`;Bash示意`python3 model.py < input.json`。把下面输入手工保存为UTF-8文件,输出按预期逐项比较。无需安装依赖,没有服务或网络调用;程序只读标准输入并写标准输出,不改原文件。失败信息写标准错误且退出2。恢复是修正输入后重读,清理只删除自己创建的input.json与输出文件,勿递归删除工作目录。

## 预期结果、失败与恢复

输入 ["0000","0003616263"],预期["abc"];["00000000"]得到[""];["0000000361"]退出2并报告truncated frame;["00000401"]报告frame too large。

失败要保留输入和错误,先与正文的不变量及边界核对。没有真实服务或性能验证,不以这些模型输出证明生产可用性。

## 独立练习

解码00000001610000000162;把分界改在第二帧头部第三字节后,再说明结束时缓存应有多少字节。

参考答案:先读第一帧长度1、负载61得a,消费5字节;第二帧头部不完整时保留3字节,下一块补齐长度和62后得b,全部结束缓存为0。未补齐就结束应报截断,不能把缓存静默丢弃。

先作答,再核对 ​

解码00000001610000000162;把分界改在第二帧头部第三字节后,再说明结束时缓存应有多少字节。

逐步参考解答

先读第一帧长度1、负载61得a,消费5字节;第二帧头部不完整时保留3字节,下一块补齐长度和62后得b,全部结束缓存为0。未补齐就结束应报截断,不能把缓存静默丢弃。

来源与适用边界 ​

继续阅读