CHUAN2 DEV ENGINE
996 正版授权研发中心 · 360 授权合作教学中心 · 抖音传奇直播合作授权 · 快手推广运营商授权
OFFICIAL LICENSED ACADEMY 查验官方授权证书 →
// 威海旷世互娱教学基地 · 技术文章
网络通信框架设计

【网络通信】TCP 粘包拆包与 Protobuf 序列化实战

2026-09-20 07:41 作者:996 技术组 4 阅读 996引擎Lua教程传奇脚本
完整课程入口:996 全套课程体系Lua 学习路径幂尔框架 mirs.cn

为什么会粘包

TCP 是流式传输,它只保证字节按序到达,不保证"一次发送对应一次接收"。你 send 了三条消息,对方可能一次收到两条半;发一条 8KB 的大消息,也可能被切成三段到达。这就是粘包与拆包。它们不是 bug,是 TCP 的本性,所以应用层必须自己定义消息边界——所有"偶现的解析错乱"十有八九是边界没处理干净。

游戏里通行的做法是"长度前缀协议":每条消息前面加 2 或 4 字节的长度字段。接收方维护一个缓冲区,收到数据就追加进去,然后循环判断:缓冲区够不够读出一个长度头?够,读出长度 N;缓冲区剩余够不够 N 字节?够,取出完整消息、移除,继续循环;不够,等待下次数据到达。这段"拆包状态机"是网络层的地基,务必独立成函数并反复测试。

lua
-- 拆包骨架(示意)
while true do
    if #buf < 4 then break end
    local len = string.unpack(">I4", buf)      -- 4 字节大端长度头
    if #buf < 4 + len then break end           -- 半包,等下一段
    local msg = buf:sub(5, 4 + len)
    buf = buf:sub(5 + len)
    handleMessage(pb.decode("game.Msg", msg))
end

Protobuf 在协议层的价值

消息体序列化,Protobuf 是成熟选择:字段用编号编码,体积比 JSON 小一大截,加字段不破坏老客户端的兼容性(新增字段老代码会跳过未知编号)。工作流是先写 .proto 描述文件,用工具生成各端代码;Lua 侧常用 pb 库直接 pb.encode/decode。协议演进纪律只有一条:老字段只废弃不删除、新字段只加在末尾,编号永远不复用,否则新老版本混跑期间必然出脏数据。

配套三件套

心跳:客户端每 15~30 秒发一个空心跳包,服务器两倍周期没收到就标记掉线。它同时承担"探测半开连接"和"防止中间设备回收空闲连接"两个职责。

断线重连:重连后不能裸连,先走"校验 token + 同步版本号 + 补发离线期间的关键变更"三步。给每条关键消息带自增序号,重连时把序号报给服务器要增量数据,可以大幅简化状态同步。

封包校验:长度头里留两位做魔数校验,解析前先核对,能把脏数据在入口处拦下来。网络层的问题永远要"早失败、带上下文失败",把缓冲区长度、预期长度记进日志,联机问题才能定位到具体一包。

← 返回文章地图返回研学路径