tcp/ip详解卷一(笔记3:tcp与相关协议)

相关文章:
tcp/ip详解卷一(笔记1:概述与IP层协议)
tcp/ip详解卷一(笔记2:UDP及相关的协议)
tcp/ip详解卷一(笔记3:tcp与相关协议)

文章目录

  • 17 TCP:传输控制协议
    • 17.1 tcp服务的可靠性
    • 17.2 tcp报文段
  • 18 TCP连接的建立和终止
    • 18.1 TCP连接状态转化图
      • 18.1.1 建立连接超时
      • 18.1.2 TCP的半关闭
      • 18.1.3 time-wait与2MSL等待时间
      • 18.1.4 复位报文
      • 18.1.5 同时开启和同时关闭
      • 18.1.6 TCP服务器对连接的处理
  • 19 TCP的交互数据流
    • 19.1 经受时延的确认(数据捎带ACK)
    • 19.2 Nagle算法
  • 20 TCP的成块数据流
    • 20.1 滑动窗口协议
    • 20.2 慢启动
    • 20.3 紧急数据
      • 20.3.1 什么是紧急数据
      • 20.3.2 关于带外数据:
      • 20.3.3 紧急数据的一般位置计算方法
      • 20.3.4 TCP协议栈进入到紧急模式后做些什么呢
      • 20.3.5 紧急数据的特点
  • 21 TCP的超时与重传
    • 21.1 RTT与RTO
    • 21.1.1 ACK的多义性 与RTT的更新
    • 21.1.2 小节
    • 21.2 慢启动、拥塞避免、快速重传与快速恢复
      • 21.2.1慢启动
      • 21.2.2拥塞避免
      • 21.2.3快速重传与快速恢复
  • 22 TCP的坚持定时器
  • 23 TCP的保活定时器

tcp 协议中包括如下内容:

  1. 如何建立连接、发送方与接收方状态转化;
  2. 不同类型的数据传输:交互型与块数据及其中相关的控制算法;
  3. 数据可靠传输确认与定时管理
    下面分章节对这里的问题分开描述。

17 TCP:传输控制协议

本章主要对tcp协议做一个简要的描述,包括协议特点,协议报文字段描述等

17.1 tcp服务的可靠性

tcp协议的可靠性通过以下方式进行保证:

  1. 数据分块,分成合适的大小传输,由tcp传给ip的信息成为报文段或是段;
  2. tcp发送一个段后,启动一个定时器,等待目的端确认收到这个报文段。如果超时前没有收到,则重传;
  3. 接收端收到数据确认无误(通过检验)后,发送确认;
  4. tcp会对收到的数据进行排序(IP数据包到达时可能会乱序);如果收到重复数据,则会丢失;
  5. 另外,tcp提供流量控制(接收方缓存区);

17.2 tcp报文段

tcp/ip详解卷一(笔记3:tcp与相关协议)_第1张图片

tcp/ip详解卷一(笔记3:tcp与相关协议)_第2张图片

要点:

  1. 每个tcp段都包含源端和目的端的端口号,用于需找发送端与接收端应用进程。TCP通过(发送端IP:port,接收端IP:port)4元组唯一确定一个TCP连接。通常,一个IP地址和一个端口号为成为一个插口(socket)。04元组唯一确定一个TCP连接==。通常,一个IP地址和一个端口号为成为一个插口(socket)。
  2. 序号用于标识从TCP发端向TCP收端发送的数据字节流。如果将字节流看作是在两个应用程序间的单向流动,则TCP用序号对每个字节进行计数;序号是一个无符号的整数。当新建一个连接时,syn标志变1,序号字段包含由这个主机选择的该连接的初始序号ISN(initial sequence number)。该主机包发送的第一个字节序号为这个ISN加1(SYN也需要占用一个序号)。发送的ACK无需任何代价,32bit的确认序号字段和ack一样,总是tcp首部的一部分。
  3. tcp是全双工的,数据能在两个方向传输。因此,每一端都需要保持每个方向上的序号。
  4. tcp首部中的6个标志bit分别为:urg(紧急指针)有效、ack有效、psh(对方应该经过提交这个报文到应用层,目前实现都不会延迟,所有该标注没有实际的作用)、rst(重建连接)、syn(同步序号)、fin(发送端完成发送任务)

18 TCP连接的建立和终止

本章主要描述tcp连接过程的各种状态与注意问题

