【TCP/IP】TCP详解笔记

时间:2022-03-28 08:11:57
目录

前言

本笔记记录 TCP/IP 中的 TCP 理论。包括三次握手、四次挥手、状态变迁、慢启动、快重传等等。

《TCP/IP详解》一共三卷,其中卷二、卷三更多偏重于编程细节,而卷一更多偏重于基础原理。

后面再发布个支持处理多线程并发及客户端数量限制的TCP服务端+TCP客户端例程。

原文:https://www.cnblogs.com/lizhuming/p/14916605.html

17. TCP 传输控制协议

17.1 引言

17.2 TCP 服务

TCP提供一种面向连接的、可靠的字节流服务。

TCP通过下列方式来提供可靠性:

  • 应用数据被分割成TCP认为最适合发送的数据块。由 TCP传递给IP的信息单位称为报文段或段(segment)。
  • 当TCP发出一个段后,它启动一个定时器,等待目的端确认收到这个报文段。如果不能及时收到一个确认,将重发这个报文段。
  • 当TCP收到发自TCP连接另一端的数据,它将发送一个确认。这个确认不是立即发送,通常将推迟几分之一秒。
  • TCP将保持它首部和数据的检验和。这是一个端到端的检验和,目的是检测数据在传输过程中的任何变化。如果收到段的检验和有差错, TCP将丢弃这个报文段和不确认收到此报文段(希望发端超时并重发)。
  • TCP报文段作为 IP 数据报来传输,而IP数据报的到达可能会失序,因此TCP报文段

    的到达也可能会失序。如果必要,TCP将对收到的数据进行重新排序,将收到的数据以

    正确的顺序交给应用层。
  • IP数据报会发生重复,TCP的接收端必须丢弃重复的数据。
  • TCP还能提供流量控制。TCP连接的每一方都有固定大小的缓冲空间。 TCP的接收端只允许另一端发送 接收端缓冲区 所能接纳的数据。(窗口)

字节流服务(byte stream service)

  • 两个应用程序通过TCP连接交换8 bit字节构成的字节流。TCP不在字节流中插入记录标识符。

17.3 TCP的首部

视图

  • 【TCP/IP】TCP详解笔记

  • 端口:每个TCP段都包含源端和目的端的端口号,用于寻找发端和收端应用进程。这两个值加上IP首部中的源端IP地址和目的端IP地址唯一确定一个TCP连接。

    • socket:包含客户 IP 地址、客户端口号、服务器 IP 地址和服务器端口号的四元组。
  • 序号:用于对字节流进行编号。

    • 例如序号为 301,表示第一个字节的编号为 301,如果携带的数据长度为 100 字节,那么下一个报文段的序号应为 401。
  • 确认号:期望收到的下一个报文段的序号。

    • 例如 B 正确收到 A 发送来的一个报文段,序号为 501,携带的数据长度为 200 字节,因此 B 期望下一个报文段的序号为 701,B 发送给 A的确认报文段中确认号就为 701。
  • 数据偏移:指的是数据部分距离报文段起始处的偏移量,实际上指的是首部的长度。

  • URG:紧急(The urgent pointer) 标志置位。只有当 URG 标志置1时紧急指针才有效。

  • ACK:确认标志。当 ACK=1 时确认号字段有效,否则无效。

    • TCP 规定,在连接建立后所有传送的报文段都必须把 ACK 置 1。
  • PSH:推标志。该标志置位时,接收端不将该数据进行队列处理,而是尽可能快将数据转由应用处理。

    • 在处理 telnet 或 rlogin 等交互模式的连接时,该标志总是置位的。
  • RST:复位标志。复位标志有效,重建连接。

  • SYN:同步标志。同步序列编号(Synchronize Sequence Numbers)栏有效。

    • 在连接建立时用来同步序号。当 SYN=1,ACK=0 时表示这是一个连接请求报文段。若对方同意建立连接,则响应报文中 SYN=1,ACK=1。
  • FIN:结束标志。用来释放一个连接,当 FIN=1 时,表示此报文段的发送方的数据已发送完毕,并要求释放运输连接。

  • 窗口:窗口值作为接收方让发送方设置其发送窗口的依据。流量控制。

  • 校验和:检验和覆盖了整个的TCP报文段:TCP首部和TCP数据。这是一个强制性的字段,一定是由发端计算和存储,并由收端进行验证。

  • 紧急指针:紧急指针是一个正的偏移量,和序号字段中的值相加表示紧急数据最后一个字节的序号。

    • TCP的紧急方式是发送端向另一端发送紧急数据的一种方式。
    • 只有当 URG 标志置1时紧急指针才有效。
  • 选项:长度可变,最长可达40字节。当没有使用“选项”时,TCP的首部长度是20字节。最后的填充字段仅仅是为了使整个TCP首部长度是4字节的整数倍。

