V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
V2EX 提问指南
narutow
V2EX  ›  问与答

怎么理解 TCP 粘包与拆包?

  •  
  •   narutow · 2021-01-23 18:40:22 +08:00 · 11112 次点击
    这是一个创建于 1398 天前的主题,其中的信息可能已经有所发展或是发生改变。

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    如果不是性能或者效率原因,建议用 WebSocket,这是正儿八经的消息协议,可以省掉定界的麻烦。
    Jirajine
        21
    Jirajine  
       2021-01-23 21:15:18 +08:00   ❤️ 1
    @l00t #19 是根本就不存在这种所谓的“现象”,你读写文件、打 log 的时候怎么没遇到过“粘包”?
    说到底还是没把 tcp 当流看待。
    opengps
        22
    opengps  
       2021-01-23 21:24:54 +08:00
    说话说太快了,没带标点,就是粘包
    cs419
        23
    cs419  
       2021-01-23 21:31:41 +08:00
    粘包是 tcp 编程 的雾里看花
    你想了解 tcp 编程 却没实际经验的 产生了错误的认知

    建议通过一个实际场景去学习
    尝试写个远程调用 或者跨语言调用 的例子
    写出来 你就不会再想粘包的概念了
    seanxx
        24
    seanxx  
       2021-01-23 21:41:21 +08:00   ❤️ 1
    满贴嘲讽,鄙夷.....
    太臭了这氛围
    reedthink
        25
    reedthink  
       2021-01-23 21:49:08 +08:00
    TCP 详解没看完吧,附一句原文:“两个应用程序通过 TCP 连接交换 8bit 字节构成的字节流。”
    ps:创造粘包概念的人,似乎对 TCP 有点误解
    Immortal
        26
    Immortal  
       2021-01-23 21:50:04 +08:00   ❤️ 1
    @seanxx #24
    这贴反而是我见过认真科普数最多的粘包贴
    yolee599
        27
    yolee599  
       2021-01-23 21:56:50 +08:00 via Android
    对,我就是粘包警察! TCP 不存在粘包说法! TCP 不存在粘包说法! TCP 不存在粘包说法!
    nicebird
        28
    nicebird  
       2021-01-23 22:11:35 +08:00
    主要是某些人把粘包当成一个问题,然后写文章解决。实际上粘包只是基于 tcp 设计发消息的表现,只要正确理解 tcp 是流式的就明白了一定会有所谓的粘包现象。
    当然实践上也能写出一些不拆包直接收,直接强转就能用的情况。那是因为消息发的太慢、包太小,刚好 tcp 放在一个 ip 包里发了出去,收到后刚好是一个完整的包罢了。
    Jaron0608
        29
    Jaron0608  
       2021-01-23 22:28:23 +08:00   ❤️ 7
    TCP 是一个面向字节流的协议,它的任务就是在提供流量控制、拥塞控制等特性的前提下,将 Payload 中的上层协议数据完成可靠的传输。

    它并不关心 Payload 中的数据格式,这里所说的“包”的包实际上是上层应用层协议的 Header+body,并不是 TCP 数据包。

    定义应用层协议时,一般需要满足下面三种需求之一:

    - 固定协议包长度(可拓展性低)
    - 指定协议包终结符,例如 redis 的 RECP 协议
    - 指定一个 Length 字段,表明协议包长度,例如 HTTP 协议

    根据这三个需求,在处理 TCP 流时找到应用层协议包的分界,就能拆解出一个个单独的应用层包。
    Cbdy
        30
    Cbdy  
       2021-01-23 22:31:12 +08:00
    粘包警察不请自来
    Meltdown
        31
    Meltdown  
       2021-01-23 22:40:27 +08:00 via Android
    本来就是这样啊,如果有往文件里读写结构化数据和知道 tcp 读写时读到或写入的长度不一定是指定的长度就自然而然考虑到这个问题了。这个粘包都快成专业名词了,我看 brpc 的文档里都这么说了。。。
    xuanbg
        32
    xuanbg  
       2021-01-23 23:00:10 +08:00
    TCP 设计出来就不是直接给应用程序用的。应用程序应该使用应用层协议来使用 TCP,譬如 http 。
    chenqh
        33
    chenqh  
       2021-01-23 23:07:58 +08:00
    TCP 是流,但是应该算是车流吧,不是水流
    chenqh
        34
    chenqh  
       2021-01-23 23:11:34 +08:00
    @Jaron0608 我到觉得 HTTP 主要是一个链接只发一个请求,导致粘包情况比较少
    love
        35
    love  
       2021-01-23 23:23:05 +08:00
    我滴妈这都 2021 年了,怎么还在说这么无聊的问题,TCP 是字节流哪来的粘包拆包,你们能不能发个代码片段让我见识下
    1109599636
        36
    1109599636  
       2021-01-23 23:29:11 +08:00
    @Jaron0608 这个回答很不错,大部分回复毫无帮助甚至营养可言,即使是 v2 讨论起问题脾气也是大,网络环境不乐观啊。。。
    micean
        37
    micean  
       2021-01-23 23:35:19 +08:00
    因为数据到缓冲区,发不发是网卡驱动决定的,可能马上发,也可能等一会发,缓冲区满了就分几次发
    lizytalk
        38
    lizytalk  
       2021-01-23 23:36:56 +08:00 via iPhone
    粘包本来就是伪问题。想用一个包一个包的模式就自己搞一个应用层协议,或者用现成的啊。tcp 本来就是流传输的协议,非要让人家传输包,tcp 也很无奈啊
    micean
        39
    micean  
       2021-01-23 23:47:56 +08:00
    比方说,缓冲区大小是 1024,你每秒发个 10byte 的帧,对方哪需要处理什么所谓粘包拆包的问题,但是你要发个 10kb 的帧,那这一帧肯定是拆开发送的啊
    neoblackcap
        40
    neoblackcap  
       2021-01-23 23:57:44 +08:00
    原理就不讲了,我说说历史吧
    socket 这套东西呢,其实本质上是历史物理过程的模拟。TCP 就是模拟电话而出现的,UDP 就是模拟电报。
    我们当然可以说拍了“一个电报”给别人,而且说明一件事情的时候,我们往往可能要用到“多个电报”。
    然而当用电话的时候则不同,我们也可以说打了“一个电话”给别人,然而往往说明一件事情的时候,我们用“一个电话”就可以搞定了。
    为啥打电话的时候不会出现“粘包”而 socket 编程又会出现“粘包”呢?当大家回忆自己是怎么打电话的时候,大概就明白了为啥不“粘包”了
    VersionD
        41
    VersionD  
       2021-01-24 00:06:01 +08:00
    @opengps hh,很精髓,比起粘包警察的解释好多了
    germain
        42
    germain  
       2021-01-24 00:20:34 +08:00
    用私有协议+编码+缓冲
    aaronlam
        43
    aaronlam  
       2021-01-24 00:31:07 +08:00 via iPhone
    b00tyhunt3r
        44
    b00tyhunt3r  
       2021-01-24 00:45:05 +08:00
    @carlclone 讲两句呗
    oisadfo
        45
    oisadfo  
       2021-01-24 01:14:12 +08:00
    居然没有人来给粘包下个定义,都是各说各的
    no1xsyzy
        46
    no1xsyzy  
       2021-01-24 02:17:56 +08:00
    @lewis89 话说也有 SCTP 这种保序又分包的,但没什么人用的样子
    后来的要么干脆 WebSocket 了
    no1xsyzy
        47
    no1xsyzy  
       2021-01-24 02:30:16 +08:00
    @seanxx @1109599636 是什么让你们有了技术工作者和爱好者会友善的错觉?
    从新闻组时代开始就会花式嘲讽了好么:
    https://github.com/ryanhanwu/How-To-Ask-Questions-The-Smart-Way/blob/master/README-zh_CN.md
    (原作者是 ESR, “开源” 一词的创造者)
    iszengmh
        48
    iszengmh  
       2021-01-24 03:50:12 +08:00 via Android
    其实你们只是想说粘包和拆包
    iszengmh
        49
    iszengmh  
       2021-01-24 03:50:42 +08:00 via Android
    其实你们只是想说 TCP
    yayoi
        50
    yayoi  
       2021-01-24 03:52:04 +08:00 via Android
    websocket,http 和 tcp,stcp 不是一层的东西吧,所谓粘包是因为直接用了 tcp 却不实现应用层协议造成的问题,直接原因是不知道 tcp 的工作方式,根本原因是不了解通信的七层模型
    iszengmh
        51
    iszengmh  
       2021-01-24 04:02:49 +08:00 via Android
    其实你们只是想说 TCP 根本没有拆包和粘包,它们是应用层做的事情,应用层把数据拆成一段一段(拆包),在传输层利用 TCP 协议一段一段发送,接收数据的应用层又把一段一段数据组合起来成为一个完整数据(粘包),整个过程在应用看起来就只是发送了一次请求而已。
    mengzhuo
        52
    mengzhuo  
       2021-01-24 04:50:48 +08:00 via iPhone   ❤️ 1
    不解释了

    我原来是粘包警察

    现在是面试别人的时候,故意挖坑,谁掉进去就直接不通过( ̄∇ ̄)
    littlecreek
        53
    littlecreek  
       2021-01-24 05:04:11 +08:00
    请教"粘包与拆包" 这俩对应的英文名词是什么? 我去学习一下.
    ryd994
        54
    ryd994  
       2021-01-24 06:02:23 +08:00 via Android
    因为这帮人还不知道 sctp,既提供可靠性,也提供字节边界。
    如果怕中间的设备不支持的话,还有基于 TCP 或 UDP 的用户态 sctp 库。

    如果只是为了传输数据,谁没事手撸 TCP 啊,protobuf 不香吗?
    lewis89
        55
    lewis89  
       2021-01-24 06:20:55 +08:00
    @no1xsyzy #46 关键是 99%的人 IP 以上的东西 就只听说过 TCP UDP,而且大部分路由器也只针对这两个做了 NAT 优化,搞不好你用其它的(我不是很了解 STCP ),就不兼容了... 好多路由器连 ICMP 都是默认防火墙拦了,而且从私有网到广域网上 层层 NAT,raw IP 协议根本行不通..
    crclz
        56
    crclz  
       2021-01-24 09:02:01 +08:00
    TCP 是面向字节流的协议。TCP 只负责传输字节序列。至于为什么 API (接收和发送)是以 byte[]为单位,只是因为批量处理数据的效率原因。TCP 代码里面并不对你发送的 byte[]做出分界,假设你要传输某 buffer[0:200],那么①传输 buffer[0:50]、buffer[50:200]和②buffer[0:100]、buffer[100:200],TCP 认为①②并没有区别。

    这个字节流可以理解为 C#中的 Stream 或者 IEnumerable<byte>这两个编程接口。

    TCP 的粘包是因为,本身就没有包的概念。你取到的字节数组有概率和传输的字节数组的“窗口”相同,给了你“包”的错觉,只是因为运气好罢了(从 IP 层的缓冲取走数据及时,并且中间的链路畅通)。
    Orenoid
        57
    Orenoid  
       2021-01-24 09:49:32 +08:00
    本质上就是在讨论如何在 TCP 流式数据里拆分出应用层的数据包,说到底就是个叫法问题,“粘包”还是“拆包”,但麻烦请用后者,错的就是错的。
    也许你是能理解问题的本质,但你能保证你在传播“粘包”这个概念的时候,也保证听众不会错误理解吗?这个词传得越广,就越有可能导致很多新手以为 TCP 传输过程就是按照一个一个包去发送的。
    nl101531
        58
    nl101531  
       2021-01-24 10:12:02 +08:00
    从一次协议请求来看,TCP 会将应用层数据分段后,转交给 ip 成,ip 成再次分片,当网卡收到数据后,ip 成会合并数据,然后转交给 TCP 成,TCP 是直接将这个段给到应用层,问题是这个段由于连接复用可能会包含多个业务请求含义。

    个人认为,粘包说法很形象,没必要揪着流式协议这一点不放。
    love
        59
    love  
       2021-01-24 10:40:34 +08:00   ❤️ 1
    @nl101531 你说的是啥你理解吗,什么叫由于连接复用可能会包含多个业务请求含义?字节流由于效率因素才在接口层面允许一次读写多个字节本质上还是一个字节一个字节发送读取数据的字节流,一个字节流有什么粘包?这词还不是误解 TCP 而来的?
    Mutoo
        60
    Mutoo  
       2021-01-24 11:44:39 +08:00 via iPhone
    @littlecreek TCP message framing
    jj783850915
        61
    jj783850915  
       2021-01-24 11:51:33 +08:00 via Android
    日经贴 隔一段时间就要来一次
    tairan2006
        62
    tairan2006  
       2021-01-24 13:33:28 +08:00
    算了,还是拉黑楼主吧,站内搜索都不会用。
    no1xsyzy
        63
    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
        64
    l00t  
       2021-01-24 13:59:20 +08:00
    @Jirajine #21 谁跟你说读写文件打 log 就不会“粘包”? 包是上层的包,怎样算一个包,是上层界定的。你想一句一句地处理,结果一次读到了两句,这就是“粘包”,你得自己再拆开来。
    Jirajine
        65
    Jirajine  
       2021-01-24 14:14:26 +08:00 via Android
    @l00t 只要你把它看做流,就像水龙头下接水,控制一次接多少是自然而然的。
    但你要不把它看作流,水是装在一个个大小不一的杯子里过来的,你才需要额外考虑“拆包”、“并包”,把大杯拆成小杯小杯合成大杯。
    而这种认知是错误的,数据在一个个包里传输属于 TCP 的内部细节,TCP 对上层的表现就是无分段的流。
    nightwitch
        66
    nightwitch  
       2021-01-24 14:28:08 +08:00   ❤️ 3
    一些二流的程序员因为对网络协议掌握的一团浆糊, 在编程实践中不能理解明明我 send 了 5 次为什么我 recv 一次就收到了所有的数据,进而将这样的现象归咎于 TCP 的实现,从而总结为"粘包",并在互联网上传播一些让人啼笑皆非的概念.
    Y29tL2gwd2Fy
        67
    Y29tL2gwd2Fy  
       2021-01-24 14:32:52 +08:00 via Android
    网线上刷层黄油就不会沾了
    l00t
        68
    l00t  
       2021-01-24 14:51:23 +08:00   ❤️ 1
    @Jirajine #65 当你说到 “数据在一个个包里传输属于 TCP 的内部细节”, 就说明你对“粘包”这个名词的认知是错误的。两个地方的“包”都不是同一个概念。应用层的包,TCP 根本就不认识,有个毛的细节。

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

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

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

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


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

    搞不清楚的话,建议重新系统学习 计算机网络 课程。
    no1xsyzy
        78
    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
        79
    wzhy  
       2021-01-24 21:30:59 +08:00
    抽刀断水水更流^^
    eastphoton
        80
    eastphoton  
       2021-01-24 21:57:03 +08:00
    @no1xsyzy 就知道要有这么回的,我已经标了“应用层又想用包”。。
    没有这个前提的话,应用层当然随你说了算。
    exiledkingcc
        81
    exiledkingcc  
       2021-01-24 22:10:58 +08:00
    @micean 你这是典型的错误认识。
    liuminghao233
        82
    liuminghao233  
       2021-01-24 22:37:00 +08:00 via iPhone
    我倒是认为 TCP 流的 feature 是不必要的
    大多数 tcp 应用都在应用层做 tlv
    协议栈完全可以禁止段分割与合并
    对确认机制进行修改
    让 TCP 变成是 Datagram
    这样的话应用层就不用关心所谓的粘包问题了
    no1xsyzy
        83
    no1xsyzy  
       2021-01-24 22:52:33 +08:00
    @eastphoton 补充啦补充
    真要说 “流拆成包” 这个组合挺诡异的,但似乎找不到好词来区分
    parse: bytes->struct
    split: bytes->bytes[]
    framing: stream[byte]->stream[bytes]
    三种情况(实际上第三个词英文也挺诡异的,不知道有无更好说法)
    结果上来说,这边简直是一堆不够严谨的动词和名词的大杂烩。
    muzuiget
        84
    muzuiget  
       2021-01-24 22:54:40 +08:00
    月经问题,已经回答无数次了。
    no1xsyzy
        85
    no1xsyzy  
       2021-01-24 23:03:55 +08:00
    @liuminghao233 这点上来说,一来有 SCTP
    二来,其实 TCP 和 UDP 分别是两种 tradeoff 。这是 feature,但是是 tradeoff feature,不是因为做成这样好,而是因为做成这样能让其他地方更好。
    允许段分割和合并还有一个效果,比如你每个报文比 IP 包大小还大一个字节,某一秒发 N 个报文(比如 20<N<30 ),如果你不允许段分割与合并,将导致你发出 2N 个 IP 包,其中一半只用于发一个字节。允许分割合并就只用发 N+1 个 IP 包。现在不怎么在乎,当时可能会很吃紧。
    liuminghao233
        86
    liuminghao233  
       2021-01-24 23:31:01 +08:00 via iPhone
    @no1xsyzy
    我还没用过 sctp 不知道能不能过 nat
    除了 udp tcp 其他的不好说

    小包问题我想知道你说的情况有实际应用吗
    我觉得对于需要 tlv 编码的应用来讲
    用流语义好像并没有带来收益
    no1xsyzy
        87
    no1xsyzy  
       2021-01-25 00:33:22 +08:00
    @liuminghao233 形态上与 TCP 一致,支持应当是可以的,也有在 TCP / UDP 上再做一层的……

    包大小变换问题其实会发生在 MTU 改变、以及数据通过隧道导致内外包限不同的情况
    前提条件是 TLV 的话确实流没什么好处,流是为了避免这一层作太多强制。
    算是历史原因。
    如果大家都用 SCTP 或者 SCTP 比 TCP 、UDP 更早被设计出来的话也没 TLV 这茬了。
    neoblackcap
        88
    neoblackcap  
       2021-01-25 00:48:30 +08:00
    @no1xsyzy SCTP 据我了解,也就是运营商在自己内部的一些网里面用。广义的网络,各种设备是针对 TCP 以及 UDP 来设计的,设备里面是有 ASIC 去处理 TCP/UDP,SCTP 没有这些硬件加成,速度立马下降。
    这也是为啥 QUIC 会设计在 UDP 之上,而不是另起炉灶。
    littlecreek
        89
    littlecreek  
       2021-01-25 04:32:16 +08:00
    @Mutoo 谢谢
    lewis89
        90
    lewis89  
       2021-01-25 06:34:56 +08:00
    @neoblackcap #88 那就没毛病了,说白了 就是 IPv4 时代,大部分路由器跟硬件平台 只针对 UDP 跟 TCP 设计了,实际上也就是 UDP 跟 TCP 成为事实标准了
    lewis89
        91
    lewis89  
       2021-01-25 06:36:49 +08:00
    @neoblackcap #88 一般家用应该都是依赖硬件 NAT 转发,可能真没有为 SCTP 设计过
    lewis89
        92
    lewis89  
       2021-01-25 06:40:36 +08:00
    其实楼主只要想清楚一个问题就行了,http 是依赖 TCP 设计的,你看 http 报文头里面有一个 content-length 实际上就是用来干 沾包 黏包 拆包 这个事情,因为对于 http 服务器来讲,真正的报文协议是一个 http 头 + http body, 但是流式传输 如果没有截断流的机制 就会变成 http 头 + http body + 下一个 http 头的一部分 ,这样解析 tcp 流就会报错,所以 content-length 就是用来干这个事情的。
    no1xsyzy
        93
    no1xsyzy  
       2021-01-25 10:55:14 +08:00
    @neoblackcap ASIC 是盲点
    确实,消息传递还是 WebSocket 吧
    或许哪个平行世界有 SCTP 是事实标准,也就没有粘包问题和粘包警察了(能瞎争的东西多了去了,没什么意义
    julyclyde
        94
    julyclyde  
       2021-01-25 12:28:39 +08:00
    这基本上是个信仰问题
    你要是信仰流,那么看啥都像流流
    如果信仰包,看啥都像包,而且还有粘包的问题
    byaiu
        95
    byaiu  
       2021-01-25 12:30:53 +08:00 via iPhone
    @soulzz 一个 fd 只能关联一个 socket 吧
    xuboying
        96
    xuboying  
       2021-01-25 12:51:13 +08:00
    打个买东西的比方,你网上买了 20 个东西,去快递点取的时候(按顺序)可能拿到 1 件可能拿到 5 件可能拿到 10 件,件数的事情你不能控制,快递也没有义务确定一次送到的件数
    TCP 是好一点的快递,只保证不丢件,或者至少丢了会负责再补发
    UDP 么丢了就丢了。
    auto8888
        97
    auto8888  
       2021-01-25 14:20:19 +08:00
    TCP 不就是一把收了 5 个货 需要自己分一下吗。。。居然还有警察的吗
    julyclyde
        98
    julyclyde  
       2021-01-26 10:47:56 +08:00
    @auto8888 问题在于并不见得一次五个,也可能是一次收到 0.35+1+1+1+1+0.87 这样
    auto8888
        99
    auto8888  
       2021-01-26 13:37:09 +08:00
    @julyclyde 确实,TCP 只保证报文的先后顺序
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   1766 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 36ms · UTC 16:39 · PVG 00:39 · LAX 08:39 · JFK 11:39
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.