18.1 TCP连接状态转化图

tcp/ip详解卷一(笔记3:tcp与相关协议)_第3张图片

特殊情况处理如下描述

18.1.1 建立连接超时

连接超时分成两种情况:

  1. 发送端超时:如果发送端发送syn后,在定时器时间内没有收到,则发送端会重发。收到服务端的确认后,发送syn的确认,连接就建立完成了。
  2. 接收端超时:server收到client的syn包,发送syn+Ack,但是没有收到对方的ack,则server端一直保持在syn_rcvd状态(半开启状态),直到超时。(SYN_RCVD是TCP三次握手的中间状 态,是服务端口(监听端口,如应用服务器的80端口)收到SYN包并发送[SYN,ACK]包后所处的状态)另外一种情况是server收到client的syn包,发送syn+Ack,但是没有收到对方;

18.1.2 TCP的半关闭

tcp提供了连接的一端在结束后还能接收来自另外一端数据的能力。这就是所谓的半关闭。通过FIN报文进入半关闭状态(设置tcp中的标志位)。

利用tcp半关闭状态的例子
使用unix 的rsh命令,远程登录到另外一个系统执行一个命令,例如:

rsh nodeX  sort < datafile

该命令就在nodeX上执行sort排序,排序的数据来源于本机文件内容。rsh通过在目标节点上建立tcp连接,然后将要排序的数据发送给目标机。那目标机什么时候执行排序操作呢?他如何直到源节点已经发送完了数据,可以排序了呢(如果不等对端发送完全部的数据,就执行排序是没有意义的)。这里就通过fin状态。源节点发送完所有待排序数据后,就发送Fin。等待目标节点执行完后,发送结果给自己。

如果没有这个特性的化,tcp还需要发送一个特殊包,表示自己传输完毕。

18.1.3 time-wait与2MSL等待时间

time_wait状态也称为2MSL等待时间。每个具体tcp实现都必须选择一个报文段最大生存时间MSL(maximum segment lifetime)。它是任何报文段被丢弃前在网络内的最大时间,该值是有限的(tcp数据封装在IP中,而IP数据报存在TTL)。

当主动执行关闭连接的一方(连接双方中第一个发送FIN报文的一方)在收到fin ack,并收到对方fin,并发送fin ack后,进入time_wait状态,且必须在该状态保持2被MSL,防止对方没有收到该fin ack而重发fin后,自己重发fin ack。

另外,在该2MSL等待时间内,这个连接相关的本地socket 端口不能再被使用。在该等待时间内,任何迟到的报文就被丢弃。

PS:如果停止服务器,服务端口进入time_wait状态,则该端口需要1到4分钟才能被重新使用。因此,立马重启服务会抛出端口占用的错误

18.1.4 复位报文

无论何时,任何一个发往某一个连接的报文出现错误,TCP都会回复一个复位报文。这里出现错误的情况包括:
(1)发到对端,但是目的端口没有进程监听(对比udp,出现该种情况下,则会产生一个ICMP 端口不可达错误);
(2)连接异常终止。当建立连接后,通信的一方(A)突然挂了,通信的另外一方(B)是无法察觉的(如果不发送数据)。然后A又重新启动了,这是B还是在以前的连接上发送数据给A,则会收到A的RST数据段;
(3)异常终止一个连接:正常终止一个连接通过发送一个Fin消息段。也可以主动发送一个RST消息段来异常终止一个连接(优点:会导致TCP丢弃任何待发数据,并立即发送RST)

18.1.5 同时开启和同时关闭

同时打开连接:
tcp/ip详解卷一(笔记3:tcp与相关协议)_第4张图片

同时关闭连接:
tcp/ip详解卷一(笔记3:tcp与相关协议)_第5张图片

18.1.6 TCP服务器对连接的处理

大多数TCP服务进程是并发的。当一个新的连接请求达到服务器时,服务器接收这个请求,并调用一个新的进程来处理这个新的客户请求。那当一个服务器进程接收一个来自客户进程的服务请求时是如何处理端口的?

当启动服务器时,会启动一个进程,监听在服务端口上(通过netstat查看,可看到state为LISTEN状态的进程。)。当有client连接过来时,会重新建立一个进程,该进程用户用户处理该client的请求,该新的server进程依然在服务端口上和client通信

在这里插入图片描述

