那些探讨所谓 TCP 层“粘包”问题的同学们,你们为什么不问问他的兄弟第一层的“物理层”,是如何在物理介质里面处理载波信号的“粘包”的?
对于网络基础同学比较扎实的同学,就应该知道所谓的 TCP“粘包”是不存在的,数据要在 TCP 层流式传播,首先就应该做好“表示层”的边界定位工作,而且数据本身就应该是结构化的。
大部分探讨“粘包”问题的案例,都是直接 nc 一个 TCP 端口,开始灌入 ASCII 码文本,但数据的传输并不是这么用的,就算在 C 语言里面,复杂数据也得用结构体来存储吧,就算是 HTTP,也使用了“\r\n”来进行分隔,你就直接往里面灌,能不出现问题嘛。
1
ahmcsxcc 2020-02-29 16:20:45 +08:00
那粘包是如何出现的呢?
|
2
dndx 2020-02-29 16:21:57 +08:00 6
粘包本身就是伪命题,讨论这个毫无意义。
|
3
mightofcode 2020-02-29 16:22:41 +08:00
人群中,你笑了
。。。 好吧,粘包确实是一个搞笑的概念,tcp 交付的是流式数据,就没包的概念,所以应该是怎么基于字节流解包 |
4
k9982874 2020-02-29 16:24:04 +08:00 via iPhone 2
你这说的就跟上帝面前人人平等一样,忽视现实哗众取宠而已。
|
5
hfc 2020-02-29 16:25:00 +08:00 17
既然你是在学习时搞明白的这个问题,那为什么不直接来写个详细的科普贴?
科普贴❌ 嘲讽贴✔ |
6
lloovve 2020-02-29 16:27:14 +08:00 via iPhone
用字节流传输时,哪来的包结构,都是 0101 数据了,要解包就需要,寻找包头,从而解析,黏包概念就是几组数据同时到达
|
7
BBCCBB 2020-02-29 16:30:22 +08:00 7
tcp 的确实流..
但是对业务方来说, 他要接收的是一个一个完整的包. 多个包的数据混在一起, 说粘包也没毛病. |
8
keygen88 2020-02-29 16:35:02 +08:00
你 G 点太低了。。。
|
9
Mohanson 2020-02-29 16:36:28 +08:00 via Android
TCP 卷一卷二卷三花一天时间看看就明白是怎么回事了,不存在的东西没必要去讨论
|
10
wanglufei 2020-02-29 16:41:27 +08:00 via Android 2
我找不到粘包警察那个帖子了。。
|
11
asilin OP @wanglufei https://www.v2ex.com/t/630159 哈哈,我们都是“粘包警察”。
|
12
waruqi 2020-02-29 16:53:19 +08:00 via Android
原本就是个伪概念
|
13
neoblackcap 2020-02-29 16:54:39 +08:00 9
我来再科普一次吧。
首先“粘包”是不存在的,“粘包”这一个词主要是面向低水平或者没有受过比较系统的网络编程学习的读者。 因为 TCP 是没有包这个概念的,都是流。 那么既然没有包这个概念了,那么“粘包”如何谈起?显然包这概念就出现在应用层了( TCP/IP 五层网络模型)。“粘包”本质上就是如何从流里面提取信息出错。导致好像两个信息好像粘连在一起一样。 实际上这是一个解析错误,本质是对二进制数据解析出错。根本不是 TCP 的问题,是应用层的信息解析器写得太烂了。出现解析错误,无法正确处理从 socket 里面读取的数据。 |
14
heiheidewo 2020-02-29 17:02:37 +08:00
没毛病啊,这个“包”是指上层的协议包
|
15
luopengfei14 2020-02-29 17:12:03 +08:00 via iPhone
@BBCCBB 我比较支持 7 楼的观点。
脱离业务的瞎逼逼没有任何意义 |
16
littlewing 2020-02-29 17:15:18 +08:00 via iPhone
不说物理层是因为不需要应用程序处理,应用程序处理的就是 TCP 的字节流
对于应用程序来说,我发出去的应用层的多个包,在 TCP 中都是字节流了,我需要自己去处理,识别出应用层的包 所以说对于应用层的包,我来处理的时候就是粘在一起了,这就是所谓的粘包 |
17
littlewing 2020-02-29 17:16:37 +08:00 via iPhone
@littlewing 另外,重来没有人说这是 TCP 的问题。如果你觉得这是问题,可以选择用 UDP 或者 QUIC
|
18
dndx 2020-02-29 17:17:13 +08:00
@BBCCBB 业务自己不做分包解包怎么保证发出来的是一个一个的包?更别说接收了。这说明业务根本就不懂流式协议是怎么用的。
或者要是自己实在是不会设计,用个 WebSocket 之类的已经封装好的协议也行啊。 |
19
tt67wq 2020-02-29 17:23:59 +08:00 via iPhone
粘包问题来自各种拿来主义的 java 程序员
|
20
dndx 2020-02-29 17:24:55 +08:00 2
其实这个问题很好理解,TCP 的设计就是一个类似文件流的读写行为,只是底层用的传输方式 IP 是基于包交换的,这是一种实现细节跟 TCP 想要达到的效果无关。
类比一下,系统读写磁盘也是基于块的,为什么没有人读写文件的时候抱怨过有“粘块”的问题?把 TCP 当作文件读写来对待就 ok 了。思维方式需要改变。 |
21
Archeb 2020-02-29 17:28:37 +08:00 1
楼主问的“什么不问问他的兄弟第一层的“物理层”,是如何在物理介质里面处理载波信号的“粘包”的?”这个问题,其实不太合理。
因为其实二层的 Ethernet 和三层的 IP 都分别有 Frame 和 Packet 的概念,不然为什么要有 MTU/MSS 呢? 只是到了四层的 TCP/UDP 等协议又把包化成流,对于更上层的应用程序来说不存在包的概念了而已。 |
22
reus 2020-02-29 17:30:52 +08:00
初学者不懂 TCP 的特性也就罢了,学习一下就能懂的。
有些人居然拿什么“现实”、“业务”来开脱,真是搞笑。 反正谁用这个词,谁就是没水平。 |
23
realpg 2020-02-29 17:34:55 +08:00 4
粘包换成人话就是:
如何处理流式数据的不同数据组分界问题。 |
24
Counter 2020-02-29 17:35:14 +08:00 via Android
难道不是因为双方讨论的不是同一层的问题
|
25
Orenoid 2020-02-29 17:39:10 +08:00 1
没有系统学习带来的用词不当与伪概念,但逻辑上都是在讨论应用层协议的问题。
看到纠正就好了,不好好解释反而一通嘲讽并不可取。 |
26
back0893 2020-02-29 17:42:35 +08:00
惹不起,惹不起
@粘包警察 |
27
zsdroid 2020-02-29 17:45:44 +08:00 3
为什么很多人喜欢揪字眼?
我在 tcp 层处理数据出现的现象,叫做 TCP“粘包”,仅此而已。这种叫法也无可厚非啊。不代表是 tcp 传输出现了粘包。 而 tcp 以流传输,然后就说 TCP“粘包”不存在。 照这么说,bug 其实也不存在喽,出现 bug 只是因为你基础不扎实?? |
29
laoyur 2020-02-29 18:03:10 +08:00
其实很简单,因为楼主觉得他弄明白了,所以肆无忌惮地加入了嘲讽的行列
|
30
arloor 2020-02-29 19:55:20 +08:00 2
你写过 tcp 网络编程吗?
你是自底而上学习了网络编程,觉得 tcp 粘包 wtf 而有的人是自上而下,先写 tcp 网络编程,遇到粘的问题,然后再从“粘”这个特点去寻找答案 两种都可以,自上而下,自下而上,两种学习方式而已。 问题 1:有什么好嘲讽的? 问题 2:你自底向上学了网络模型,那你写过网络编程吗?你有没有考虑过如何在应用层解决“粘”?程序员的精神是 show me the code,而不是“我笑了”。那些对说“粘包”的人冷嘲热风的人,真的可耻 从你的描述,“自底而上”,80%的可能性,你没写过。 最后一句,那些说粘包的,他们又没说粘的是 tcp 的包,说的是应用层的包 |
31
yisiyisi 2020-02-29 20:03:12 +08:00
有啥好笑的?
|
32
useben 2020-02-29 20:13:23 +08:00
粘包这个概念是针对应用层的,tcp 是流式传输,哪管你啥包啥包。粘包是说你的不同意义的业务数据因为流式传输而造成在同一次读取。。。
|
33
vevlins 2020-02-29 20:22:50 +08:00
就是楼主这种人让我在讨论这种问题时不得不(tcp 其实是面向流的,并没有粘包)。照你这么说,大学以下不少课程都是学了假知识。
|
34
DOLLOR 2020-02-29 20:27:13 +08:00 1
让我想起某物理讨论区因为“离心力”而大战几百楼的盛况。
|
35
luckyrayyy 2020-02-29 20:36:07 +08:00
笑
懂? |
36
LPeJuN6lLsS9 2020-02-29 20:47:38 +08:00
本粘包警察来了。粘包学家还有碰包学家,全都抓进监狱
|
37
boyhailong 2020-02-29 21:15:33 +08:00 1
人家说的是收到的数据解析成应用层可用的“包”
|
38
hardwork 2020-02-29 21:19:43 +08:00
物极必反,粘包警察都有了,lz 还在笑粘包
|
39
NullData 2020-02-29 21:28:42 +08:00 via Android 1
想说什么就好好说,在不合适的语境下阴阳怪气让我感到恶心
|
40
gamexg 2020-02-29 21:32:29 +08:00 2
看过协议的都知道 tcp 是流。
但是初学者或者代码 bug 造成错误的处理了包边界的现象需要个名称。 虽然不知道粘包是从哪里叫起的,但是这的确是个很常见的称呼。 碰到初学者类似问题求助,真没心情去详细解释,直接给个"粘包"关键字,对方可以容易的找到相关资料。 这个就足够了,不使用粘包,我真不知道什么关键字可以更准确的让对方找到解决办法。 |
41
sgissb1 2020-02-29 21:47:28 +08:00
参加之前上海某美国制裁黑名单企业面试时,小兄弟就用了这个词问我,我当时一脸黑人问号,这么含糊的意思,让我怎么回答么。再说我自己 tcp 的基础知识也确实不牢靠。。。
|
42
sgissb1 2020-02-29 21:47:57 +08:00
看来要上制裁黑名单也太容易了吧。
|
45
a627795061 2020-03-01 01:03:28 +08:00 via iPhone
纸上谈兵
|
46
back0893 2020-03-01 01:37:11 +08:00
我觉得说粘包很现象
出现粘包就是因为数据是不断获得 我依据规定的格式获得数据, 从而的得到我需要的包. |
47
Mutoo 2020-03-01 05:54:09 +08:00 via iPhone
这是应用层的包,纯熟翻译问题,如果翻译成帧(frame)就没问题了。从流里拆帧。
|
48
alphatoad 2020-03-01 06:01:02 +08:00
TCP 当然有包,不能因为封装好了就说没有
|
49
jangit 2020-03-01 08:38:49 +08:00 via iPhone
这个是有可能的,tcp 有个小特性叫 tcp delay,就是把小的数据包组在一起发送
之前知道粘包就是因为这个特性 |
51
PDX 2020-03-01 08:51:55 +08:00 1
这种 沾沾自喜 的样子 很低级
|
52
xuanbg 2020-03-01 09:46:55 +08:00
数据在网络层是分包发送和接收的。每个包都有固定的数据格式,这个数据格式就是我们说的网络协议。包的大小和 MTU 值有关,流数据超出 MTU 值,就会被自动分成多个数据包进行发送。在网络层确实不存在什么粘包这个说法,只有接收侧会有包的次序问题。因为网络设备不能保证你先发送的包一定会先到达。
说 TCP 是流没有包某种意义上也没错,对于上层应用来说,网络传输就是数据流。实际上我们在使用网络传输数据的时候,也是按流而不是包进行处理的。 |
53
nianyu 2020-03-01 10:29:53 +08:00
阴阳怪气
|
54
pythonee 2020-03-01 10:33:31 +08:00
还是不懂,而且工程上似乎是要考虑的
|
55
itskingname 2020-03-01 10:43:53 +08:00 via iPhone 1
@Mohanson #9 你这个回答毫无意义,就像是说:楼主你的帖子里面有一个错别字,你花一天时间翻一下新华字典就好了。 除了让人觉得你看过,还能说明什么?你能不能具体说明是卷几?哪一章哪一节第几页?
|
57
letking 2020-03-01 11:31:49 +08:00
这 tm 都是月经贴了吧?
不讨论楼主自身水平如何、做法对不对,反正如今还在维护“粘包”这个说法的都是傻 B,欢迎互 B。 |
58
wangyzj 2020-03-01 11:32:51 +08:00
明明知道这是针对不同职位和不通层级的说法
为啥一定要纠结呢 |
59
shujun 2020-03-01 12:13:39 +08:00 via iPhone
角度不同而已
在底层实现者眼里,传输不涉及应用逻辑 所以不负责处理这个封包解包 应用层开发者确实直接用 tcp 的不多,习惯了 http 那种封装好的协议 请求和相应一对一的。 |
60
FrankHB 2020-03-01 12:37:31 +08:00
想知道粘包警察是何种文化输出……樱警察 /车万警察 /厄介警察?
另外倒是想出警把 serialization/deserialization 或者 mashalling/unmarshalling 和 I/O 混为一谈的…… |
61
scnace 2020-03-01 12:48:21 +08:00 via Android
别特么再粘了!这不是误导初学者吗😂 (话说粘包这个词有原始出处吗?
|
62
rio 2020-03-01 13:07:07 +08:00
「 TCP 粘包」有与之互补的「 UDP 分段」版本了 https://www.v2ex.com/t/648023
|
63
asilin OP @rio 对,如果自底向上看,UDP 只不过是 IP+端口号 而已,本质上和 IP 数据包没有任何区别,没有附加任何功能。
|
64
GeruzoniAnsasu 2020-03-01 13:37:41 +08:00
@FrankHB 那首先得先给这两组词创造单独贴切的中文翻译
|
65
GeruzoniAnsasu 2020-03-01 13:47:31 +08:00 2
其实 “粘包” 这个现象很有意思
用文件 IO 来打比喻是不恰当的,因为你不可能遇到往文件里写了一篇文章然后往外读结果只读了个第一列函数就返回了导致文章狗屁不通的情况 大家都知道 TCP 是流的,但 write(&struct, sizeof(struct)) read(&strcut, sizeof(struct)) 可看不出来是流式的 |
66
sgissb1 2020-03-01 13:57:18 +08:00
@dndx 这个不需要,因为面试的时候小伙子很热衷于 leecode 的题,并且非常自信,而且面试中很多错误的术语和技术用词,也没有问任何工程实践问题。我都 8 年以上工作经验了,和其他家公司面试还真不一样,别家还会追加求证一下我的工程经验是否捏造 ^_^
|
67
rdZZZ 2020-03-01 14:17:40 +08:00
粘迟但到 :D
|
68
nicevar 2020-03-01 14:24:28 +08:00
老一辈程序员跟 TCP 打了十几二十年交道,突然看到这个新创造的词真的懵逼
|
69
jhdxr 2020-03-01 14:37:08 +08:00
@zsdroid 能够说出这个词的人,基本上都是在应用层( TCP/IP 四层模型,或者)处理数据,而不会是在 TCP 层(更准确的来说,传输层)的
|
70
FrankHB 2020-03-01 14:48:33 +08:00
@GeruzoniAnsasu
serialization 序列化 deserialization 反序列化 marshalling 列集 unmarshalling 散集 翻译不是问题,只是因为没多少人用所以才用原文。问题是原文意思就被普遍用错,而这个错误来自权威来源。 具体来说,I/O 是指系统内部和外部的交互。在一个系统内部交互的作用强行称为 I/O 会引发混乱。 这种误用的典型例子是,以抽象机语义指定行为的 ISO C++规定 I/O 操作(“库的 I/O 函数”)引发副作用——不管其实现是否改变了抽象机这个系统的状态。 这不科学。因为至少像 std::stringstream 这种( ISO C++所谓 Input/output library 里的)东西本来就不应该和系统外部交互,在这个定义下因为钦定副作用可能影响可观察行为而没法被优化成 no-op,即便实际可能什么都不用做。 再如,像格式转换之类的功能本来就该是 serialization/deserializaton,是应该能够证明不必要就该消除的东西。 真正的 I/O,是和设备交互的操作,操作的设备这种抽象机以外的接口。 这个意义下,C++的标准库 I/O 原则上设计就是错的,因为它违反了核心语言要求的抽象边界。让所谓标准 I/O 库函数比用户实现的 I/O 函数具有更加一等的地位也不可能是目的,何况在这里根本还没法直接优化。 这个问题从 C 就有,不过大概 stdio 隐藏了缓冲所以才没那么明显,以讹传讹惯了…… |
71
iEverX 2020-03-01 15:05:40 +08:00 1
可以说这种说法不准确,然而实际应用中,不可避免需要处理。
说 tcp 粘包,是因为 udp 没有这样的问题需要处理。因为使用 udp,必然会在应用层做应用协议包的拆分,收到 udp 包时,也得自己拼成一个应用协议包。不会有多个应用协议包在一个 udp 包发过来。 tcp 里,发送的时候,编码成字节流,收到的时候还是字节流。但是,对于程序员而言,字节流只是抽象,实际收到的仍然时一段一段的字节序列。这些字节序列不能直接用,必须解码成应用协议包。这里就的确会出现多个应用协议包在一段序列中的情况。decode 这个步骤,就是需要处理 byte[] 可能得各种情况。 这种情况下,称之为 tcp 粘包并不是问题,这个包本身指代就不是 tcp 的包。非得说 tcp 时字节流,所以没有包,过于死板。 |
72
dndx 2020-03-01 15:33:28 +08:00 via iPhone
@GeruzoniAnsasu 没看懂你这个比喻是想说明什么,TCP 的 socket API 默认就是阻塞的,你从里面读 x 字节系统在收到 x 字节之前也是不会返回的。这跟读文件流有什么区别?要不怎么 BSD socket 的 API 跟读写文件的都是兼容的呢,否则不如全部重新设计一套。
|
73
paoqi2048 2020-03-01 16:18:49 +08:00
你只看到了第二层,而你把他只想成了第一层,实际上,他是第五层
|
74
watzds 2020-03-01 16:22:49 +08:00 via Android 1
如果写书,字眼要扣好,特别严谨才行。平常讨论就抓住重点才是最重要的
|
75
rrfeng 2020-03-01 16:49:58 +08:00
所以楼主刚晋升为『粘包警察』,然后一下子出现了一堆『粘包监察』?
|
76
a852695 2020-03-01 18:58:27 +08:00
首先确定为概念本身是错误的;
然后讨论正确的是字节流; 仅此而已。 |
77
ajaxfunction 2020-03-01 19:06:42 +08:00
那我想问恩 鼠标上为什么没老鼠呢? 软盘为什么是硬的?
|
78
wangkai0351 2020-03-01 21:34:07 +08:00
@Mohanson 三本书一天看完, 我的天,仰望
|
79
yankebupt 2020-03-01 22:05:12 +08:00
@asilin 我一直覺得這是個僞命題,
天哪 直到我自己碰到了這個問題 這是一個 M11F 主板自帶的 AQC111C 5Gbps 網卡 win10 自帶驅動的 bug 當大數據量視頻串流的時候會立刻觸發這個問題 當使用某一**r 個協議轉 socks5 的中轉程序的時候不會觸發這個問題 使用另一個***ay 協議轉 socks5 的程序就會觸發這個問題 當觸發時網卡會 [直接罷工] 除非重新禁用 socks 是 tcp 的,外網協議也是 tcp 的。 唯一可能的可疑點估計就是用了流氓版 load balance,幾個出口 ip 不一樣,估計會有大量的 tcp retransmission 或報錯包夾雜在裏面。 [但也就是說,某一個純 tcp 的 pattern 會觸發這個網卡直接罷工的 bug。] 怎麼發現這個 bug 的呢?因爲把網卡 link 限定在 100M 物理層就不會觸發,1Gbps 就會... 換一塊 intel 的卡也不會觸發 查了一下,win10 自帶的 driver 版本之上,廠商發了兩個版本 patch,其中第一次的版本還沒完全補上這個 bug,第二個.18 版本才補上(我還不知道是不是完全補上了) 估計這就是你們說的黏包... |
80
yuikns 2020-03-01 22:21:49 +08:00
@FrankHB 我的语文有点拙计了。
想确认一下 serializing / deserializing 和 marshalling / unmarshalling 这两套行为上没有区别吧?貌似我成对用,但是好像没有读到区别。 io 操作那应该是 read/write 那一套了? |
81
dany813 2020-03-02 13:34:45 +08:00
围观大佬们的嘲讽
|
82
FrankHB 2020-03-03 15:48:27 +08:00 1
@yuikns 按我看到的一般的常见的用法,serializing / deserializing 指数据结构的内部表示和(至少)允许串行访问的外部存储的表示(尤其是直接对应外存中的可持久化格式,如文件映像)之间的转换; marshalling / unmarshalling 则强调系统内处理的以既定方式访问的数据(通常就是语言运行时支持的一等对象(first-class object) )和系统外的表示形式(不限制格式是否公开稳定,不强调持久化,具体支持和语言之间互操作的要求有关)的交互。两者外延不同但有交集,条件允许时可以共用一套实现。
划分 I/O 操作的基准要看什么是系统“内部”。I/O 是指保证对外部有不可忽略的影响的操作。否则,找不到一个外延去限定什么才能算 I/O,字面含义也说不通。 有些操作作为 I/O 是没疑问的:产生可观察且无法保证消除的行为(比如说,总是需要消耗电力)的物理设备发生的通信是真实的 I/O。 除此之外,还可以假装认为存在其它类似的影响外部状态操作,以实现不可控的“外部”系统交互。例如通用操作系统的内核把设备驱动排除出这里的“内部”,统计的所谓 I/O 可以包括仅在虚拟设备上有意义的操作;为便于设备驱动实现和统计方式的原因,这种假定姑且是合理的。 但就不提供让用户显式指定如何处理的计算作用(computational effect) 大多数编程语言来讲,没有说得过去的理由这样装。特别是 C 和 C++ 对这种“外部”副作用已经专门用 volatile 区分了,再含糊地给库函数开洞只会导致不必要的劣化和库 API 设计的混淆。 |