18. TCP连接的建立与终止

18.1 引言

18.2 连接的建立与终止

18.2.1 建立连接

三次握手建立连接

  • 参考图:

    • 【TCP/IP】TCP详解笔记
  • 建立连接的过程是由客户端发起,服务端等待客户请求

  • 第一步:客户端向服务器端发送一个SYN报文段(只有首部,且SYN被置 1),初始序号(ISN)随机选择,假设为num_a,ACK 置 0。(客户端进入SYN_SEND状态)

  • 第二步:服务器端收到 SYN报文段,便知道客户端需要请求握手,从 SYN报文段 中提取对应的信息,为该 TCP 连接分配 TCP 缓存和变量,并向该客户 TCP 发送允许连接的报文段(握手应答报文)。这个报文段只有首部,包含3个重要的信息:(建立客户端-->服务端的连接)(服务器进入SYN_RECV状态)

    1. SYN与ACK标志位1
    2. 将TCP报文段首部的确认序号字段设置为 num_a+1(这个num_a(ISN)是从握手请求报文中得到)。
    3. 服务器随机选择自己的初始序号(ISN,注意此ISN是服务器端的ISN,假设为num_b),并将其放置到TCP报文段首部的序号字段中。
  • 第三步:客户端接收到服务器端的握手应答后,会将 SYN 置 0,ACK 置 1,确认序号置为 num_b+1, 设置窗口值,可以添加数据域的报文段发给服务器端。同时给该TCP连接分配缓存和变量。(建立服务端-->客户端的连接)(客户端和服务器端都进入ESTABLISHED状态)

18.2.2 终止连接

四次挥手终止连接

  • 参考图

    • 【TCP/IP】TCP详解笔记
  • 第一步主机A发出 FIN报文段(首部FIN被置 1),序号假设为 num_c,ACK 被置 1,但是确认序号是无效的。(主机A进入FIN_WAIT_1状态)(主机B进入CLOSE_WAIT状态)

  • 第二步:当主机B收到 FIN报文段,它返回一个 ACK报文段(终止连接应答),确认序号为 num_c+1。此时断开客户端-->服务器端的方向。(主机A进入FIN_WAIT_2状态)

  • 第三步主机B发出 FIN报文段主机A请求终止连接,此时序号为 num_d,ACK 被置 1,但是确认序号是无效的。(主机B进入LAST_ACK状态)

  • 第四步:当主机A收到FIN报文段,它返回一个ACK报文段(终止连接应答),确认序号为 num_d+1。此时断开服务器端-->客户端的连接。(主机A进入TIME_WAIT状态)(主机B进入CLOSE状态)

18.2.3 TCP 连接状态变迁图

视图:

  • 【TCP/IP】TCP详解笔记

18.3 连接建立超时

18.4 最大报文段长度

最大报文段长度(MSS)表示TCP传往另一端的最大块数据的长度。当一个连接建立时,连接的双方都要通告各自的 MSS。

我们已经见过MSS都是1024。这导致 IP 数据报通常是4 0字节长:2 0字节的TCP首部和2 0字节的 IP 首部。

使用IEEE 802.3的封装,它的MSS可达 1452 字节。

18.5 TCP 半关闭

TCP 是全双工的。主要断开一线的连接,剩下一线还可以正常工作。如A只能发,B只能收。

为了使用这个特性,编程接口必须为应用程序提供一种方式来说明:

  • 我已经完成了数据传送,因此发送一个文件结束(FIN)给另一端,但我还想接收另一端发来的数据,直到它给我发来文件结束(FIN)。

18.6 TCP 状态变迁图

视图:

  • 【TCP/IP】TCP详解笔记

18.6.1 2MSL 等待状态

MSL:报文段最大生存时间,它是任何报文段被丢弃前在网络内的最长时间。