那在server端,处理不同client请求的进程(或线程)共用同一端口,那如何区分不同的Socket,收到数据后,怎么分发呢?

由于TCP使用四元组(server ip,server port, client ip, client port)唯一标识一个连接,而client的远端端口号不同,这不会造成冲突。

服务程序在listen某个端口并accept某个连接请求后,会生成一个新的socket来对请求进行处理,就可以区分客户。

呼入连接请求队列

一个并发服务器调用一个新的进程来处理每个客户请求,因此处理被动连接的服务器应该始终准备处理下一个呼入的连接请求。但是当服务器正常创建新的进程处理连接请求时,达到多个连接请求,TCP应该如何处理这些呼入的连接请求?

在伯克利的TCP实现中采用如下规则:

  1. 正等待连接请求的一端有一个固定长度的连接队列,该队列的连接已被TCP接收(即三次握手已经完成),但还没有被应用层接收(TCP接收一个连接请求是将请求放入这个队列,应用层接收是将其从队列中移除);
  2. 应用层应该指明该队列的最大值,这个值通常称为积压值。它的取值范围是0 ~ 5之间;
  3. 当一个连接请求达到时,TCP使用一个算法,根据当前的连接队列数来确定是否接收这个连接。注意:积压值说明的是TCP监听的端点已被TCP接收而等待应用层接受处理的最大连接数。和并发服务器最大并发处理数并没有什么关系;
  4. 如果对应新的连接,该TCP监听端点的队列中还有空间,则TCP模块收到的SYN进行确认并完成连接。对于应用层来说,只有在三次握手中的第三个报文收到后才会直到这个连接。另外,当客户进程的主动打开成功,但是服务器的应用层还不知道这个连接(还处于队列中),客户进程会发送数据给服务器(client端成功打开,任务服务进程也完全准备好了),这时,服务器的TCP模块仅将数据放入缓冲队列;
  5. 对于新的连接,如果队列没有空间,则TCP不会理会收到的SYN报文,也不会发送任务的报文段。
  6. 处于队列中的连接,如果应用层不能进行接受并处理,则这些连接可能会占满整个连接队列,客户最终会超时
  7. 应用层接受到client连接请求后,三次握手已经完成了。应用层不能使client的主动打开失效。这时,如果server不想服务这个client,只能发送rst或是fin

19 TCP的交互数据流

本章主要描述传输交互数据时可能会发生的经受时延的确认(数据捎带ACK)、发送数据时使用的Nagle算法。

19.1 经受时延的确认(数据捎带ACK)

在接收方收到数据包后,接收方可能不会里面发送ACK,而是会根据一些策略进行一定时间的推迟。数据捎带ACK就是一种推延策略,在接收方发送数据时,顺便确认,减小网络中的数据包。

交互数据特点:数据量较小,但是数据段数量较多。以Rlogin 为例,和远程系统通信,需要回显我们键入的字符。当发生一次按键后,发生的tcp通信流如下所示(在建立好连接后)。
tcp/ip详解卷一(笔记3:tcp与相关协议)_第6张图片

在实际中,一般会将报文2,3进行合并,即数据捎带ACK。这种合并技术称为经受时延的确认。

当TCP接收到一个数据段DS后,会启动一个定时器,如果在定时器超时前,有数据要发送,则将该数据段DS的ACK和要发送的数据一起发送出去。如果一直没有数据要发送,则定时器超时时,单独发送DS的ACK。

19.2 Nagle算法

Nagle算法要求:在一个TCP连接上最多只能有一个未被确认的未完成的小分组,在该分组的确认达到之前不能发送其他的小分组(即发送一个数据段,然后等待ACK。在等待期间,缓存将要发送的数据,当收到ACK后,一次性的将数据发送出去)。 TCP收集这些小量的数据,并在上一个分组的确认到达的时候,将收集的数据在一个分组发送出去。 该算法的优越之处在于 该算法是自适应的:确认达到越快,数据也发送的越快(ACK达到的越快,则网络情况越好,即不太可能会拥塞,则不必为了网络的整体质量,增加时延,将数据段尽快发送出去)。

算法使用场景:在一个rlogin连接上,客户一般每次发送一个字节到服务器,这就产生了一些41字节的分组:20字节的IP首部,20字节的TCP首部和一字节的数据。在局域网上,这些小分组通常不会引起麻烦,应为局域网一般不会拥塞。但是在广域网中,这些小分组则会增加拥塞(广域网数据量大)。一个简单的就是使用RFC 896中建议的Nagle算法。

