关于tcp的”粘包“和“拆包”
第一次听到“粘包”这个名词,还是用 websocket 做游戏的时候,当时服务端收到的消息出现了乱码,因为中文汉字在utf-8中用三个字节表示,如果被截断了话,解析出来就是乱码。
后面就一直做的传统的http服务端开发,就没有太关注这个问题了。
最近在看一个《趣谈网络协议》课,就顺便把这个问题搞明白。
为什么会出现”粘包“和“拆包”
知乎上有些说什么 “中式伪科学” 之类的来鄙视称呼“粘包”和“拆包”的人。
关于“粘包”这个名词,我们不去讨论它的正确性,我觉得只要能使大家达成共识,明白表达的意思就行。
关于tcp的知识也不详细说了,上一篇文章就是《趣谈网络协议的笔记》。
首先tcp是一个面向流的全双工协议,并且在发送端和接收端分别有一个 发送缓冲区和接收缓冲区。
因为 tcp 是一个面向流的协议,而流是没有边界的,使用流来传输数据时,tcp 并不能理解这些数据在业务层含义,并且有缓冲区的存在,tcp不会及时的读取和发送数据。
所以发生“粘包”和”拆包“的原因差不多就是:
- 要发送的数据大于tcp缓冲区的大小,会发生“拆包”
- 待发送数据大于MSS(最大报文长度),TCP在传输前将进行”拆包“
- 要发送的数据小于TCP发送缓冲区的大小,TCP将多次写入缓冲区的数据一次发送出去,将会发生“粘包”
- 接收数据端的应用层没有及时读取接收缓冲区中的数据,将发生“粘包”
关于buffer这篇文章中,也有使用流读取取数据,被buffer截断的列子。
如何解决
一般来说,只要让接收方知道,每一个有效数据包的大小就可以了。
解决方法:
- 使用分隔符:比如换行符这种作为一个包的结束标记。
- 定义数据包长度:在数据包中增加一个长度字段,标记这个数据包的大小。
更多解决方法网上有很多,但是比较推荐的就是定义数据包的长度这种做法。
http协议是如何做的?
我一直有个疑问,自从没有做游戏开发(没有用websocket)后,好像就在也没有遇到过这类问题。
http 是基于tcp的应用层协议,tcp会遇到的问题,理论上来说它也会遇到。
但是为什么我从来不用关心,也没有遇到“粘包”问题呢?
推荐看一下这篇文章:https://blog.fundebug.com/2019/09/10/understand-http-content-length/
所以,我的理解就是,http 通过请求头的 content-length 来指定数据包的长度,所以每次解析出来的数据都和发送的数据一样大。
ps: 不知道理解的对不对。希望有人可以给我解惑。。
参考链接: