当前位置: 首页 > backend >正文

skynet.socket.recv 没有处理分包问题

在这里插入图片描述

目录

  • 核心机制说明
  • 解决方案
    • 1. 固定长度协议
    • 2. 分隔符协议
    • 3. 混合协议(长度 + 内容)
  • Skynet 中的推荐实践
  • 注意事项
  • 总结

在 Skynet 中,skynet.socket.recv 不会自动处理 TCP 粘包/分包问题。由于 TCP 是流式协议,数据可能被拆分成多个片段到达,也可能合并成一次接收,开发者需要自行设计协议来确保数据的完整性。


核心机制说明

  1. skynet.socket.recv 的行为

    • 从 Socket 缓冲区读取当前可用的数据块。
    • 若缓冲区为空,则挂起当前协程,等待数据到达后继续读取。
    • 返回的数据可能是完整包、部分包,或合并了多个包的片段。
  2. 粘包/分包的必然性

    • 发送方调用 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 中的推荐实践

  1. 使用 netpack 模块
    Skynet 提供了 netpack 库辅助处理粘包,例如:

    local netpack = require "skynet.netpack"
    local buffer = netpack.filter(handler_func, "data")  -- 返回分片后的数据队列
    
  2. 协程 + 缓冲区管理

    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)
    

注意事项

  1. 性能优化

    • 避免频繁拼接字符串,可使用 table 暂存分片。
    • 对高吞吐场景,考虑预分配内存或使用 C 模块加速。
  2. 协议设计安全

    • 校验长度字段合法性(如最大允许值)。
    • 处理恶意数据(如超长包导致内存耗尽)。
  3. 调试工具

    • 使用 hexdump 打印二进制数据,观察实际传输内容。
    • 添加日志记录分片过程。

总结

Skynet 的 socket.recv 不处理分包,开发者需通过协议设计(如长度前缀、分隔符)结合缓冲区管理,自行实现粘包/分包解析逻辑。建议参考 Skynet 官方示例(如 gate 服务)或使用 netpack 库简化流程。

http://www.xdnf.cn/news/3799.html

相关文章:

  • 办公文档全能处理工具功能解析
  • GR00t 安装使用教程踩坑记录
  • 专为焦油介质打造:煤焦油专用气动硬密封调节 V 型球阀(带手动)的卓越特点-耀圣
  • mvvm 如何 实现 MultiBinding 与转换器
  • SCAU18124--N皇后问题
  • 基于Vue2 + Element 实现任务列表管理功能的详细教程
  • tp5 php获取农历年月日干支甲午
  • MCP协议的使用分享
  • 数据库=====
  • 2025 年最新 Python 语言实现网易企业邮箱邮件推送验证码详细教程(更新中)
  • 智能决策支持系统的基本概念与理论体系
  • Ubuntu下安装Node.js
  • 【java八股文】深入浅出synchronized优化原理
  • 嵌入式Linux应用项目----智能网关
  • Docker Compose:服务编排:批量管理多个容器
  • 《Java高级编程:从原理到实战 - 进阶知识篇四》
  • 利用Elixir中的原子特性 + 错误消息泄露 -- Atom Bomb
  • 深度思考Qwen3
  • MySQL 中日期相减的完整指南
  • # 基于词袋模型(BoW)的猫狗图像分类实践
  • vue的diff算法是什么、比较方式,原理分析、示例解释讲解
  • 迭代器的思想和实现细节
  • 【序列化与反序列化详解】
  • 【漫话机器学习系列】237. TSS总平方和
  • 【2025软考高级架构师】——未来信息综合技术(11)
  • C++笔记-多态(包含虚函数,纯虚函数和虚函数表等)
  • 在MySQL中建索引时需要注意哪些事项?
  • Vue3源码学习5-不使用 `const enum` 的原因
  • 普推知产:图形商标通过初审,图形商标申请时注意!
  • 【深度学习】典型的 CNN 网络