计算机网络速通(三) TCP 的封包格式:为什么需要粘包和拆包?

本文最后更新于:2024年9月30日 下午

从稳定性的角度深挖 TCP 协议的运作机制

  • 如今大半个互联网是建立在 TCP 协议之上,比如使用的 HTTP协议、消息队列、存储、缓存都需要用到 TCP 协议。这是因为 TCP 协议提供了可靠性,简单来说可靠性就是让数据无损送达。但是考虑到成本,就会变得非常的复杂,因为还需要尽可能的提升吞吐量,降低延迟,减少丢包率。
  • TCP 协议就有很强的实用性,可靠性又是 TCP 协议最核心的能力,具体来说,从一个终端有序地发出多个数据包,经过一个复杂的网络环境到达目的地的时候,经常会变得无序,而可靠性要求数据又恢复到原始的顺序,在这里就涉及两个问题:
    • TCP 协议是如何恢复数据的顺序的?
    • 拆包和粘包的作用是什么?

TCP 协议是如何恢复数据的顺序的?

TCP 协议是一个传输层协议,TCP 发送数据的时候,往往不是一次将数据一次性发送送达的,而是将数据拆分成很多个部分,再逐个发送,同样的在目的地,TCP 协议又需要逐个地接收数据。 network1

TCP 协议为什么不一次发送完所有数据?

比如:需要传一个大小为 10MB 的数据,对于应用层而言,就是一次性传送完成的,而传输层协议为什么不将这个文件一次性发送过去呢?原因如下: 1. 为了稳定性,一次发送的数据越多,出错的概率越大; 2. 为了效率,拆分数据包就能更好地利用这些并行的路径; 3. 发送和接受数据的时候,都存在着缓冲区;(缓冲区是在内存中开辟的一个区域,目的是缓冲,因为大量的应用频繁地通过网卡收发数据,这个时候网卡只能一个个处理应用的请求,当网卡忙不过来的时候,数据就需要排队,也就是要讲数据放入缓冲区,如果每个应用都随意的发送很大量的数据,可能就会导致其他应用的实时性遭到破坏) network1 4. 还有一些原因:在操作系统中,内存的最小分配是页表,如果数据的大小超过一个页表,可能会存在页面置换问题,造成性能损失。 network1 总之,在传输层封包不能太大,这种限制往往以缓冲区大小为单位,也就是说: - TCP 协议:会将数据拆分成不超过缓冲区大小的一个个部分; - 每个部分有一个独特的名词,叫作 TCP 段(TCP Segment) 在接受数据的时候,一个个 TCP 段又被重组成原来的数据,像这种数据经过拆分,然后传输,然后在目的地重组,我们俗称拆包: - 拆包:将数据拆分成多个 TCP 段传输; 那么粘宝是什么?有时候发往一个目的地的多个数据都太小了,为了防止多次发送占用资源,TCP 协议有可能将他们合并成一个 TCP 段发送,再在目的地还原成多个数据: - 粘包:将多个数据合并成一个 TCP 段发送。 下图是一个 TCP 段的格式: network1 我们可以看到 TCP 的很多配置选项和数据粘在了一起,作为一个 TCP 段,显然,让你把每一个部分都记住是不太现实的,因此我们只关注最主要的部分: - TCP 协议就是依靠每一个 TCP 段工作的,所有我们每认识一个 TCP 的能力几乎都会在 TCP 段找到与之对应的字段。 首先: 1. 源端口(Source Port)/发送端口(Destination Port)描述的是发送端口号和目标端口号,代表发送数据的应用程序和接受数据的应用程序;(在 TCP 协议中使用端口号来描述发送和接受的应用程序,比如 80 端口往往代表 HTTP 服务,22 端口往往代表 SSH 服务); network1

  1. Sequence Number 和 Achnowledgment Number 是保证可靠性的连个关键; network1

  2. Data Offset 是一个偏移量,这个量存在的原因如下:

    • TCP Header(整个头部的长度) 部分的长度是可变的,需要一个数值来描述数据从哪个字节开始; network1
  3. Reserved 是很多协议设计会保留的一个区域,用于日后扩展能力; network1

  4. URG/ACK/PSH/RST/SYN/FIN 是几个标志位,用于描述 TCP 段的行为,也就是 TCP 封包到底是做什么用的?

    • URG 代表这个 TCP 段是一个紧急数据:比如远程操作的时候,用户其实已经按了 ctrl+c 了,代表用户想终止当前的程序,像这种紧急的需求需要加急处理,这时候 URG 会置 1;
    • ACK 代表响应;
    • PSH 代表数据推送,传输数据:代表当前 TCP 段是在传输数据;
    • SYN 代表请求同步,申请握手;
    • FIN 代表终止请求,挥手。 特别说明:这五个标志位每一个只占了 1bit,可以混合使用,比如握手的 ACK-SYN同时为 1,代表同步请求和响应。这也是 TCP 协议三次握手,四次挥手的原因。 network1
  5. Window 也是 TCP 保证稳定性并进行流量控制的工具;(下一节介绍) network1

  6. Checksum 是校验和,用于校验 TCP 段有没有损坏; network1

  7. Urgent Pointer 指向最后一个紧急数据的序号号(Sequence Number):紧急指针存在的原因是有时候紧急数据它不是一个 TCP 段,就好比说有一个紧急数据,它分成了很多个段来传输,这时候需要提前告诉接受方究竟有多少个紧急数据。 network1

  8. Options 中存储了一些可选字段 (比如:最大分段大小 MSS(Maxiumun Segment Size)) network1

  9. Padding 存在的意义是因为 Options 的长度不固定,需要 Padding进行补齐; network1

