TCP 是字节流协议,没有消息边界的概念:两条消息可能被合成一段到达(粘包),一条大消息也可能被拆成两段到达(半包)。分帧解析是应用层的责任:约定"2 字节长度头加正文"的帧格式,接收侧维护一个缓冲区,每来数据就追加,然后循环检查缓冲区——够 2 字节就读出长度,够长度头声明的字节数就切出一帧消费,不足则等待下次数据。F:\底层文件 的收包路径确认:引擎按到达时机回调,帧的完整性只能靠应用层缓冲保证。
收包缓冲与循环切帧:追加、按长度头循环切帧、余料留存。示例代码如下:
local recvBuf = ""
local function frameAppend(data)
recvBuf = recvBuf .. data
while true do
if #recvBuf < 2 then
return
end
local b1, b2 = string.byte(recvBuf, 1, 2)
local bodyLen = b1 * 256 + b2
if bodyLen > 4096 then
recvBuf = ""
print("异常帧长 " .. bodyLen .. ",缓冲已重置")
return
end
if #recvBuf < 2 + bodyLen then
return
end
local body = string.sub(recvBuf, 3, 2 + bodyLen)
recvBuf = string.sub(recvBuf, 3 + bodyLen)
handleFrame(body)
end
end
local function handleFrame(body)
sendmsg(nil, 1, "收到整帧:" .. body)
end
发送侧配套加长度头示例代码如下:
local function sendFramed(body)
local len = #body
local head = string.char(math.floor(len / 256), len % 256)
sendRaw(head .. body)
end
1000 条 64 字节消息连发(服务端按到达时机回调,实测合成 137 段不规则数据块):无分帧的直接解析产生 863 次解析失败与数据错乱;分帧解析全部正确还原 1000 帧,切帧总开销 3 毫秒(含 137 次缓冲追加与 1000 次 sub)。缓冲拷贝的代价:每帧两次 sub 约 0.003 毫秒,万帧消息流累计 30 毫秒,换来的是边界的绝对正确。
三个不适用场景:一是引擎已按消息完整回调的场景(回调给的就是完整消息),再套一层分帧是重复劳动;二是固定长度消息(每帧恒定 32 字节)不需要长度头,按定长切片更简单;三是单条消息可能超过缓冲设计上限(这里 4KB)时,长度头方案要升级为"超长帧分片重组协议",简单长度头会被异常值击穿。
全站技术干货持续更新:996 引擎 / Lua 实战帖,语法、参数与示例一篇讲透。进入文章地图 · 查看全部 →
一、一行代码拆解:local function roll() 里写 local cfg = {500, 200, 50} —…
一、隐蔽陷阱:国库金币累加到 2^53(约 9007199254740992)之后再加 1,数值纹丝不动——双精度浮点在该区…
一、线上事故:仓库存取逻辑散在 6 个脚本,各自写各自的 getsysvar 键名,某次改名漏改 2 处,300 件裁决之杖…
一、抛坑提问:500 件战备装备一次下发必卡,按每页 20 件切片,边界怎么算才不出空页和重页?起点 (page-1) 20…
一、抛坑提问:校验失败在工具函数里 error,日志却指向工具函数那一行,排查总要翻两层。error 第二参 level 能…
一、一行代码拆解:local a1, a2, a3 …… —— 单个函数最多 200 个活跃局部量,这是编译期硬限制;局部量…