skynet.socket.recv 没有处理分包问题
目录
- 核心机制说明
- 解决方案
- 1. 固定长度协议
- 2. 分隔符协议
- 3. 混合协议(长度 + 内容)
- Skynet 中的推荐实践
- 注意事项
- 总结
在 Skynet 中,skynet.socket.recv
不会自动处理 TCP 粘包/分包问题。由于 TCP 是流式协议,数据可能被拆分成多个片段到达,也可能合并成一次接收,开发者需要自行设计协议来确保数据的完整性。
核心机制说明
-
skynet.socket.recv
的行为:- 从 Socket 缓冲区读取当前可用的数据块。
- 若缓冲区为空,则挂起当前协程,等待数据到达后继续读取。
- 返回的数据可能是完整包、部分包,或合并了多个包的片段。
-
粘包/分包的必然性:
- 发送方调用
send("A")
后调用send("B")
,接收方可能一次收到"AB"
。 - 发送方调用
send("12345")
,接收方可能先收到"123"
,再收到"45"
。
- 发送方调用
解决方案
1. 固定长度协议
- 适用场景:每个数据包长度固定(如协议头固定长度)。
- 示例:
local BUFFER = "" function on_recv(data)BUFFER = BUFFER .. datawhile #BUFFER >= 4 do -- 假设协议头为 4 字节长度字段local len = string.unpack(">I4", BUFFER) -- 大端 4 字节无符号整数if #BUFFER >= len + 4 thenlocal packet = string.sub(BUFFER, 5, 4 + len)process_packet(packet)BUFFER = string.sub(BUFFER, 5 + len)elsebreakendend end
2. 分隔符协议
- 适用场景:用特定字符(如
\n
)标记包结束。 - 示例:
local BUFFER = "" function on_recv(data)BUFFER = BUFFER .. datalocal from, to = string.find(BUFFER, "\n", 1, true)while from dolocal packet = string.sub(BUFFER, 1, from - 1)process_packet(packet)BUFFER = string.sub(BUFFER, to + 1)from, to = string.find(BUFFER, "\n", 1, true)end end
3. 混合协议(长度 + 内容)
- 常见设计:协议头包含长度字段,后续为实际数据。
- 优化技巧:
- 使用
string.unpack
解析二进制协议头。 - 使用环形缓冲区减少内存拷贝。
- 使用
Skynet 中的推荐实践
-
使用
netpack
模块:
Skynet 提供了netpack
库辅助处理粘包,例如:local netpack = require "skynet.netpack" local buffer = netpack.filter(handler_func, "data") -- 返回分片后的数据队列
-
协程 + 缓冲区管理:
local socket = require "skynet.socket" local function read_all(sockfd, len)local data = ""while #data < len dolocal chunk = socket.read(sockfd, len - #data)if not chunk thenreturn nil -- 连接关闭enddata = data .. chunkendreturn data end-- 使用示例:先读 2 字节长度头,再读内容 local len_header = read_all(sockfd, 2) local len = string.unpack(">I2", len_header) local packet = read_all(sockfd, len)
注意事项
-
性能优化:
- 避免频繁拼接字符串,可使用
table
暂存分片。 - 对高吞吐场景,考虑预分配内存或使用 C 模块加速。
- 避免频繁拼接字符串,可使用
-
协议设计安全:
- 校验长度字段合法性(如最大允许值)。
- 处理恶意数据(如超长包导致内存耗尽)。
-
调试工具:
- 使用
hexdump
打印二进制数据,观察实际传输内容。 - 添加日志记录分片过程。
- 使用
总结
Skynet 的 socket.recv
不处理分包,开发者需通过协议设计(如长度前缀、分隔符)结合缓冲区管理,自行实现粘包/分包解析逻辑。建议参考 Skynet 官方示例(如 gate 服务)或使用 netpack
库简化流程。