怎么理解 TCP 粘包与拆包?

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

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

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

11831 次点击
所在节点    问与答
99 条回复
jj783850915
2021-01-24 11:51:33 +08:00
日经贴 隔一段时间就要来一次
tairan2006
2021-01-24 13:33:28 +08:00
算了,还是拉黑楼主吧,站内搜索都不会用。
no1xsyzy
2021-01-24 13:46:11 +08:00
@lewis89 看了一下,似乎 WebRTC 是或者可选是 SCTP 的……
这具体的我也不想埋进 RFC 读了,只看到原本的运用面也就是电信方面的。
你上面那位也提到了用户态 SCTP 库,但目前 WebSocket 有许多开箱即用的好处,比如现成的 SSL/TLS 体系、以及反向代理。

@Orenoid @Mutoo 实际上这名字也不叫拆包叫分帧……
上面某篇文章看了下,IP 叫包,TCP 叫段,UDP 叫报,SCTP 和 WebSocket 叫消息,比包包包包清晰多了。
实际上拆包并包应当是存在的,比如 TCP 一个段的数据超过 IP 包大小上限时 src 发生拆包,之后 dst 再并包提交给 TCP 层。
至于反过来两个段分别放两个包提交出去不会被 IP 层粘在一起,除非是 TCP 中继,解包重构流再重新分段发包。
l00t
2021-01-24 13:59:20 +08:00
@Jirajine #21 谁跟你说读写文件打 log 就不会“粘包”? 包是上层的包,怎样算一个包,是上层界定的。你想一句一句地处理,结果一次读到了两句,这就是“粘包”,你得自己再拆开来。
Jirajine
2021-01-24 14:14:26 +08:00
@l00t 只要你把它看做流,就像水龙头下接水,控制一次接多少是自然而然的。
但你要不把它看作流,水是装在一个个大小不一的杯子里过来的,你才需要额外考虑“拆包”、“并包”,把大杯拆成小杯小杯合成大杯。
而这种认知是错误的,数据在一个个包里传输属于 TCP 的内部细节,TCP 对上层的表现就是无分段的流。
nightwitch
2021-01-24 14:28:08 +08:00
一些二流的程序员因为对网络协议掌握的一团浆糊, 在编程实践中不能理解明明我 send 了 5 次为什么我 recv 一次就收到了所有的数据,进而将这样的现象归咎于 TCP 的实现,从而总结为"粘包",并在互联网上传播一些让人啼笑皆非的概念.
Y29tL2gwd2Fy
2021-01-24 14:32:52 +08:00
网线上刷层黄油就不会沾了
l00t
2021-01-24 14:51:23 +08:00
@Jirajine #65 当你说到 “数据在一个个包里传输属于 TCP 的内部细节”, 就说明你对“粘包”这个名词的认知是错误的。两个地方的“包”都不是同一个概念。应用层的包,TCP 根本就不认识,有个毛的细节。

水按一杯一杯地来,是你的需求,不是说你把水看成流就能绕过去了。你看成流你也要把水装到一个个杯子里。而且实际编程中,你也不是想控制一次取多少就取多少,实际中因为 API 的关系水是一桶一桶来的。
Orenoid
2021-01-24 14:59:00 +08:00
@no1xsyzy #63
我也不确定拆包或者分帧是不是一个标准的叫法,因为这里只是在抽象地讨论应用层对流式数据的拆分,没有具体的应用层协议可以对应,比如 HTTP 报文、WebSocket 消息就有一个标准的命名。但脱离了具体协议的话,我之前学习的时候并没有看到对这个行为有一个标准的统称。

不过我觉得这个不是重点,只要做过这一块相关的开发,就都能理解大家说的其实是一个问题。我想强调的点还是在于,不该使用“粘包”这个叫法,是因为很容易误导新手。比如上面一位老哥提到的,“在编程实践中不能理解明明我 send 了 5 次为什么我 recv 一次就收到了所有的数据”,新手本来就理解不透彻,然后网上一搜,再看到一些不懂装懂的“粘包”文章,就被彻底带歪了。
Jirajine
2021-01-24 15:02:30 +08:00
@l00t 你们所谓的“粘包”,不就是发了多个应用层的包被“粘”到一个 tcp 包里,这当然错误的理解了 TCP 的实现细节。
你把 tcp 看成水流,当你需要一杯一杯接水的时候自然就会控制一次接多少。实际编程中流式 API 当然是想一次取多少就取多少,如果数据不够就会阻塞直到足够,如果多余则会缓存下来供下次调用。
wudaye
2021-01-24 15:10:56 +08:00
这玩意做过的人就会觉得很简单的事情,没做过的某些人就会以为是很玄的东西
momocraft
2021-01-24 15:39:43 +08:00
一個不錯的民科過濾器
k8ser
2021-01-24 16:17:07 +08:00
英语里拆包是 unpack,那粘包你怎么翻译???
ihuzhou
2021-01-24 16:41:46 +08:00
你们口中的包是不是同一个都没定好,还争个啥啊...
ihuzhou
2021-01-24 16:42:41 +08:00
同理“拆包”,“粘包” 都没定义好,这么说没啥意思
tqz
2021-01-24 16:44:29 +08:00
@l00t 正解,先说 TCP 是流,不存在包,然后分析应用层上面关于包的问题才是正常人的做法
eastphoton
2021-01-24 20:30:09 +08:00
1.流传输是 TCP 的设计目的,是 feature,不是缺点。

2.需要实现把传输层的 TCP 数据流拆成应用层的包,也是客观事实。
(无论你是否接受发明粘包与拆包这一对名词,只要传输层用了 TCP,应用层又想用包,就得这么干没错吧)

以上两点,这应该是没有什么可争议的。


1 本来就是设计目的 2 是一个类似封装的事,是分层模型自然而然的事。

搞不清楚的话,建议重新系统学习 计算机网络 课程。
no1xsyzy
2021-01-24 21:25:43 +08:00
@Orenoid agreed

@k8ser 这里又有一个词义问题,拆包到底是 1. 给定一个包,解析包并提取包体;还是 2. 给定一个包,分拆成 N 个包;还是 3. 给定流,分拆成若干包? 1 是 unpack 。2 是 split packet ? repack ? 3 才是主题提出的问题,没有明确名称但 framing 是一个选项

@eastphoton (其实 TCP 数据流不一定要解析成包,如果 HTTP/1.0 那样不允许 keep alive 就没这个问题,其次还有像 telnet 那样不进行重新封装在上层仍然是流的话也不需要)
wzhy
2021-01-24 21:30:59 +08:00
抽刀断水水更流^^
eastphoton
2021-01-24 21:57:03 +08:00
@no1xsyzy 就知道要有这么回的,我已经标了“应用层又想用包”。。
没有这个前提的话,应用层当然随你说了算。

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

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

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

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

© 2021 V2EX