Sequence Number 和 Achnowledgment Number

在 TCP 协议的设计当中,数据是被拆分成多个部分的,而每个部分都增加了协议头,将它们合并称之为一个 TCP 段,进行传输,这个过程称之为拆包。这些 TCP 段经过复杂的网络结构,由底层的 IP 协议,负责传输到目的地,然后再进行重组。 因此需要思考一个问题:稳定性要求的是数据无损的传输(拆包获得数据,又需要恢复成原来的样子),而在复杂的网络环境当中,即便所有的 TCP 段都是顺序发出的,也不能保证它们是顺序到达的,因此发出的每一个 TCP 段都需要有序号--Sequence Number(seq)。 如下图所示:发送数据的时候,为每一个 TCP 段分配一个自增的 Sequence Number,接受数据的时候,可以通过 seq 为乱序的 TCP 段进行排序 network1

但是如果是这样的一种设计又会产生一种新的问题:接收方回复发送方,也去要 seq,而网络的两个终端,去同步一个自增的序号是非常困难的,因为任何两个网络主体之间,时间不能做到完全的同步,又没有公共的存储空间,无法共享数据,更别说实现一个分布式自动序号。 这个问题的本质就好像两个人在说话,我们要确保他们说出去的话和回答之间的顺序。因为 TCP 协议是一个双工的协议,两边都可能会同时说话,所以如果想确定一句话的顺序,那么是需要两个值去描述的,也就是发送的字节数和接受的字节数。

以下是 seq 的设计: network1

重新定义 seq 如上图所示,对于任何一个接收方,如果知道了发送者发送某个 TCP 段时,已经发送了多少字节的数据,那么就可以确定发送者发送数据的顺序。 但是这里有一个问题:如果接收方也向发送者发送了数据请求,接收方就不知道发送者发送的数据到底对应哪一条自己发送的数据? 举个例子: network1 A 和 B 对话,我们可以确定他们彼此之间接受数据的顺序,但是无法确定数据之间的关系,所以只有 seq 是不够的,比如说 A 说今天天气好吗,A 又说今天你开心吗,B 说开心,B 又说天气不好,对于人类来说去理解这几句话的顺序是非常容易的,但是对于机器就需要特别的标注,因此还需要另一组数据:每个 TCP 段发送时,发送方已经接受了多少数据(Achnowledgment Number)。

下图中,终端发送了三条数据,并且接受了四条数据,通过观察,根据接收到的数据 seq 和 ACK,将发送和接受数据可以进行排序。

例如:发送方发送了 100 字节的数据,而接收到的两个封包(seq = 0 和 seq = 100),都是针对发送方(seq = 0)这个封包的。

发送 100 个字节,所以接收到的 ACK 刚好是 100,这说明(seq = 0 和 seq = 100)这两个封包其实都是针对接收到第 100 字节数据后发送回来的,这样我们就确定了整体的顺序。 network1

注意:无论是 seq 还是 ACK ,都是针对对方而言的。是对方发送数据和对方接受数据。

我们在实际工作当中可以通过 Whireshark 调试工具观察两个 TCP 连接的 seq 和 ACK。 network1

MSS(Maxiumun Segment Size)

  • 这也是面试会经常闻到的 TCP Header中的可选项(Options)
  • 可选项控制 TCP 段的大小,它是一个协商字段(Negotiate)

协议是双方都要遵循的标准,配置不能由单方决定,需要双方协商。 - TCP 段的大小(MSS)涉及发送、接受缓冲区的大小设置 - 双方实际发送接受封包的大小,对拆包和粘包的过程有指导作用

因此需要双方去协商,如果这个字段设置的非常大,会带来一些影响: 1. 对方可能会拒绝:对方是服务的提供方,它可能就不愿意接受太大的 TCP 段,因为太大的 TCP 段会降低整体的性能,比如说内存的使用性能; network1 2. 资源的占用:用户占用服务器太多的资源,意味着其他用户就需要等待或者降低他们的服务质量; 3. 支持 TCP 协议工作的 IP 协议,工作效率会下降;TCP 协议不可拆包,那么 IP 协议就需要拆大量的包,那么 IP 协议为什么需要拆包呢? - 在网络当中,每次能够传输的数据不能太大,受限于具体网络传输设备(物理特性); - 但是对于 IP 协议拆分太多的封包并没有意义,因为可能会导致属于同个 TCP 段的封包被用不同的网络线路传输,加大延迟; - 同时,拆包需要消耗硬件和计算资源; 4. 是不是 MSS 越小越好? - MSS 太小的情况下,会浪费传输资源(降低吞吐量)。因为数据被拆分之后,每一份数据都要加一个头部,如果 MSS 越小,那么头部的占比就会上升,这样吞吐量就会成为一个灾难,所以在具体使用的过程当中 MSS 的配置往往都是一个折中的方案。

总结

  • TCP 协议是如何恢复数据的顺序的,TCP拆包和粘包的作用是什么?
    • TCP 拆包的作用:将任务拆分处理,降低整体任务出错的概率,以及减小底层网络处理的压力,粘包过程需要保证数据经过网络的传输,又能恢复到原始的顺序,这中间需要数学提供保证顺序的理论依据。
    • TCP 利用(发送字节数、接受字节数)的唯一性来确定封包之间的顺序关系。

计算机网络速通(三) TCP 的封包格式:为什么需要粘包和拆包?
https://zzmes.github.io/2024/09/30/network3/
作者
YangYangYang
发布于
2024年9月30日
许可协议