等待 2MSL 原因:

  1. 保证 TCP 协议的全双工连接能够可靠关闭。
    • 如果 B 端没有收到 ACK ,触发超时重发 FIN报文段,A 端依然能处理重发 ACK。如果 A 端直接 CLOSE 状态,就不能保证 B 端收到 ACK。
  2. 保证这次连接的重复数据段从网络中消失。
    • 保证下次连接收到的数据报文段都是来自新连接的目标端。

18.6.2 平静时间的概念

平静时间(quiet time):TCP在重启动后的 MSL 秒内不能建立任何连接。

  • 防止重启前的报文段被认为新的报文。所以要保证重启前的报文在网络中消失才能重启。

18.6.3 FIN_WAIT_2 状态

在挥手断开连接过程中,如果 B端(第三次挥手)不发出 FIN报文段 将会导致 A端 一直处于 FIN_WAIT_2 状态。而 B端 一直处于 CLOSE_WAIT 状态。

直至应用层决定关闭进行关闭才能跳出该状态。

可以设立定时器维护这个状态,但是必须在代码中注明此做法是违背协议的规范。

18.7 复位报文段

TCP首部中的 RST比特 是用于“复位”的。

18.7.1 到不存在的端口的连接请求

产生复位的一种常见情况是当连接请求到达时,目的端口没有进程正在听。

  • 对于 UDP,当一个数据报到达目的端口时,该端口没在使用,它将产生一个ICMP端口不可达的信息。
  • 而TCP则使用复位。

18.7.2 异常终止一个连接

正常的终止连接请求是发送 FIN报文段。

但是直接发送 RST复位报文段 也可以终止连接。称为 异常终止

异常终止一个连接对应用程序来说有两个优点:

  1. 丢弃任何待发数据并立即发送复位报文段;
  2. RST的接收方会区分另一端执行的是异常关闭还是正常关闭。

18.7.3 检测半边打开连接

半打开(Half-Open):一方已经关闭或异常终止连接而另一方却还不知道的TCP连接。

当异常的一方回复正常且继续收到来自另一方的数据(另一方并不知道这边的情况),这时接收方无法辨认接受的数据是什么,于是发送 RST复位报文段 作为应答。

tip:另一种检测方法:

  • 可以采用 keepalive 功能。(在23章-TCP的保活定时器 中说明

18.8 同时打开

同时打开(simultaneous open):每一方必须发送一个SYN,且这些SYN必须传递给对方。

TCP是特意设计为了可以处理同时打开,对于同时打开它仅建立一条连接而不是两条连接。

其他的协议族,最突出的是OSI运输层,在这种情况下将建立两条连接而不是一条连接

  • 两端几乎在同时发送 SYN,并进入SYN_SENT状态。当每一端收到SYN时,状态变为SYN_RCVD,同时它们都再发SYN并对收到的SYN进行确认。当双方都收到SYN及相应的ACK时,状态都变迁为ESTABLISHED
  • 【TCP/IP】TCP详解笔记

18.9 同时关闭

  • 【TCP/IP】TCP详解笔记

18.10 TCP 选项

当前 TCP 选项格式:

  • 【TCP/IP】TCP详解笔记

  • 每个选项的开始是1字节k i n d字段,说明选项的类型。

18.11 TCP 服务器的设计

大多数的TCP服务器进程是并发的。当一个新的连接请求到达服务器时,一般会用一个新的进程或线程处理新客户请求。(不同的操作系统有不同的处理方案

TCP服务器忙时处理连接请求规则:

  1. 服务端维护一个固定长度的连接队列。该队列维护着已经三次握手完成,但是还没有被应用层接受的连接。
    • 注意: 区分TCP接受一个连接是将其放入这个队列,而应用层接受连接是将其从该队列中移出。
  2. 应用层将指明该队列的最大长度,这个值通常称为积压值 ( backlog)
  3. 当一个连接请求(即SYN)到达时,TCP使用一个算法,根据当前连接队列中的连接数来确定是否接收这个连接。
  4. 如果对于新的连接请求,该 TCP 监听的端点的连接队列中还有空间,TCP 模块将对SYN进行确认并完成连接的建立。但应用层只有在三次握手中的第三个报文段收到后才会知道这个新连接时。另外,当客户进程的主动打开成功但服务器的应用层还不知道这个新的连接时,它可能会认为服务器进程已经准备好接收数据了(如果发生这种情况,服务器的TCP仅将接收的数据放入缓冲队列)。
  5. 如果对于新的连接请求,连接队列中已没有空间,TCP将不理会收到的SYN。也不发回任何报文段(即不发回RST)。如果应用层不能及时接受已被 TCP 接受的连接,这些连接可能占满整个连接队列,客户的主动打开最终将超时。

19. TCP 的交互数据流

19.1 引言

建立在TCP协议上的应用层协议有非常多,如FTP、HTTP、Telnet、Rlogin等。这些协议依据数据传输的多少能够分为两类:

  • 交互数据类型:如 Telnet,这类协议一般仅仅做小流量的数据交换,比方每按下一个键,要回显一些字符。
  • 成块数据类型:如 FTP,这类协议须要传输的数据比较多,一般传输的数据量比较大。

19.2 交互式输入

视图:

  • 【TCP/IP】TCP详解笔记

一个回显键入会产生4个报文段:(也可以把②和③合并

  1. 客户发出的按键数据报文段。
  2. 服务器发出的按键确认报文段。
  3. 服务器发出的回显数据报文段。
  4. 客户发出的回显确认报文段。

19.3 经受时延的确认

通常 TCP 在接收到数据时并不立即发送 ACK;它推迟发送,以便将 ACK 与需要沿该方向发送的数据一起发送(有时称这种现象为数据捎带 ACK)。

绝大多数实现采用的时延为200 ms,也就是说,TCP 将以最大 200 ms的时延等待是否有数据一起发送。

19.4Nagle 算法

在上面描述的 Rlogin 回显中,一个字符就回显一次,其中产生的分组为 41字节的分组:20字节IP首部 + 20字节TCP首部 + 1字节的数据。

若把这些小分组放到广域网上,会增加拥塞。

Nagle 算法就是解决上面的情况。

Nagle

  • 该算法要求一个 TCP 连接上最多只能有一个未被确认的未完成的小分组。在该分组的确认到达之前不能发送其他的小分组。
  • TCP 收集这些少量的分组,并在确认到来时以一个分组的方式发出去。
  • Nagle 优点是自适应:确认到达得越快,数据也就发送得越快。

19.4.1 关闭 Nagle 算法

插口API用户可以使用 TCP_NODELAY 选项来关闭Nagle算法。

19.5 窗口大小通告

20. TCP的成块数据流

20.1 引言

主要涉及:

  • 滑动窗口-流量控制
  • 慢启动

20.2 正常数据流

20.3 滑动窗口

利用滑动窗口实现流量控制。

视图:

  • 【TCP/IP】TCP详解笔记

  • 窗口两个边沿的相对运动增加或减少了窗口的大小。

    • 称窗口左边沿向右边沿靠近为窗口合拢。这种现象发生在数据被发送和确认时。
    • 当窗口右边沿向右移动时将允许发送更多的数据,我们称之为窗口张开。这种现象发生在另一端的接收进程读取已经确认的数据并释放了 TCP 的接收缓存时。
    • 当右边沿向左移动时,我们称之为窗口收缩。
  • 注意,窗口只是说明接收方当前能接收的最大容量而已,发送方不必一次性发满整个窗口大小。

20.4 窗口大小

由接收方提供的窗口的大小通常可以由接收进程控制,这将影响 TCP 的性能。

插口API允许进程设置发送和接收缓存的大小。

接收缓存的大小是该连接上所能够通告的最大窗口大小。

应用程序可以通过修改插口缓存大小来增加性能。

20.5 PUSH 标志

发送方使用 PUSH标志 通知接收方将所收到的数据全部提交给接收进程。

这里的数据包括与 PUSH 一起传送的数据以及接收方 TCP 已经为接收进程收到的其他数据。

20.6 慢启动

慢开始算法处理属于 TCP拥塞控制

当主机开始发送数据时,如果立即所大量数据字节注入到网络,那么就有可能引起网络拥塞,因为现在并不清楚网络的负荷情况。所以,较好的方法是 先探测一下,即由小到大逐渐增大发送窗口。

慢开始和拥塞避免

  • 发送方维持一个拥塞窗口 cwnd ( congestion window )的状态变量。
  • 拥塞窗口的大小取决于网络的拥塞程度,并且动态地在变化。
  • 发送方让自己的发送窗口等于拥塞窗口。
  • 发送方控制拥塞窗口的原则是:
    • 只要网络没有出现拥塞,拥塞窗口就再增大一些,以便把更多的分组发送出去。
    • 但只要网络出现拥塞,拥塞窗口就减小一些,以减少注入到网络中的分组数。

慢开始算法

  1. 通常在刚刚开始发送报文段时,先把拥塞窗口 cwnd 设置为一个最大报文段MSS的数值。
  2. 在每收到一个对新的报文段的确认后,把拥塞窗口增加至多一个MSS的数值。
  3. 用这样的方法逐步增大发送方的拥塞窗口 cwnd ,可以使分组注入到网络的速率更加合理。
  4. 为了防止拥塞窗口cwnd增长过大引起网络拥塞,还需要设置一个慢开始门限 ssthresh 状态变量。
    1. cwnd < ssthresh 时,使用上述的慢开始算法。
    2. cwnd = ssthresh 时,既可使用慢开始算法,也可使用拥塞控制避免算法。
    3. cwnd > ssthresh 时,停止使用慢开始算法而改用拥塞避免算法。

拥塞避免

  • 让拥塞窗口cwnd缓慢地增大,即每经过一个往返时间RTT就把发送方的拥塞窗口cwnd加1,而不是加倍。

注意

  1. 符合以下条件之一即可使用 拥塞避免 ,不一定要达到 ssthresh 值。
    1. cwnd > ssthresh 时。
    2. 发送方判断网络出现拥塞(其根据就是没有收到确认)。
  • 【TCP/IP】TCP详解笔记

20.7 成块数据的吞吐量

20.7.1 带宽时延乘积

带宽时延乘积:capacity(bit) = bandwidth (b/s) × round-trip time ( s )

  • 这个值依赖于 网络速度两端的 RTT

20.7.2 拥塞

部分拥塞情景

  • 当数据到达一个大的管道(如一个快速局域网)并向一个较小的管道(如一个较慢的广域网)发送时便会发生拥塞。
  • 当多个输入流到达一个路由器,而路由器的输出流小于这些输入流的总和时也会发生拥塞。

20.8 紧急方式

TCP提供了 紧急方式 ( u rgent mode),它使一端可以告诉另一端有些具有某种方式的 紧急数据 已经放置在普通的数据流中。

另一端被通知这个紧急数据已被放置在普通数据流中,由接收方决定如何处理。

URG比特 被置 1,并且一个 16bit 的紧急指针 被置为一个正的偏移量,该偏移量必须与 TCP 首部中的序号字段相加,以便得出紧急数据的最后一个字节的序号。

紧急方式的作用

  • 两个最常见的例子是 TelnetRlogin。当交互用户键入中断键时。(参考 卷一26 章
  • 另一个例子是 FTP,当交互用户放弃一个文件的传输时。(参考 卷一27 章

21. TCP的超时与重传

21.1 引言

对每个连接,TCP管理4个不同的定时器:

  1. 重传定时器使用于当希望收到另一端的确认。功能如拥塞避免。
  2. 坚持( persist )定时器使窗口大小信息保持不断流动,即使另一端关闭了其接收窗口。
  3. 保活( keepalive )定时器可检测到一个空闲连接的另一端何时崩溃或重启。
  4. 2MSL定时器测量一个连接处于TIME_WAIT状态的时间。

21.2 超时与重传的简单例子

21.3 往返时间测量

TCP 超时与重传中最重要的部分就是对一个给定连接的往返时间(RTT)的测量。

由于路由器和网络流量均会变化,因此我们认为这个时间可能经常会发生变化,TCP 应该跟踪这些变化并相应地改变其超时时间。

首先 TCP 必须测量在发送一个带有特别序号的字节和接收到包含该字节的确认之间的 RTT

M 表示所测量到的 RTT

计算new_RTTS = (1-α)x(old_RTTS) + αx(new_RTT)

  • α:值推荐为 1/8 的平滑因子。
  • new_RTTS:新的平滑往返时间。
  • old_RTTS:旧的平滑往返时间。
  • new_RTT:测量到的 RTT
  • 每个新估计的90%来自前一个估计,而10%则取自新的测量。

计算RTO = RTTS+4x(RTTD)

  • RTO:重传超时时间。
  • RTTD:是RTT的偏差加权的平均值,与 RTTS 和新得到的 RTT 样本之差有关。
  • 第一次测量到 RTT 样本时,RTTS 的取值就为测量的 RTT 样本值。

计算new_RTTD = (1-β)x(lod_RTTD) + βx(new_RTT)

  • β:推荐值为 1/4。
  • 第一次测量 RTTD 时,RTTD 的取值为 RTT 样本值的一半。

如上所述就是测量 RTO 公式和参数。

但是仍然有一个问题需要确定,在计算加权平均 RTTS 时,只要数据包重传没有使用往返时间,就可以得出 RTTSRTO 是准确的。

当之后出现超重时,新 RTOold_RTO 的2倍。

21.4 往返时间RTT的例子

21.5 拥塞举例

21.6 拥塞避免算法

拥塞避免算法是一种处理丢失分组的方法。

拥塞避免算法和慢启动算法需要对每个连接维持两个变量:一个拥塞窗口 cwnd 和一个慢 启动门限 ssthresh。

符合以下条件之一即可使用 拥塞避免 ,不一定要达到 ssthresh 值。

  • cwnd > ssthresh 时。
  • 发送方判断网络出现拥塞(其根据就是没有收到确认)。

21.7 快速重传与快速恢复算法

快重传

快重传算法:

  • 首先要求接收方每收到一个失序的报文段后就立即发出重复确认(为的是使发送方及早知道有报文段没有到达对方)而不要等到自己发送数据时才进行捎带确认。
  • 快重传算法还规定,发送方只要一连收到三个重复确认就应当立即重传对方尚未收到的报文段,而不必继续等待M3设置的重传计时器到期。

快恢复

快恢复有两个要点:

  • 当发送方连续收到三个重复确认,就执行乘法减小算法,把慢开始门限 ssthresh 减半。

  • cwnd 值设置为 慢开始门限 ssthresh 减半后的数值,然后开始执行拥塞避免算法(加法增大),使拥塞窗口缓慢地线性增大。(而不是重新执行慢开始算法

  • 【TCP/IP】TCP详解笔记

21.8 拥塞举例(续)

21.9 按每条路由进行度量

如今较新的TCP实现的路由表项中维持了很多指标,当一个TCP连接关闭时,假设已经发出了足够多的数据来获得有意义的统计资料。

且目的节点的路由表项不是一个默认表项。

那么下列信息就保存在在路由表项中以备下次使用:

  • 被平滑的RTT
  • 被平滑的均值偏差
  • 慢启动门限

足够多的数据:是指 16 个窗口的数据。

21.10 ICMP的差错

TCP 能够遇到的最常见的 ICMP 差错就是:

  • 源站抑制
  • 主机不可达
  • 网络不可达

处理:

  1. 一个接收到的源站抑制引起拥塞窗口 cwnd 被置为1个报文段大小来发起慢启动,但是慢 启动门限 ssthresh 没有变化,所以窗口将打开直至它或者开放了所有的通路(受窗口大小和往返时间的限制)或者发生了拥塞。
  2. 一个接收到的主机不可达或网络不可达实际上都被忽略,因为这两个差错都被认为是短暂现象。这有可能是由于中间路由器被关闭而导致选路协议要花数分钟才能稳定到另一个替换路由。
    • 在这个过程中就可能发生这两个ICMP差错中的一个,但是连接并不必被关闭。相反,TCP试图发送引起该差错的数据,尽管最终有可能会超时。

21.11 重新分组

当TCP超时并重传时,它不一定要重传同样的报文段。

相反,TCP允许进行重新分组而发送一个较大的报文段,这将有助于提高性能( 接收方声明的MSS)。

在协议中这是允许的,因为TCP是使用字节序号而不是报文段序号来进行识别它所要发送的数据和确认。

TCP数据长度

**TCP数据长度 = IP包总长度(IP首部里)- IP首部长度(IP首部里)- TCP首部长度 **

22. TCP 的坚持定时器

22.1 引言

如果接收方发送一个大小为 0 的窗口到发送方,那发送方就停止发送数据,直至窗口为非 0。

TCP 不对 ACK 报文段进行确认,只确认那些包含有数据的 ACK 报文段

22.2 例子&解决

bug情景:如果一个确认丢失了,则双方就有可能因为等待对方而使连接终止:

  • 接受方等待接收数据
    • (因为它已经向发送方通告了一个非0的窗口,但是丢失了,且接收方是不对这个报文段进行确认的)
  • 而发送方在等待允许它继续发送数据的窗口更新。

解决:为防止这种死锁情况发生,发送方使用了一个坚持定时器(persist timer)来周期性地向接收方查询,以便发现窗口是否已增大,范围在5~60 s之间。这些从发送方发出的报文段称为窗口探查(window probe)

22.3 糊涂窗口综合症

糊涂窗口综合症SWS(Silly Window Syndrome)

  • 发生在两端中的任何一端。
  • 接收方可以通告一个小的窗口(而不是一直等到有大的窗口时才通告)。
  • 而发送方也可以发送少量的数据(而不是等待其他的数据以便发送一个大的报文段)。

可以在任何一端采取措施避免出现糊涂窗口综合症的现象:

  1. 接收方不通告小窗口。通常的算法是接收方不通告一个比当前窗口大的窗口(可以为0),除非窗口可以增加一个报文段大小(也就是将要接收的MSS)或可以增加接收方缓存空间的一半,不论实际有多少。
  2. 发送方避免出现糊涂窗口综合症的措施是只有以下条件之一满足时才发送数据:
    1. 可以发送一个满长度的报文段。
    2. 可以发送至少是接收方通告窗口大小一半的报文段。
    3. 可以发送任何数据并且不希望接收ACK(也就是没有还未被确认的数据)或该连接上不能使用 Nagle 算法。
      • 该条件使在有尚未被确认的数据(正在等待被确认)以及在不能使用Nagle算法的情况下,避免发送小的报文段。
      • 例如如果应用程序在进程小数据的写操作(例如比该报文段还小),该条件就可以避免出现糊涂窗口综合症。

23. TCP 的保活定时器

情景:启动一个客户与服务器建立一个连接,然后离去数小时、数天、数个星期或数月,而连接依然保持。中间路由器可以崩溃和重启,电话线可以被挂断再连通,但是只有两端的主机没有被重启,则连接依然保持建立。这种非活动状态可以导致应用进程中的任何一个终止其活动。

许多时候一个服务器希望知道客户主机是否崩溃并关机或者崩溃又重新启动。许多实现提供的保活定时器可以提供这种能力。

保活并不是 TCP 规范中的一部分。Host Requirements RFC提供了3个不使用保活定时器的理由:

  • 在出现短暂差错的情况下,这可能会使一个非常好的连接释放掉;
  • 它们耗费不必要的带宽;
  • 在按分组计费的情况下会在互联网上花掉更多的钱。

如果一个给定的连接在2个小时之内没有任何动作,则服务器向客户发送一个探查报文段。客户主机必须处于以下4个状态之一:

  1. 客户主机仍然正常运行,并从服务器可达。客户的TCP响应正常,而服务器也知道对方是正常工作的。服务器在2个小时以后将保活定时器复位。如果在2个小时定时器到时间之前应用程序的通信量通过此连接,则定时器在交换数据后的未来2个小时再复位。
  2. 客户主机已经崩溃,并且关闭或者正在重新启动。在任何一种情况下,客户的TCP都没有响应。服务器将不能够收到探查的响应,并在75s后超时。服务器总共发送10个这样的探查,每个间隔75秒。如果服务器没有收到一个响应,它就认为客户主机已经关闭并终止连接。
  3. 客户主机崩溃并已经重新启动。这时服务器将收到一个对其保活探查的响应,但是这个响应是一个复位,使得服务器终止这个连接。
  4. 客户主机正常运行,但是从服务器不可达。这与状态2相同,因为TCP不能够区分状态4与状态2之间的区别,它所能发现的就是没有接收到探查的响应。

在这2、3、4情况下,服务器application将收到来自它的TCP的差错报告(通常服务器已经向网络发出了读操作请求,然后等待来自客户的数据。如果保活功能返回一个差错,则该差错将作为读操作的返回值给服务器)。其差错对应如下:

客户主机已经崩溃,并且关闭或者正在重新启动 类似 连接超时
客户主机崩溃并已经重新启动 类似 连接被对方复位
客户主机正常运行,但是从服务器不可达 类似 连接超时

24. TCP 的未来和性能

有兴趣可看原文

参考

链接