计算机网络 TCP 协议原理总结

2023年 7月 10日 90.5k 0

本文总结 TCP 协议的原理,包括:

  • TCP 概念和特点
  • 可靠传输的基础机制
  • TCP 滑动窗口机制
  • TCP 重传机制
  • TCP 流量控制机制
  • TCP 拥塞控制机制
  • TCP 连接建立和释放
  • TCP 协议头
  • 概念图谱总览

本文笔记目的,内容较多。 TCP 协议非常复杂, 读完本文需要许多耐心 。

网络是不可靠的 

计算机网络是不可靠的,存在 丢包、乱序、延时 。

这是众多 TCP 协议机制的设计出发点,万恶之源,将贯穿全文。

TCP 概念和特点 

TCP 协议全称 传输控制协议, 是一种 面向连接的、可靠的、面向字节流的 传输层通信协议。

TCP 在 TCP/IP 协议模型 中位于传输层。

计算机网络 - TCP 协议原理总结图 1.1 - TCP 位于 TCP/IP 分层中的传输层

TCP/IP 模型中各层数据包结构的关系如下图。

计算机网络 - TCP 协议原理总结图 1.2 - TCP/IP 模型中各层数据包结构的关系

TCP 协议的主要特点:

  • 面向连接,一对一通信。
  • 可靠交付:保序、不重复、不丢失。
  • 全双工通信:双方均可收发。
  • 和上层应用进程的交互方式是 面向字节流的。
  • TCP 和上层应用进程的交互方式:

  • 发送方可以发送不同大小的数据块到发送缓存。
  • 先会对发送缓存内的数据分段,打包成 TCP 数据包再发送。
  • 在接收一侧,数据包会先进入接收缓存区。
  • 当一定数量的数据包全部到达,重排、组装,以数据流方式吐给接收方。
  • 计算机网络 - TCP 协议原理总结图 1.3 - TCP 和应用程序的交互方式

    虽然 TCP 是面向字节流的,但是 TCP 所处理的数据单元却是面向报文段的, 也就是本文所说的数据包。

    将看到,短短 可靠交付 四字背后并不简单。

    可靠传输的基础机制 

    若不止考虑 TCP 协议本身的实现,如何设计可靠的网络通信?

    首先,由于 丢包的可能性,要实现可靠通信:

  • 发送方要知道对方接收成功,因此需要接收方回复确认 即 ACK。
  • 如果丢包发生,发送方需要重传。
  • 一种触发重传的方式是,超时重传 (也有其他触发方法,见后续 TCP 重传机制 )。

    此外,无论往返中哪个包丢失或延迟, 发送方认为对方没有收到,就会重传 。

    计算机网络 - TCP 协议原理总结图 2.1 - 确认和超时重传机制的几种情况

    网络延时发生时,重传可能会导致重复:

  • 接收方会丢弃收到的重复数据包,但是仍然回复确认。
  • 发送方会丢弃收到的重复确认包。
  • 其次,对于如何发送确认包和重传包,有两种方式。

    一问一答的方式

    也叫做 停止并等待 ARQ 协议, 是指 发送方等到接收方的确认包后,再发下一个数据包 。

    类似乒乓方式,具体来说:

  • 如果时限内收到对方确认,才发送下一个数据包。
  • 否则,重传当前数据包。
  • 计算机网络 - TCP 协议原理总结图 2.2 - 一问一答的方式

    可以看到这种方式下,发送方大部分时间在等待,效率非常低。

    流水线传输的方式

    叫做 连续 ARQ 协议 , 是指 发送方会连续发送一组数据包,同时等待这些数据包的确认 。

    具体来说:

  • 发送方发送一批数据包。
  • 同步地接收对方的确认包。
  • 简单来说, 发送方不闲着,一边发送,一边等回复 。

    计算机网络 - TCP 协议原理总结图 2.3 - 流水线传输的方式

    可以看到这种方式相对一问一答的方式,效率要高。

    如果发生丢包或延时,需要重传,有两种方式:

  • 回退 N 重传

    发送方每发送一个数据包,都会发起一个定时器。

    一旦一个某个定时器触发,就会重传。

    发送指针回退到未拿到确认的数据包处,以实现重传。

    计算机网络 - TCP 协议原理总结图 2.4 - 回退 N 重传的方式

    可以看到,此方法下, 会重传后面所有的数据包。

  • 选择重传

    同样,每发送一个数据包,都会发起一个定时器。

    不同点仅在于, 只重传未拿到确认的数据包,不回退发送指针。

    计算机网络 - TCP 协议原理总结图 2.5 - 选择重传的方式

  • 综合以上,得出的结论是,不考虑 TCP 协议的具体实现的话, 要实现可靠的网络通信,需要依赖确认和重传机制, 并且一个好的办法是采用流水线传输的方式。

    而流水线传输方式,正是下一个部分 TCP 协议中的 滑动窗口机制 的引子。

    TCP 滑动窗口机制 

    滑动窗口机制是 TCP 协议的精髓所在,它是 TCP 协议设计的基本框架 。

    滑动窗口机制就是 流水线传输方式 在 TCP 协议中的细化设计, 发送方一边连续地发送数据包,一边等待接收方的确认。

    滑动窗口分为两种:发送窗口 和 接收窗口。

    由于 TCP 是全双工的, 所以通信的每一端都会同时维护两种窗口 。

    数据包序号

    在 可靠传输的基础机制 中, 接收的数据包可能是重复的、乱序的,因此 TCP 会对每一个数据包进行唯一标号, 叫做数据包的序号。

    数据包的序号是 TCP 协议头 中的一个 32 比特大小的整数字段。

    每次发送一个包,这个序号就会增加一。

    TCP 是全双工的,两个通信端各自维护自己的序号。

    因为 网络延时不可控, 如果两次连接建立时差很短、或者连接重建后老连接的数据包延迟到达, 会造成序号冲突。

    所以,序号并非由固定数字初始化。可以综合时间、随机数来生成等。

    确认号和累计确认

    在 TCP 中,一个用以确认的回复包,会有确认号。

    如果一个数据包同时也是一个确认包,那么它也会有确认号。

    一个序号为 SEQ 的数据包,其确认包的确认号会是 SEQ+1 。

    同样,确认号也是 TCP 协议头 中的一个 32 比特大小的整数字段。

    可以理解为,接收方已收到序号为 SEQ 的数据包,期待发送方下一次给 SEQ+1 的包。

    更广义的理解是, 确认号是接收方期望对方发送的下一个包的序号 。

    下图中,发送方连续发送一组包,如果中间有丢包, 接收方则期待序号最小的丢失的包。当重传成功后, 接收方仍然期待下一个未拿到的数据包:

    计算机网络 - TCP 协议原理总结图 3.1 - 接收方的确认号

    接收方所期待的是序号最小的没拿到的数据包 。

    这种确认号的机制,即可实现累计确认机制:

    接收方确认了标号为 SEQ 的数据包, 即代表确认了所有小于 SEQ 的数据包, 此时接收方给的确认号是 SEQ+1 。

    计算机网络 - TCP 协议原理总结图 3.2 - 累计确认机制

    累计确认其实是一种批量确认的机制,以减少确认包的数量。

    此外,如果接收方恰好需要发送数据,确认号可以直接标在数据包上,即捎带确认。

    一个问题是,如何控制累计确认的时机?

    TCP 协议中的 Nagle 算法 给出的办法是 延迟确认。

    其大概的原理是,未确认的包达到一定量、或者达到一个时间阈值,才回复一次确认。

    所以说, 在默认的 TCP 协议中,确认不是立即回复的,而是延迟的 。

    不过,接收方的延迟确认不应该过分延迟,否则会造成发送方的重传,浪费网络资源。

    可以设置 TCP_NODELAY 选项 来禁用 Nagle 算法。

    TCP 的累计确认机制,是累计确认和延迟确认两个策略的综合。

    此外,TCP 协议还有另外一种确认机制,叫做 选择确认机制 ,将会后面讲到。

    发送机制

    已经讲过,TCP 协议默认的 Nagle 算法 采用了延迟确认的方法。

    对应的,发送的时机如何确定?

    办法是类似的,大概是,未发送的包达到一定量、或者达到一个时间阈值,才发送一次。

    计算机网络 - TCP 协议原理总结图 3.2.1 - Nagle 算法的发送机制

    TCP 的发送机制,是累计发送和延迟发送两个策略的综合。

    同样可以设置 TCP_NODELAY 选项 来关闭延迟发送的行为。

    发送窗口

    发送窗口的示意图如下,当收到对方的累计确认后,则向右滑动。

    计算机网络 - TCP 协议原理总结图 3.3 - 发送窗口

    注意的是 窗口大小 是有限的(稍后将讨论它的受限情况), 发送方只能发送窗口内的数据包 。

    接收窗口

    接收窗口的示意图如下,当回复对方确认后,则向右滑动。

    计算机网络 - TCP 协议原理总结图 3.4 - 发送窗口

    仍需注意 窗口大小 是有限的(稍后将讨论它的受限情况), 接收方只能接收窗口内的数据包 。

    由于 网络数据包是乱序的 , 所以接收后的数据包会按照序号重新排序,才可以交付给应用程序。

    窗口大小

    窗口的大小是受限的,也是动态的 。

    窗口大小是 TCP 协议头 中的一个 16 比特大小的整数字段。

    首先,显然 接收窗口受限于接收缓存区的大小, 发送窗口受限于发送缓存区的大小 。

    其次指出, 发送窗口大小也受限于接收窗口大小 。

    这其实比较容易理解,发送方是生产者,接收方是消费者, 如果生产速度快而消费速度慢,则会导致接收方来不及接收。

    计算机网络 - TCP 协议原理总结图 3.5 - 生产者和消费者

    所以,发送方只能发送窗口内的数据包 。

    其实,这就是 流量控制机制 : 由接收方控制的、调节发送方生产速度的机制 , 其具体的实现方式,就是在回复时设置 TCP 协议头 中的窗口大小字段。

    窗口的大小也是动态变化的, 因为两端接收和发送能力是动态变化的 :

  • 接收能力的变化导致窗口大小的变化,即后面所讲的 TCP 流量控制机制。
  • 发送能力的变化导致窗口大小的变化,即后面所讲的 TCP 拥塞控制机制。
  • 可以看到,滑动窗口的大小是一个贯穿式的重要概念。

    但是,如果窗口要缩小,窗口的前沿是不可以向前收缩的。

    窗口收缩的方法则是,慢慢地,随着已发送的数据包得到确认, 保持窗口前沿不动,前移窗口后沿。

    计算机网络 - TCP 协议原理总结图 3.6 - 窗口前沿不可以收缩

    因此, 发送窗口也并不总和接收窗口一样大 。

    窗口大小的初始化,是在 连接建立 过程中两端协商确定的, 在后续的传输阶段,它会因主动或被动的原因而动态变化。

    选择确认机制

    和前面所讲的 累计确认机制 一样, 都是 TCP 协议中用来控制如何回复确认包的机制。

    因为网络丢包、延时、乱序的不确定性,序号大的包可能先到达。

    如果仅采用累计确认机制,发送方并不知道大号的包传输成功了, 它只会执行 回退 N 重传 , 将后面的所有包重传。

    下图中,如果接收方的回复包丢失或延迟、 或发送方的速率较快,就会导致短时间内多次无效重传。 接收方则不得不丢弃重复数据。 造成信道资源浪费。

    计算机网络 - TCP 协议原理总结图 3.7 - 累计确认机制的无必要重传

    选择确认机制,简记作 SACK,是指 接收方在回复确认包 ACK 的同时, 告诉对方已收到数据包的序号区间 。

    比如,在回复对方 ACK=10 的时候,同时回复 SACK=20,39 , 这表示接收方已经收到 20~39序号的数据包。 发送方只需要重传 10~19 就可以了。

    下图中同样的场景,可以看到开启选择确认机制, 可以一定程度上优化无意义的重传。

    计算机网络 - TCP 协议原理总结图 3.8 - 选择确认机制

    如果接收方采用了选择确认机制, 那么发送方就可以采用前面所讲的 选择重传 。

    所以,选择确认机制是搭配选择重传机制一块使用的。

    这个机制是可选的,需要两端都支持,在 TCP 连接建立 阶段, 两端协商确定是否采用此机制。

    TCP 重传机制 

    重传机制是 TCP 协议中比较复杂的部分。

    其触发方式有两种:超时重传 和 快速重传。

    其具体实施方式又有两种,前面所讲的 回退 N 重传 和 选择重传 。

    超时重传和超时计算

    前面 可靠传输的基础机制 有讲到超时重传, TCP 协议每发送一个数据包,就对这个数据包设置一次计时器 ,超时即触发重传。

    超时重传中的一个问题是:超时的时间是多少呢?

    这是 TCP 协议中又一个复杂的问题。

  • 如果超时重传时间设置太短,会引起不必要的重传。
  • 如果设置太长,会使得网络空闲时间增大,降低传输效率。
  • TCP 采用一种自适应算法,具体地,是一种加权平均的方法。

    首先,记录每个数据包发出去后到收到确认包的时间差,叫做往返时间 RTT 。

    计算机网络 - TCP 协议原理总结图 4.1 - 往返时间的方差可能会很大

    具体的,假设加权平均后的数据叫做 TT ,其计算方式:

    Tn=(1−α)×Tn−1+α×RTTnTn=(1−α)×Tn−1+α×RTTn

    其中 0≤ααα 越大,新数据的贡献越大,TT 跟随 RTTRTT 的就越快。 反之,αα 越小,新数据的贡献越小,TT 跟随 RTTRTT 的就越慢。

    按这个计算式迭代下去,历史数据的权重会越来越小,TT 会跟随实时数据的趋势。

    同时,由于综合了历史数据,又可以消除毛刺。

    快速重传机制

    TCP 的另一种重传机制,是快速重传机制,它不再以超时作为触发标准, 而是观察确认包的情况。

    具体来说, 如果收到同一个数据包的多次确认,立即发起重传 。

    一般取 3 次作为阈值,常叫做 3ACK 方法。

    快速重传机制的作用在于,将有可能在超时触发之前,提前发起重传。

    计算机网络 - TCP 协议原理总结图 4.2 - 收到同一个数据包的 3 次确认即发起重传

    细节地, 因为每发送一个数据包,就会对这个包设置一次定时器, 所以,快速重传触发后不会造成超时重传的重新触发。

    TCP 流量控制机制 

    前面 窗口大小 部分已经提及, 如果数据发送过快,接收方就会来不及接收。

    流量控制,就是接收方调控对方的发送速度不要太快的机制 。

    发送窗口的大小受限于接收窗口 , 接收方在回复时通过设置 TCP 协议头 中的窗口大小字段, 来限制发送方的发送窗口大小。

    需要注意的是, 流量控制过程是动态的 。

    其原因在于,两端的发送和接收数据的能力是动态变化的 。

    以接收方为例, 哪怕我们假设缓存区的大小未发生变化,上层应用程序读取数据数据的能力也是动态变化的。

    比如,接收方应用程序因为负载升高,无法及时读取缓存区数据, 导致已回复数据包大量积压,从而挤压接收窗口的大小。

    计算机网络 - TCP 协议原理总结图 5.1 - 接收窗口缩小的示例

    零窗口死锁问题

    一个特殊的场景是,如果接收方的窗口变为 0 ,发送方的窗口也会被限制到 0 。

    此时,发送方窗口是 0 ,无法发送新数据包。而接收方在等待数据包到来, 也不会回复任何确认包,即使接收方的窗口已经可以变大,也没办法告知对方。

    这样,两端都在等待,造成零窗口死锁问题。

    计算机网络 - TCP 协议原理总结图 5.2 - 零窗口死锁问题

    在 TCP 协议中,打破死锁的方法,是采用 持续计时器机制。

    具体来说,发送方收到零窗口通知时,启动计时器, 当计时器超时,就发送一个 仅携带一个字节数据的探测数据包 。

    如此,接收方就可以回复确认包,如果此时接收方的窗口可以恢复, 双方就回到正常轨道。

    计算机网络 - TCP 协议原理总结图 5.3 - 持续计时器解决零窗口死锁问题

    一个细节是,在发送探测数据包时,持续定时器会重新开始计时。 也就是说, 发送方会周期性地进行窗口探测,这样可以应对探测数据包丢失的情况 。

    如果接收方拿到探测数据包后, 仍坚持零窗口, 那么发送方的计时器就重新计时。 如此往复一定次数后仍无法恢复,则关闭连接。

    糊涂窗口综合征

    糊涂窗口综合征 的通俗理解是, 双方处理速度不一致,会导致通信演化为小包通信的问题 。

    如果接收方的处理速度跟不上发送方的生产速度, 最终接收方的数据积压在缓存区来不及拿走,接收窗口宣告为 0 , 此时发送方停止发送。

    一旦接收方的应用进程处理了一个字节,接收窗口变为 1 , 发送方就可以发送一个字节。

    如果发送方总是足够快地填充接收方的窗口, 这个过程会不断循环下去,导致每次通信的数据包都只有一个字节, 显然降低了通信效率。

    计算机网络 - TCP 协议原理总结图 5.4 - 糊涂窗口综合征

    此问题的解决措施有多种,其中包括 Nagle 算法的 累计与延迟确认 和 累计与延迟发送 。

  • 接收方 过一定时间 或 累计一定数量的包 后再确认。
  • 发送方 过一定时间 或 累计一定数量的包 后再发送。
  • 此外,还有一种关键的措施,就是 延迟宣告窗口 。

    大概意思是,接收方的窗口稍稍变大时,不要急于告知对方, 而是达到一定阈值才告知对方,如此发送方就会等接收方窗口足够大时将数据包一并发送, 从而解决小包传输问题。

    或者是,接收方的窗口变的足够小的时候,直接宣告窗口关闭,阻止发送方再发数据。 直到接收窗口可以变的足够大,再恢复大包传输。

    下图是延迟宣告窗口的一种推演过程。

    计算机网络 - TCP 协议原理总结图 5.5 - 延迟宣告窗口 - 解决糊涂窗口综合征

    可以看出, 窗口探测实际是探测一个足够大的窗口,而不是完全非零的窗口 。

    TCP 拥塞控制机制 

    网络拥塞是指,对网络中某种资源的总需求量大于总可用量的情况。

    直接的理解就是, 要传输的数据量超过网络负荷 。

    在网络拥塞的情况下,TCP 协议的重传机制反而会加剧拥塞情况。

    虽然网络拥塞是一个全局的、宏观的问题, 但是 TCP 协议对网络拥塞的缓解措施,是从个体角度出发的。

    简而言之,TCP 协议的拥塞控制的办法是, 发送方主动减少发送量 。

    拥塞控制和 流量控制 是不同的事情:

    • 拥塞控制是发送方主动减少数据传输,解决的宏观网络的超负荷问题的机制。
    • 流量控制是接收方调控发送方数据传输量,以平衡生产消费速度的机制。
    网络拥塞的判断

    拥塞判断的方法有许多种,这里主要说明两种。

    这两种就是 TCP 协议中重传机制的两种触发方式 。

  • 超时标准

    前面 超时重传和超时计算 部分有讲, TCP 协议每发送一个数据包,就会对它设置一次计时器。

    当超时发生时,意味着数据包丢失、或延迟,此时网络可能发生拥塞。

  • 3ACK 标准

    前面 快速重传机制 部分有讲, 如果收到同一个数据包的多次确认,意味着数据包丢失、或延迟, 此时网络可能发生拥塞。

  • 拥塞窗口算法

    TCP 协议仍然基于滑动窗口的机制,进行拥塞控制。

    发送方会维护一个拥塞窗口 CWND ,此窗口范围内的数据才允许被发送。

    因此实际的发送窗口的大小是:Min(对方接收窗口大小,自身拥塞窗口大小) 。

    拥塞窗口算法就是如何调整拥塞窗口大小的办法。

    它主要有四个策略:慢开始、拥塞避免、快重传、快恢复。

    虽然有四个策略,但是它们其实是 一套算法。

    计算机网络 - TCP 协议原理总结图 6.1 - 拥塞控制算法图示 - 图片来自 计算机网络(谢希仁第七版)-方老师

    首先,「快重传」就是前面所讲的 快速重传 , 也就是说, 如果遭遇 3ACK ,就立即发起重传 。

    慢开始

    发送方每收到一个对新数据包(就是不包括重传包)的确认, 就让拥塞窗口增一。

    虽然叫做慢开始,但是其实并不慢, 拥塞窗口是倍增的 。

    简而言之,如果没有遭遇超时 或 3ACK, 拥塞窗口就扩大一倍。

    计算机网络 - TCP 协议原理总结图 6.2 - 拥塞控制 - 慢开始策略

    拥塞避免

    慢开始的增长速度是非常快的, 发送量越大,就离拥塞越近。

    所以有一个门限值 ssthresh 的概念。

    拥塞窗口倍增到门限值后,就改为线性增长,比如每次增大一 。

    其后,线性增大拥塞窗口的过程,就是拥塞避免阶段。

    慢开始和拥塞避免,都是在探测拥塞边缘 : 一开始疯狂试探,到达门限值后,慢慢试探。

    拥塞控制的惩罚

    如果出现 拥塞的征兆,拥塞窗口的大小会受到惩罚,让它减小。

  • 超时条件

    超时未确认,意味着发生丢包或者较大延迟,所以处罚更为严厉。

    拥塞窗口打回初始值 1 , 门限值改为当前拥塞窗口大小的一半。

    总而言之,从新进入慢启动 。

  • 3ACK 条件

    收到 3 个相同的确认包,意味着发生少许延迟,所以处罚相对柔和。

    同样,慢启动的门限值减半。

    不同的是,此时不必要从新进入慢启动, 而是减少一些拥塞窗口的值,跳过慢启动,直接重新进入拥塞避免阶段。

    减少到多少呢?减少到最新门限值大小,也就是从新进入拥塞避免。

    这种直接跳过慢启动,直接进入拥塞避免的方式,就叫做 快恢复 。

  • 拥塞控制算法总览

    计算机网络 - TCP 协议原理总结图 6.3 - 拥塞控制算法总览 - 图片来自 计算机网络(谢希仁第七版)-方老师

    总结如下:

  • 慢开始阶段,倍增到门限值,激进探测拥塞边缘。
  • 到达门限值,进入拥塞避免,线性增加,小心探测拥塞边缘。
  • 遭遇超时,惩罚严厉,从新进入慢开始。
  • 遭遇 3ACK,惩罚柔和,快恢复,门限减半,直接进入拥塞避免。

    同时,3ACK 时,立即重传,即 快重传。

  • TCP 连接建立和释放 

    TCP 的三次握手和四次握手,已经是老生常谈了。

    连接建立 - 三次握手

    首先,TCP 连接建立过程要解决的问题:

  • 确定双方都可以正常收发 TCP 数据包。
  • 双方协商一些参数和可选项(例如前面所讲的 选择确认 、窗口大小 等), 尤其是交换初始序号。
  • 对资源进行分配,比如缓存区、端口号。
  • 下面是 TCP 协议三次握手建立连接的过程示意图,其中

  • SYN(synchronous) 是同步标志位 ,表示开始传输数据的意思。
  • ACK(acknowledgement) 是确认标志位,表示是否启用确认机制。
  • ack(小写)是前面所说的 确认号。
  • seq 是前面所说的 序号 。
  • 计算机网络 - TCP 协议原理总结图 7.1 - TCP 协议三次握手过程

    具体过程的描述如下:

  • 客户端发送一个 TCP 数据包,主动发起连接。

    设置 SYN=1 标志位,表示发起连接,想要传输数据。

    客户端初始化自己的数据包序号 seq=x ,一同发送给对方。

  • 服务端收到对方的连接请求,回复对方确认包。

    设置回复标志位 ACK=1 ,表示确认。

    根据 确认号的规则, 同时设 ack=x+1 表示期待对方下一次发过来的数据包序号为 x+1

    因为 TCP 是全双工的,服务端也会设置 SYN=1 , 并初始化自己的数据包序号 seq=y ,捎带回复给客户端。

    此时,服务端可以确定: 客户端可以发送 TCP 数据包,自身可以接收 TCP 数据包 。

  • 客户端收到对方的确认包,并回复对方确认。

    同样,设置回复标志位 ACK=1,表示确认。

    对方的序号是 y , 回复对方 ack=y+1 表示期待对方下一次发来的数据包序号为 y+1 。

    同时,自身的数据包序号需要自增 seq=x+1 。

    自己发送上一个数据包,对方收到了,并且收到了对方回复。

    所以客户端可以确定: 服务端可以正常收发 TCP 数据包, 自身也可以正常收发 TCP 数据包 。

  • 服务端收到客户端的确认,三次握手结束。

    此时服务端可以进一步确定:

    客户端端可以正常接收 TCP 数据包, 自身也可以正常发送 TCP 数据包 。

  • 三次握手结束后,双方都可以确定对方是可以正常收发 TCP 数据的。

    计算机网络 - TCP 协议原理总结图 7.2 - 三次握手过程中双方逐步确定收发能力正常

    三次握手的必要性

    一个经常出现的问题是: 两次握手可以吗?

    当然是不可以的,可以分几个方面解释:

  • 三次握手是为了确定双方的收发能力

    如果缺少最后一次 ACK 的话,服务端就无法知道客户端是否可以接收 TCP 数据, 也无法知道自己的发送是否成功,就无法做到可靠传输。

  • 序号同步的确定性

    如果缺少最后一次 ACK 的话,服务端就无法确定对方有无收到自己的初始序号。

    前面讲到,数据包的序号是 TCP 滑动窗口机制的基本字段,它如果是不可靠的, 后续的确认机制、重传机制等都无从谈起。

    网络是不可靠的,存在丢包,所以连接建立时, 双方都必须确定初始序号交到了对方手中。

    三次握手过程,也是可靠地交换初始序号的过程 。

    计算机网络 - TCP 协议原理总结图 7.3 - 三次握手也是可靠地交换初始序号的过程

  • 历史失效连接请求的乱序问题

    网络中,数据传输可能存在延迟、乱序。

    如果一个老的失效的连接请求,延迟到达服务端,倘若缺少第三次握手, 服务端会建立一条新连接,直接进入 ESTABLISHED 状态。

    客户端收到服务端的回复包时,可以根据序号是否过期判断是否是失效连接。

    在三次握手的情况下,客户端就进一步可以发送 RST 标志,终止连接。

    如果不是历史失效连接,客户端则发送 ACK 标志,正常建立连接。

  • 三次握手中丢包的情况

    另一个常见的问题是: 如果第三次 ACK 丢包呢?

    无论哪一次通信发生丢包或延迟,都出发 TCP 的 重传机制 。

    简单说,再发一次,如果达到一定次数,则放弃连接。

    对于第三次 ACK 丢包的情况,重试一定次数后,服务端会关闭连接,回收资源。

    不过,服务端单方面关闭连接,客户端并不知晓,它认为连接已经建立。

    如果客户端向服务端发送数据,会被服务端打回 RST 报文, 借此客户端可知道连接失效。

    计算机网络 - TCP 协议原理总结图 7.3.1 - 第三次 ACK 包丢失的最坏情况

    数据传输的开始时机

    上面的 三次握手图示 中,一个细节是:

    • 客户端在两次握手之后就进入了 ESTABLISHED 状态。
    • 服务端则是在三次握手之后才进入 ESTABLISHED 状态。

    其原因在于, 在 第二次握手 后, 客户端已经可以确定双方都可以正常收发数据包 。

    所以,客户端可以提前进入 ESTABLISHED 状态,分配端口,开始通信。

    因此, 第三次 ACK 是可以捎带客户端的数据一并发送的 。

    服务端直到 第三次握手 成功之后,才可以确定双方都正常, 所以它会稍晚进入 ESTABLISHED

    连接关闭 - 四次挥手

    TCP 的连接释放过程,需要注意两个点:

  • TCP 是全双工的。
  • 双方都可以主动释放连接。
  • 释放连接的过程有一个重要的目的:

    双方都要确定地知道对方不再发送数据了,连接才正式关闭 。

    假设客户端主动关闭,下面是 TCP 协议四次挥手释放连接的过程示意图。

    其中 FIN(finish)是结束数据传输的标志位。

    计算机网络 - TCP 协议原理总结图 7.4 - TCP 协议四次挥手过程

    具体过程的描述如下:

  • 客户端发送 FIN 数据包,不再发送数据。

    设置 FIN=1 标志位,表示想要关闭连接,不再发送数据。

    但是,此时服务端还可以继续发送数据 。

  • 服务端收到 FIN 数据包,回复确认 。

    服务端对于 FIN 包也会进行确认。

    此时服务端明确知道,客户端不再发送数据了 。

  • 服务端传输完剩余数据 。

    由于 TCP 是全双工的, 连接关闭由客户端主动发起,并不意味着服务端的数据已传输完。

    服务端需要把剩余数据传输完毕。

    此时服务端知道,双方都不再发送数据了。 。

  • 服务端发送 FIN 数据包,不再发送数据包 。

    同样,设置 FIN=1 标志位,表示服务端不再发送数据。

  • 客户端收到对方 FIN 包后,回复确认 。

    任何一方结束数据发送,都要确保对方是知道的,就需要对方确认。

    此时客户端知道,双方都不再发送数据了。

  • 客户端等待 2MSL 时间等待后,四次挥手结束,释放所有资源
  • 再次明确, FIN 标志位的意思是,不再发送数据 。

    挥手四次的必要性

    双方都必须对 FIN 包做确认, 这样对方才可以确定地知道自己不再发送数据。

    TCP 连接的建立需要三次握手,是因为, 第二次握手过程服务端将设置 SYN 和 ACK 合并为一个数据包发送,所以是三次 。

    而对于释放连接的情况, 无法将第二次和第三次合并, 因为中间还要传输数据, 所以是四次。

    如果任何一个确认包发生丢包或延迟,主动设置 FIN 的一方会触发重传。

    等待 2MSL 时间的原因

    首先,MSL 是指报文在网络中最大生存时间。

    2MSL 就是两倍的 MSL ,也被叫做 TIME_WAIT 时间, 在 linux 中常被设置为 2*60s 。

    2MSL 的值一定需要大于重传超时阈值 。

    为何要等待这么长时间呢?

  • 如果最后一次 ACK 丢包呢?

    前面有说过,任何一方的 FIN 包丢失后,如果超时未收到对方确认, 就会触发重传。

    计算机网络 - TCP 协议原理总结图 7.5 - 最后一次 ACK 丢失,会导致服务端重传 FIN 包

    如果客户端在一定时间内未收到服务端的 FIN 包重传,说明对方已经收到 ACK 。

    否则,如果收到服务端的 FIN 包重传,自然回复 ACK

    一次 ACK 包的发送时间,再算上重传 FIN 的时间,所以叫做 2MSL 。

    不过,无论如何,2MSL 时间至少要比重传超时阈值长。

  • 防止新老连接数据包混乱

    如果立即释放端口资源,此端口可能被一个新连接立即使用。

    如此的话,假设网络中存在老数据包的延迟,那么老数据包会被新连接接收,造成混乱。

    所以, 等待一定时间,使得网络中潜在的、可能延迟传输的数据包悉数殆尽, 才不会影响到后面的连接。

  • 综合来看,还是因为网络是不可靠的。

    TCP 连接的有限状态机

    综合连接建立和连接关闭的过程,整个 TCP 连接的状态机图示如下。

    计算机网络 - TCP 协议原理总结图 8.1 - TCP 有限状态机 - 图片来自 网络

    两个常见的问题:

  • TIME_WAIT 非常多的情况

    已经知道,TIME_WAIT 是主动关闭连接一方,发送完最后一次 ACK 数据包后进入的状态。

    并且 TIME_WAIT 的时间默认是 120s 。

    TIME_WAIT 非常多,说明 连接建立的并发数比较大,端口来不及回收。

    比如,对于高并发的 TCP 短连接,例如 未开启 keep-alive 选项 的 HTTP 短连接。

    补充,HTTP 协议中是服务端主动关闭连接 。

  • CLOSE_WAIT 非常多的情况

    已经知道,CLOSE_WAIT 是被动关闭连接的一方,传输最后的剩余数据的状态。

    它是当服务端收到对方 FIN 包时,主动进入的一个状态。

    所以,一般地,是由于应用程序未调用或太忙未来得及调用 close 关闭连接导致。

    比如程序编写遗忘连接关闭调用代码,或者负载过高,未及时执行关闭连接, 导致 CLOSE_WAIT 状态的 socket 积压。

  • TCP 协议头 

    TCP 的协议头部总共有 20 个字节。

    下面是 TCP 协议头的栏位示意图,其中大部分字段已经在上面讲到。

    计算机网络 - TCP 协议原理总结图 9.1 - TCP 协议头 - 图片来自 网络

    概念图谱总览 

    最后,对本文的知识做一个树状图梳理。

    计算机网络 - TCP 协议原理总结图 10.1 - TCP 总结图谱 - 图片来自 网络

    结语 

    纵观全文,TCP 协议的众多机制源于,万恶之源:网络是不可靠的,丢包、乱序、延时。

    相关文章

    如何在 Linux 中使用 logname 命令?
    为什么有 HTTPS?HTTPS 如何实现安全通信?
    HTTPS的TSL握手流程是什么
    华为无线网络射频调优及WLAN跨VLAN的三层漫游示例
    502错误是什么、应该怎么排查?
    HTTP3为什么抛弃了经典的TCP,而选择QUIC

    发布评论