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

02 如何解决粘包问题

网络通信程序实际开发中,或者技术面试时,面试官通常会问的比较多的一个问题是:网络通信时,如何解决粘包?

有的面试官可能会这么问:网络通信时,如何解决粘包、丢包或者包乱序问题?这个问题其实是面试官在考察面试者的网络基础知识,如果是 TCP 协议,在大多数场景下,是不存在丢包和包乱序问题的,TCP 通信是可靠通信方式,TCP 协议栈通过序列号和包重传确认机制保证数据包的有序和一定被正确发到目的地;如果是 UDP 协议,如果不能接受少量丢包,那就要自己在 UDP 的基础上实现类似 TCP 这种有序和可靠传输机制了(例如 RTP协议、RUDP 协议)。所以,问题拆解后,只剩下如何解决粘包的问题。

先来解释一下什么是粘包,所谓粘包就是连续给对端发送两个或者两个以上的数据包,对端在一次收取中可能收到的数据包大于 1 个,大于 1 个,可能是几个(包括一个)包加上某个包的部分,或者干脆就是几个完整的包在一起。当然,也可能收到的数据只是一个包的部分,这种情况一般也叫半包

无论是半包还是粘包问题,其根源是上文介绍中 TCP 协议是流式数据格式。解决问题的思路还是想办法从收到的数据中把包与包的边界给区分出来。那么如何区分呢?目前主要有三种方法:

固定包长的数据包

顾名思义,即每个协议包的长度都是固定的。举个例子,例如我们可以规定每个协议包的大小是 64 个字节,每次收满 64 个字节,就取出来解析(如果不够,就先存起来)。

这种通信协议的格式简单但灵活性差。如果包内容不足指定的字节数,剩余的空间需要填充特殊的信息,如 \0(如果不填充特殊内容,如何区分包里面的正常内容与填充信息呢?);如果包内容超过指定字节数,又得分包分片,需要增加额外处理逻辑——在发送端进行分包分片,在接收端重新组装包片(分包和分片内容在接下来会详细介绍)。

以指定字符(串)为包的结束标志

这种协议包比较常见,即字节流中遇到特殊的符号值时就认为到一个包的末尾了。例如,我们熟悉的 FTP协议,发邮件的 SMTP 协议,一个命令或者一段数据后面加上"\r\n"(即所谓的CRLF)表示一个包的结束。对端收到后,每遇到一个”\r\n“就把之前的数据当做一个数据包。

这种协议一般用于一些包含各种命令控制的应用中,其不足之处就是如果协议数据包内容部分需要使用包结束标志字符,就需要对这些字符做转码或者转义操作,以免被接收方错误地当成包结束标志而误解析。

包头 + 包体格式

这种格式的包一般分为两部分,即包头和包体,包头是固定大小的,且包头中必须含有一个字段来说明接下来的包体有多大。

例如:

struct msg_header { int32_t bodySize; int32_t cmd; };

这就是一个典型的包头格式,bodySize 指定了这个包的包体是多大。由于包头大小是固定的(这里是 size(int32_t) + sizeof(int32_t) = 8 字节),对端先收取包头大小字节数目(当然,如果不够还是先缓存起来,直到收够为止),然后解析包头,根据包头中指定的包体大小来收取包体,等包体收够了,就组装成一个完整的包来处理。在有些实现中,包头中的 bodySize可能被另外一个叫 packageSize 的字段代替,这个字段的含义是整个包的大小,这个时候,我们只要用 packageSize 减去包头大小(这里是 sizeof(msg_header))就能算出包体的大小,原理同上。

在使用大多数网络库时,通常你需要根据协议格式自己给数据包分界和解析,一般的网络库不提供这种功能是出于需要支持不同的协议,由于协议的不确定性,因此没法预先提供具体解包代码。当然,这不是绝对的,也有一些网络库提供了这种功能。在 Java Netty 网络框架中,提供了FixedLengthFrameDecoder 类去处理长度是定长的协议包,提供了 DelimiterBasedFrameDecoder 类去处理按特殊字符作为结束符的协议包,提供 ByteToMessageDecoder 去处理自定义格式的协议包(可用来处理包头 + 包体 这种格式的数据包),然而在继承 ByteToMessageDecoder 子类中你需要根据你的协议具体格式重写 decode() 方法来对数据包解包。

这三种包格式,希望读者能在理解其原理和优缺点的基础上深入掌握。

http://www.gsyq.cn/news/1603791.html

相关文章:

  • 2026年银行全员营销新变局:当任务完成率统计成为“硬指标”,哪套系统真正能落地?
  • AI视频生成神器Pixelle-Video:3分钟让普通人变身视频创作高手
  • 地产三维动画制作公司怎么选:从技术路线到交付保障的完整决策框架
  • 3步掌握CDS API:解锁全球气象数据的Python神器
  • Pytest测试用例精准执行:从命令行筛选到CI/CD集成的完整指南
  • 第5章-与HTTP协作的Web服务器
  • 2026滨州黄金回收白银回收铂金回收旧料回收怎么选?五家高实价铂金白银线下门店测评清单 + 联系方式
  • 产品无人问津?五大采用障碍及Baklib AI知识库破局之道
  • 【学术导航】从SCI分区到IEEE Fellow:解码科研评价体系的核心指标与进阶路径
  • 【限时决策框架】:用3分钟完成你的ChatGPT付费评估——含自测清单+成本分摊计算器(仅开放72小时)
  • 全新强化学习框架 BeautyGRPO:重塑真实人像
  • 这个项目是做什么的
  • [MAF预定义ChatClient中间件-01]LoggingChatClient——在调用LLM前后输出日志
  • 通用PLM根本撑不住!汽车/芯片/新能源研发的痛,它懂[特殊字符]全星研发项目管理APQP软件系统来救场
  • 评估模块(EVM)使用指南:规避法律风险与安全合规要点
  • FDE课程: Codex+AI 编程+ SeedanceAI 视频+ AgentAI 智能体
  • 汉明码编码译码推演与验证(P124302158李晨雨)
  • C#摸鱼实录——IoC与DI案例详解
  • FanControl终极指南:三步搞定Windows风扇智能控制
  • DLSS Swapper:终极游戏性能优化指南,告别卡顿从版本管理开始
  • python爬虫实战项目|第100篇:爬虫技术全景回顾与未来展望
  • 强制访问控制的数学基石:深度拆解BLP机密性模型的设计哲学与工程遗产
  • vi 删除指定范围的行,不用再反复按 dd
  • 编写高质量 Skill 系列 -- 如何设计需求分析与用例生成的 SKILL
  • 如何在10分钟内解决离线音乐库的歌词同步难题?LRCGET批量歌词下载终极指南
  • Seedance 2.0 做短视频分镜,我最在意的不是“出片”,而是能不能交给团队复用
  • 洛谷 P1854 花店橱窗布置:从 OJ 题解到动态规划实战心法
  • TLS 1.3如何防御中间人攻击:从加密原理到Nginx实战部署
  • 2026 年深圳远程IO模块选型指南:明达智控MR30系列
  • 国内外网站建设选择-主流网站建设工具客观对比(2026年6月最新)含零代码、AI编程、源码定制交付