怎么理解 TCP 粘包与拆包?

2021-01-23 18:40:22 +08:00
 narutow

记得一个大佬跟我说过, "TCP 哪有什么粘包拆包的问题, 人家本来就是流式协议, 你用它传你的结构化数据, 你是做数据的序列化和反序列化, 而不是在克服 TCP 的缺点."

这样的理解是否准确呢, 大家什么看法. 本菜鸡感到疑惑~

10672 次点击
所在节点    问与答
99 条回复
GGGG430
2021-01-23 18:42:40 +08:00
回复你的这个人其实也是从网上看的别人的术语
kyuuseiryuu
2021-01-23 18:44:36 +08:00
本质上就是要你重新设计一个应用层协议,over 。
bengol
2021-01-23 18:57:34 +08:00
哪儿发明的乱七八糟的名词
lewis89
2021-01-23 19:19:51 +08:00
因为 TCP 设计就是这样的,而且设计 TCP 的人也只是设计成了流,以至于 TCP 里面有一个 tcp_nodely 的开关,因为小包
用 TCP 发过去不划算(报文头可能比报文本体还大),操作系统要延迟一会看你有没有继续写入,然后凑一起再发出去,但是对于 telnet 这种命令行,你延迟发过去,远程操作服务器的人就有延迟,人用起来就不爽了。

TCP 流式传输本质上并没有解决上层应用程序员的问题,程序员要的是 类似 HTTP Websocket 这种一问一答的设计,而且这种设计的可靠性由底层来维护,上层的应用逻辑不需要写代码来尽可能维护这个数据传输可靠性,但是除了 TCP 一个能打都没有,你就只能用 UDP,但 UDP 既不保证 包的顺序,也不保证包一定送到,还得写一大堆逻辑去维护会话状态,所以大家纷纷用 TCP 来做可靠性,像 HTTP 里面 实际上就是浏览器用 HTTP 头告诉 http 服务器,我剩下的 body 内容有多少,服务器兄弟你慢慢读,读完这么多就算是我一个 http 请求的包,服务器兄弟你千万别搞混球了把下一个 http 的报文头 一起当一个 http 包给解析了。
lewis89
2021-01-23 19:23:02 +08:00
carlclone
2021-01-23 19:28:53 +08:00
先理解字节流..... 比如 Java 的各种 Stream , 然后类比就懂了
dcalsky
2021-01-23 19:31:32 +08:00
坐等粘包警察到来。
Archeb
2021-01-23 19:33:57 +08:00
月经问题,答到不想答了...
chanlk
2021-01-23 19:40:54 +08:00
最近工作刚好遇到了这个问题, 前来说一下:

工作是负责搭一个 Server, 用来接收一些智能家电传上来的数据;

这些数据是硬件厂商自定义的协议, 开头和结尾有一些标记 整体格式如 [ 起始码 长度 数据 结束码 ]

对于我服务器来说, 我当然是希望每次取到的消息都是一条完整的消息, 不多不少不错刚刚好
但是实际上, 因为对于 tcp 来说这个, 他在传输的时候, 并不会理会你应用层是怎么定义的, 只管往外发
我上面说的一条数据, 在到达我服务器的时候, 可能被分成了好几个数据包
这个时候就是所谓的拆包了, 如果服务端不对取到的数据做合法校验的话, 就会出问题了

服务端这个时候就要对取到的数据, 根据应用层定义的协议, 当满足协议的时候, 才将数据从 buffer 中取出
然后再进行业务操作.

关键是理解 TCP 的流的概念吧~ 这个流是一个没头没尾的东西, 所以需要长度, 标记符之类的来定义数据的边界
个人的理解大致是这样吧.
soulzz
2021-01-23 19:52:06 +08:00
用 netty 随便去解析任意一个基于 TCP 的常见协议你就明白了
我拿 JT808 举个例子 0x7E 作为包与包之间的区分
包中有 7E 的转义为 7D01,保中还会有 BCC 校验位,去除包头包尾后的长度位

至于为什么一定要有区分?设备几毫秒发送几十个包都有,经常发生的情况是一个包附带好几个剩下的包作为一次发送的数据。为了确保每一个包内容完整无错,所以必须要有处理粘包的逻辑,最常见的就是固定包头包尾+包内容转义+内容长度位+校验码