注意:该算法可以关闭,即不使用该策略。

另外,在某些交互应用中,如X窗口应用中,不能忍受长时延,即使只有一个字符也必须要立马发送,则需要关闭该算法。在Socket API中,通过TCP_NODELAY来关闭Nagle算法。

20 TCP的成块数据流

本章主要描述TCP中的滑动窗口协议、慢启动、紧急数据;

20.1 滑动窗口协议

在传输成块数据时,更加在乎的是网络的吞吐量,即尽快在一段时间内多发送数据,而不是每发送一段数据,尽快给我反馈。那就需要发送方尽量快的发送数据,那是不是越快越好、仅本地网络最大带宽,发送呢?当然也不是,还需要考虑TCP接收方的处理速度。如果TCP都收到了数据,但是上层应用处理数据慢,数据一直放在缓冲区中,发送方不停的发,最终肯定会耗尽接收方的缓冲区(快的发送方与慢的接收方问题


那接收方如何告诉发送方自己的处理能力呢?这就是滑动窗口协议要做的事。通过TCP首部中的窗口字段,告知发送方自己的当前窗口情况。


窗口更新示例:通信报文如下图所示。
tcp/ip详解卷一(笔记3:tcp与相关协议)_第7张图片

  1. 通过1,2,3个数据段,建立TCP连接;
  2. 发送方发送4,5,6,7四个数据段;
  3. 在收到4,5,6,7个数据段后,发送ACK,同时告知自己的缓冲区满了,接收窗口为0,发送方停止发送数据;
  4. 通过第9个重复的ACK,刷新接收窗口(该报文看起来像个ACK,但由于并不确认任何数据,至少用于增加窗口右边沿,也被称为 窗口更新)
  5. 。。。。

20.2 慢启动

那在建立TCP连接后(主动建立连接的client会收到Server的syn+ack包,该包会告知client的当前窗口),那client直到当前窗口后,是不是就可以里面一直使劲的发送,直接撑死Server的TCP接收缓存窗口呢?当然也不是,发送方不知道当前自己所处的通信网络的拥塞情况。举个例子,通过A和D通信,中间经过BC路由器(A–>B–>C–>D),而BC路由器所处的网络情况不好。A在这使劲的发,把自己的电脑卡的要死,但是BC网络差,丢包严重,数据无法传给D,D压根就收不到,那么A会在一定时间还没有收到ACK后,又要重传。最后效果是A不停的发,D收不到几个,且还大大的加重了BC网络的拥塞情况。费力不讨好。


那A怎么发送才是最优的呢?如果感知网络通信质量呢?这就是慢启动要做的事


TCP支持一种称为“慢启动”的算法。该算法基本思想:新分组进入网络的速率应该与另一端返回确认的速率相同。

慢启动为发送方的TCP增加了另一个窗口:拥塞窗口,记为cwnd。当与另一个网络的主机建立TCP连接时,拥塞窗口被初始化为1个报文段(即在收到确认前,只能有一个未被确认的数据段)。每收到一个ACK,拥塞窗口就增加一个报文段。发送方取拥塞窗口与通告窗口中的最小值为发送上限。拥塞窗口是发送方使用的流量控制,而通告窗口(rwnd)是接收方使用的流量控制。

cwnd在达到阈值前呈指数级增长:假定当前cwnd=1,收到对端的ACK后变为2,若接下来发送的两个数据包又收到ack,则cwnd=4;

慢启动阈值ssthresh:随着cwnd的增加,可能会导致网络过载即出现丢包,此时cwnd的大小会迅速衰减至当前的一半;一旦触发了慢启动阈值,cwnd趋于线性增长,以避免再次迅速引发网络阻塞,直至下次丢包(如此反复)。

网络中实际传输的未经确认的数据大小 = min(rwnd, cwnd);

那拥塞窗口和通告窗口的最佳大小是多少呢?

网络中的传输通道容量为:

Capicity(bit)= bandwidth(b/s) * rount-trip time(s)   

该值称为宽度时延乘积。在网络中,最佳稳定状态是每发送一个数据包,就接收到一个ack。这样,不管有多少个报文填充了网络传输通道,返回路径上总有相同的ACK。发送方接收方都不会有积压的未处理的数据。一个报文的返回时间时为rount-trip time(s) 。这段时间要发送一个包,能发送的数据量就是发送速度* 发送时间。

rwnd的合理值取决 宽度时延乘积

cwnd 的合理值 计算公式:cwnd = min(4 * MSS, max(2 * MSS, 4380))
以太网标准的MSS(maximum segment size)大小通常是1460,初始值为4380即3MSS。

20.3 紧急数据

https://blog.csdn.net/dhaiuda/article/details/79128584

20.3.1 什么是紧急数据

tcp/ip详解卷一(笔记3:tcp与相关协议)_第8张图片

当TCP中在首部设置了URG标志后,在 16 位紧急指针的特定的位置(指向紧急数据的最后一位,也可以是紧急数据的下一位,两者都是实现标准)的数据就是紧急数据。因为只有一个紧急指针,这也意味着它只能标识一个字节的数据。这个指针指向了紧急数据最后一个字节的下一个字节。

对于16 位紧急指针配合使用,只有设置了URG标志后,该指针才有效。

在读取到紧急指针所指向的位置之前,TCP的接受进程都处于紧急状态,当读取到紧急数据后一位时,回复到正常状态。

从上面的特性可以看到,TCP无法告诉紧急数据从哪里开始,只能告诉紧急数据从哪里结束,URG位为1的TCP报文并不是带外数据。抄袭一段百度上的解释:

传输层协议使用带外数据(out-of-band,OOB)来发送一些重要的数据,如果通信一方有重要的数据需要通知对方时,协议能够将这些数据快速地发送到对方。为了发送这些数据,协议一般不使用与普通数据相同的通道,而是使用另外的通道。但是TCP协议没有真正意义上的带外数据。为了发送重要协议,TCP提供了一种称为紧急模式(urgent mode)的机制。TCP协议在数据段中设置URG位,表示进入紧急模式。接收方可以对紧急模式采取特殊的处理。

20.3.2 关于带外数据:

TCP协议没有真正意义上的带外数据,以下是我的理解,原因很简单,从发送主机到接收主机方向的通道只有一条,若要发送带外数据,不是还要建立另外一个TCP连接?TCP连接占用的资源比较大,且连接的建立与释放会耗费一定的时间,每次连接不一定都会频繁的发送紧急数据,甚至不会发送,此时占用资源的利用率非常低,所以TCP协议不会有带外数据。

20.3.3 紧急数据的一般位置计算方法

TCP 在传输数据时是有顺序的,它有字节号,URG 配合紧急指针,就可以找到紧急数据的字节号。紧急数据的字节号公式如下:

紧急数据字节号(urgSeq)= TCP报文序号(seq)  +  紧急指针(urgpoint)−1

例子,如果 seq = 10, urgpoint = 5, 那么字节序号 urgSeq = 10 + 5 -1 = 14.
tcp/ip详解卷一(笔记3:tcp与相关协议)_第9张图片

知道了字节号后,就可以计算紧急数据字位于所有传输数据中的第几个字节了,如果从第 0 个字节开始算起,那么紧急数据就是第 urgSeq - ISN - 1 个字节(还记得 ISN 吗,它表示初始序列号),减 1 表示不包括第一个 SYN 段,因为一个 SYN 段会消耗一个字节号。

20.3.4 TCP协议栈进入到紧急模式后做些什么呢

(1)发送端
发送端也可以进入紧急模式,TCP协议栈会为每个套接字维护一个发送端紧急模式标志和一个发送端紧急指针,当发送端TCP协议栈得知有紧急数据要发送时(即某个进程调用了send(MSG_OOB)函数),将发送端紧急模式置为1,同时将紧急指针的值记录在发送端紧急指针处,随后进入紧急状态,(发送缓冲区中)含有未发送字节到紧急字节之间数据的报文都会将URG位置为1,设置紧急指针的值,进入紧急模式后,无论数据字节是否发出,URG紧急通知都会发送( 数据流会因为TCP流量控制而停止,紧急通知总是无障碍的发送到对端TCP),但紧急数据因为滑动窗口满而不随同发送。当含有紧急字节的报文发送并确认接收后,发送端会解除紧急状态。

tcp/ip详解卷一(笔记3:tcp与相关协议)_第10张图片
可以看到,第十一个字段返回时指出了窗口大小为0,但sun还是发送了含紧急通知的报文(第十二个字段)。接着第十四个字段中真正包含紧急数据。

(2)接收端
接收端TCP协议栈也会给每个套接字配上紧急模式标志和紧急指针接收端在接收到TCP报文后,若发现TCP头部的URG位为1,则将紧急模式标志置为1,保存紧急指针的值,随后进入接收端紧急模式,通知接收进程。此后,TCP监听每一个收到的数据字段,若其中含有紧急字节,则将该字节放在单独的带外缓冲区中(独立于接收缓冲区),如果接收端对套接字调用setsockopt开启了SO_OOBINLINE,此字节将混在普通数据中,称为在线接收。在接收进程读取数据时,只有在下一个待读字节越过紧急字节之后,接收端紧急模式才被解除。

20.3.5 紧急数据的特点

  1. 紧急数据在发送和接受时没有特权,紧急数据时插入到普通的数据中进行流式发送的。在TCP层面(非应用层),发送端缓冲区中,只有先把紧急数据之前的普通数据发送后才能发送紧急数据。
  2. URG标志紧急状态,紧急数据是否正式到达需要依据紧急指针来判定(该指针指向的位置的数据如果还未到来,说明这个紧急数据还未被接收)。

21 TCP的超时与重传

TCP 提供可靠的运输层。它使用的方式之一就是确认从一另一端收到的数据。但数据和确认可能会丢失。TCP通过在发送时设置一个定时器来解决这种问题。如果当定时器溢出时还没有收到确认,它就重发该数据。那定时器时间应该设为多久呢?重复的频率应该是什么呢?本章主要讲述RTT(Round Trip Time,表示从发送端到接收端的一去一回需要的时间)的测量方式与RTO(超时重传时间)如何确定,TCP如何发现包超时、如何决定重发,如何主动避免拥塞

21.1 RTT与RTO

由于路由器和网络流量均会变化,因此RTT会也会进程变化,TCP应该跟踪这些变化并相应的改变其超时时间。

TCP通过记录发送的TCP数据段的时间和接收到该字节的确认的时间来衡量RTT。如下图,可通过报文4,7测量到一个RTT。

tcp/ip详解卷一(笔记3:tcp与相关协议)_第11张图片

用M表示所测量到的RTT。最开始的TCP规范使用低通过滤器来更新一个被平滑的RTT估计器,即为R(即会对每次测量到的RTT进行平滑过滤处理)。

R=a*R+(1-a)M (式1)

这里a是一个推荐值为0.9的平滑因子。每次进行新策略时,这个被平滑的RTT将会得到更新。
RFC 793推荐的重传超时时间RTO(Retransmission timeout)的值为:

RT0=Rβ  (式2)
其中β为推荐值为2的时延离散因子(即随时间变化,每次自增两倍,直到RTO变成64s封顶)

举例:如果数据报D超时还没有收到ACK,则首次在R时间后重传,如果还没有收到确认,则在2R后再次重传,之后是4R…,直到增加到64s秒后,一直按照64s的间隔重传。

** 另外,TCP还需要记录RTT的方差。当RRT变化起伏很大时,使用基于均值和方差来计算RTO **,其计算公式如下:

Err=M-A
A=A+gErr 
D=D + h(|Err | - D)
RTO = A + 4D

其中A表示被平滑的RTT。即上公式1计算出的值。D是被平滑的均值偏差。Err是刚得到的测量结果与当前RTT估计值之差。增量g起平均作用,取值为1/8。偏差的增益是h,取值为0.25。当RTT变化时,较大的偏差增益使得RTO快速上升

21.1.1 ACK的多义性 与RTT的更新

当ACK超时,发生重传后,收到一个ACK,则该ACK是对之前的包的确认还是对重传包的确认呢?

当一个超时和重传发生时,在重传数据的确认最后到达时,不能更新RTT的估计值,因为我们不知道该ACK是针对那次数据传输。

另外,还有一种情况不不会进行RTT的更新,即:在发送一个报文段时,如果给定连接的定时器已经被使用,则该报文段不被计时(即不会测量、记录该报文的RTT)

举例:
当发送报文3 后,启动了定时器,此时3包的ack没有收到,则该定时器不会关闭,此时发送了报文 4多个报文,则在收到4的ack也不会更新RTT(原因很简单,发送时,这几个报文的发送不会产生新的定时器,所有也无从计时)。这里会在收到3的ack后,记录到一个新的RTT。
tcp/ip详解卷一(笔记3:tcp与相关协议)_第12张图片

21.1.2 小节

TCP会记录追踪RTT,并根据RTT来确定RTO(什么时候应用重传了)。当RTT比较平稳(方差较小时),使用 被平滑的RTT估计值公式估计RTT,并更新RTO。当RTT方差较大,使用均值偏差公式计算RTT和RTO。

另外,当发生重传后,对重传包的ACK不进行RTT的更新。

21.2 慢启动、拥塞避免、快速重传与快速恢复

本节主要讲述TCP如何控制自己的发送速度,以保证自己的传输速度,另外,在网络拥塞发生时,尽量控制自己的速度,造成网络情况的快速恶化。

TCP在开始建立连接后,会执行慢启动来快速的增加自己的发送速度。当发送速度达到某一个值后,开始执行拥塞避免算法来控制自己的发送速度 。 但是网络还坏情况动态变化的,有时候,会发现一下网络异常情况,如包丢失,这时TCP会进行快速重传,然后TCP发送进入快速恢复阶段。

相关变量:

  1. cwnd:拥塞窗口大小。初始化为1个报文段大小。
  2. ssthresh:慢启动门限。初始化为65535字节,当拥塞发生时,ssthresh=max( 2, min(cwnd, rwnd)/2)。
  3. rwnd:通告窗口,即TCP接收方告知的自己的接收缓冲区的大小;
  4. 发送窗口:min(cwnd,rwnd);

拥塞的定义:

由于有份受到损坏引起的丢失是非常少的。因此,分组的丢失就意味者在源主机和目的主机之间的某处发生了拥塞。有两种分组丢失的指示:发生超时和收到重复的确认。

21.2.1慢启动

采用该算法的条件:当 cwnd <= ssthresh

算法执行过程:

  1. 开始时,将cwn初始化为1.
  2. 此后,每收到一个ACK,Cwnd加1。该种情况下,窗口按照指数方式增加。直到cwnd > ssthresh此后,每收到一个ACK,Cwnd加1。该种情况下,窗口按照指数方式增加。直到cwnd > ssthresh
  3. 另外,如果包超时引起了拥塞时,会重置cwnd 为1个报文段大小。

21.2.2拥塞避免

采用该算法的条件:当 cwnd > ssthresh

算法执行过程:

  1. 开始时,将ssthresh=65535字节。当拥塞发生时(超时或是产生了重复的确认),ssthresh=max( 2, min(cwnd, rwnd)/2 )。
  2. 此后,每次收到一个确认时,将cwnd增加1/cwnd,cwnd处于线性增长。(在一个往返时间内,最多能发送cwnd包,则在一个往返时间内,最多能收到接近于cwnd个的ack。则下次cwnd 趋近与cwnd+1 )。在一个往返时间内,cwnd最多增加一个报文段。(对比,慢启动算法中,在一个往返时间内,cwnd最多能增加cwnd)。

正常情况(没有发生拥塞时),cwnd的增长曲线如下:

tcp/ip详解卷一(笔记3:tcp与相关协议)_第13张图片

21.2.3快速重传与快速恢复

TCP在收到一个失序的报文时,TCP立即产生一个ACK(重复的ACK,该重复的ACK的目的在于让对方直到收到了失序的ACK,并告诉对方自己希望收到的序号) 。正常情况下,会是经受时延的确认,即数据捎带ACK或是ACK确认时钟超时后发送(绝大多数实现,采用的时延是200ms,即TCP将以最大200ms的时延等待是否有数据一起发送)。

由于我们不知道一个重复的ACK是由一个丢失的报文启动的,还是由于因为出现了几个报文段的重新排序,因此我们等待少量的ACK到来。如果只是一些重新排序,则在重新排序的被处理时,可能只会产生1~2个重复的ACK。如果收到3个或三个以上的重复ACK,就非常可能是一个报文段丢失了。于是立马重传丢失的报文段,而无需等待超时定时器溢出,这就是快速重传。

快速重传:当确定发生包丢失时,立马重传丢失的报文段,而无需等待超时定时器溢出,这就是快速重传。

采用该算法的条件:当发生快速重传后,会进入快速恢复算法

算法执行过程:

  1. 如果收到3个或三个以上的重复ACK时,将ssthresh设置为当前cwnd的一半。重传丢失的报文段;
  2. 设置cwnd为ssthresh加上三倍的报文段大小;
  3. 每次收到另外一个重复的ack,cwnd增加一个1个报文段大小,并发送1个分组(如果新的发送窗口允许发送);
  4. 当下一个确认新数据的ACK达到时,设置cwnd为ssthresh。该ACK应该是在进行重传后的第一个往返时间内对步骤1中重传的确认。另外,同时,这个ACK也应该对丢失分组和收到的第一个重复的ACK之间的所有的中间报文段的确认(例如,接收方收到了报文3,4,5,然后收到了7,8。报文6丢失,则在收到重传的报文6后,确认的是报文8,一起确认了7和8)。之后恢复正常,进入拥塞避免阶段,采用拥塞避免算法。

22 TCP的坚持定时器

当TCP通过让接收方指定希望从发送方接收的自己树(即窗口大小)来进行流量控制。如果当窗口变成0之后,发送方会停止发送新的数据,直到接收方发送一个窗口更新报文。那么如果该窗口更新报文丢失会怎么样呢?发送方等待窗口更新,而接收方等待发送方发送新的数据。TCP必须能够处理此窗口更新报文丢失的情况。

TCP 的ACK传输并不可靠,TCP不会对ACK报文进行确认,只确认那些含有数据的ACK报文段(不然,会不停的、无休止的双方确认下去)

为了防止窗口更新报文丢失引起了双方相互等待,发送方使用一个检查定时器(persist timer)来周期性的向接收方查询,以便发现窗口是否增加。这些发送方发成的报文段称为窗口探查。

当TCP发送方收到一个通告窗口为0的报文时,发送方设置其坚持定时器。如果该定时器时间到时还没有收到窗口更新报文,则发送方发送窗口探查报文,查询这个窗口更新报文是否丢失

定时器的时间序列:坚持定时器使用了普通的TCP指数退避,对于一个典型的局域网连接,检测定时器首次超时时间1.5s左右,第二次超时增加一倍,为3s,下次乘以4,再下次乘以8…

==窗口探查报文:==包含一个字节的数据。TCP总是允许发送已关闭的窗口之后的一个字节的数据(如果窗口更新了,则会对该字节进行确认;否则确认是原先的数据,该新数据由于缓冲区慢,而丢弃了,不会进行确认)

基于窗口的流量控制方案,会导致“糊涂窗口综合症”的状态。如果发生这种状况,则连接之间会发送少量的数据,而不是满长度的报文段(即连接之间会存在小报文)

该种情况可发生在连接的任何一方:接收方通告一个小的窗口(而不是一直等到有大的窗口时才通告),而发送方发送少量的数据(而不是等待其他的数据以便发送一个大的报文段),这两种情况就是“糊涂窗口综合症”的现象。为了避免这种情况(糊涂窗口综合症),在窗口更新时,TCP采用如下的方法

  1. 接收方不通告小窗口。通常的算法是接收方不通告一个比当前大的窗口,除非窗口可增加一个报文段大小(也就是说要接收的化,接收MSS大小的数据)或者是增加接收方缓存空间的一半,不论实际大小(也就是说,接收方接收窗口空闲区变化很小时,不会更新。如某个时候,缓存区满了,这是接收方发送窗口为0的通告。然后一段时间后,接收应用程序读取了3个字节的数据,然后程序处理其他优先级更高的事去了,TCP接收缓存区空了3个字节的空间,但是TCP不会更新这个3个字节的变化,防止发送方发送只有三个字节有效数据的小包);
  2. 发送方避免出现“糊涂窗口综合症”的措施是只有以下条件之一满足时,才发送数据:1)可以发送一个满长度的报文段;2)可以发送至少接收方通告窗口大小的一半的报文;3)能够发送手头的所有数据,并且不希望接收ACK(发送方没有未被确认的数据)或是连接禁止了Nagle算法。

23 TCP的保活定时器

正常情况下,在建立连接后,如果双方都没有发送任何数据,只要两端的主机没有重启,则连接会依然保持建立。

宝货定时器的作用是:试图检测半开发的连接(例如,当client关闭后,服务器还是保持连接,等待来自client的数据)。

注意:保活并不是TCP规范中的一部分。该功能也可以在应用级完成,如在应用层协议或是应用程序中实现。

你可能感兴趣的:(编译原理,网络,操作系统)