有些 iot 设备发包频率低,可能几十秒才发一个包,理论上这样随便基于 tcp 自定义协议都可以,粘不粘包完全不用考虑,但是实际情况是,一个服务器可能会处理上万十万个 iot 设备连接,你这个设备发送的报文后面马上跟着其它设备的报文完全是可能发生的
soulzz
2021-01-23 19:54:00 +08:00
当然,如果是 udp 的话就不用考虑这么多,设备直接发完整包,包中有校验位校验就可以了,服务器收不收得到不用考虑
lewinlan
2021-01-23 20:10:37 +08:00
学 go,自然就懂了
Mutoo
2021-01-23 20:13:01 +08:00
TLDR: 业务中使用 TCP 协议获得一个有序的稳定的流作为消息的载体。但在实践中应用层不断向传输层轮循缓冲区的数据的时候会得到片段化的数据,虽然有序但是这些缓冲中的数据可能归属是多个不同的消息(造词:粘包)。将这些数据以特定形式(例如包长度(length)+负载(payload))拆分或重组成逻辑消息(造词:拆包)。
laminux29
2021-01-23 20:22:46 +08:00
1.TCP 通信中不存在 [黏包] 这个问题。

2.遇到 [黏包问题] 的人,基本上都是因为这方面学习不过关。

3. [黏包问题] 的发展史是:
很多年前,一堆基础不扎实的黑客,以及上一代的程序员们,经常用 VC 写各种 Windows 客户端程序。一方面他们需要进行通信编程,另一方面由于这部分内容对他们的水平而言比较深奥,他们掌握不好,因而采用“如何简单就如何来”的策略,首当其冲就是 udp 。

VC 的 UDP 编程,一条数据发一个数据包,自然不存在界限问题;同时 VC 的 UDP 编程,代码简单,收发逻辑简单明了,因此深受这群人的欢迎与追捧。

接着,天时地利人和,这群人的时代,恰好是单物理核 CPU 大行其道,最多也只会遇上单物理核的超线程 CPU,因此即使把 UDP 换成 TCP,只要模块拆分得当,就算是并行的程序,也不易黏包。不过也只是不易而已,这些程序时刻有黏包出错的风险,因此,他们那时的程序,很不稳定,经常需要关闭重启。

他们的历史,也就到此为止了。

由于通信应用的复杂性,以及用户需求进一步提出更高的要求,当他们尝试多条数据放在一个 UDP 数据包里进行发送时,就会出现所谓的 [黏包] 问题。这是其一。他们所谓的 [粘包] ,也就是他们本来的收发逻辑是,一次发送或接收,只处理一个数据包,然后一个数据包里,只存放一条数据。现在他们把多条数据放在一个数据包里,甚至多条数据放在多个数据包里,于是在发送与接收的业务就全变了,同时又需要引入合并、拆分、处理顺序以及考虑性能问题,整个流程变得非常复杂,他们处理不了,程序经常出现问题。

UDP 功能有限,因此他们总会需要用到 TCP 。此时,由于水平的局限性,当他们按照 UDP 的方式来进行 TCP 通信时,就非常容易遇到所谓的 [黏包] 问题。这是其二。

这帮人水平本来就欠缺提高,再遇上那时双物理核以及更多物理核的 CPU 开始普及,多线程与并行编程就跟着开始流行,这对于程序员提出了更高的要求。此时,这群人在并行程序里按照以前串行程序的老路子进行通信,注定会遇到黏包问题。这是其三。

以上三种场景,被这帮人把这个问题总结出来,称之为 [黏包] 问题,并且在网络各处留下这个问题的详述,以及一堆古灵精怪嘀笑皆非的解决方案。
Jirajine
2021-01-23 20:25:11 +08:00
沾包就是自己加戏,本身就是流式协议哪来的沾包。要控制底层传输方式去用 udp 去。
Leigg
2021-01-23 20:32:26 +08:00
粘包警察还有三十秒到达此贴
l00t
2021-01-23 20:38:28 +08:00
否认”粘包”问题的人才是理解能力有问题。这里说的 "包”又不是指 TCP 的包。TCP 是流式协议自然是没包,但是应用层的业务有包啊。一说流协议就没有“粘包”,是根本都没弄清楚别人说的什么概念。
huskar
2021-01-23 20:40:36 +08:00
不知多少年前的二流程序员,在对 tcp 一知半解的情况下搞出来这么一种叫法,竟能遗毒至此,误导无数后人,呜呼哀哉。
l00t
2021-01-23 20:41:52 +08:00
而且提出 "TCP 粘包问题” 又不是说 TCP 协议有什么缺陷,而是描述了这么一个现象。你要用 TCP 那你自然要处理这个问题,处理办法一般要么设置长度信息,要么设置边界。因为这点就去用 UDP ?因噎废食么?
jim9606
2021-01-23 21:11:52 +08:00
没啥问题。

TCP 本来是一个流传输协议(数据不分块,保证有序,也就是只保证多次 recv 的数据依顺序拼起来保持原样),偏要把它当成一个消息传输协议(数据分块,保证有序,保证一次 send 对应一次 recv )来用。
不巧的是在环回网络以及短途网络里调试时,这种误用并不会出问题。

如果不是性能或者效率原因,建议用 WebSocket,这是正儿八经的消息协议,可以省掉定界的麻烦。

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/747735

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX