TCP/IP详解之:TCP

第17章 TCP:传输控制协议

TCP提供了一种可靠的面向连接的字节流运输层服务

TCP的服务

尽管TCP和UDP都使用相同的网络层(IP),TCP却向应用层提供与UDP完全不同的服务。

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

1)应用数据被分割成TCP认为最适合发送的数据块(而对UDP,应用程序产生的数据报长度不变)。由TCP传递给IP的信息单位称为报文段或段(segment)[ TCP如何确定报文段的长度 ]

2)当TCP发出一个段后,启动一个定时器,等待目的端确认收到这个报文段。若不能及时收到一个确认,将重发这个报文段。[ TCP协议中自适应的超时及重传策略 ]

3)当TCP收到发自TCP连接另一端的数据,它将发送一个确认(不是立即发送)。

4)TCP将保持它首部和数据的检验和。这是一个端到端的检验和。如果收到段的检验和有差错,TCP将丢弃这个报文段和不确认收到此报文段(希望发端超时并重发)。

5)既然TCP报文段作为IP数据报来传输,而IP数据报的到达可能会失序,因此TCP报文段的到达也可能会失序。如果必要,TCP将对收到的数据进行重新排序,将收到的数据以正确的顺序交给应用层。

6)既然IP数据报会发生重复,TCP的接收端必须丢弃重复的数据

7)TCP提供流量控制。

TCP的首部

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

2)一个IP地址和一个端口号也称为一个插口(socket)

3)只有ACK标志为1时,确认序号字段才有效

4)TCP为应用层提供全双工服务。这意味数据能在两个方向上独立地进行传输。因此,连接的每一端必须保持每个方向上的传输数据序号。

5)TCP首部中有6个标志比特:

  URG 紧急指针( urgent pointer)有效
  ACK 确认序号有效
  PSH 接收方应该尽快将这个报文段交给应用层。
  RST 重建连接。
  SYN 同步序号用来发起一个连接。这个标志和下一个标志将在第 1 8章介绍。
  FIN 发端完成发送任务。
6)窗口大小是一个16 bit字段,因而窗口大小最大为65535字节

7)检验和覆盖了整个的TCP报文段:TCP首部和TCP数据。这是一个强制性的字段,端到端校验

8)最常见的可选字段是最长报文大小,称MSS (Maximum Segment Size)。

9)TCP报文段中的数据部分是可选的。在一个连接建立和一个连接终止时,双方交换的报文段仅有TCP首部。在处理超时的许多情况中,也会发送不带任何数据的报文段。

第18章 TCP连接的建立与终止

连接建立时的三次握手和连接终止时的四次挥手

  三次握手和四次挥手的过程示意以及序号关系如下:

  TCP建立连接时三次握手而关闭时需四次挥手,这是由TCP的半关闭特性造成的。 

  一个TCP连接是全双工的,因此每个方向必须单独进行关闭!当一方完成它的数据发送后就可以发送一个FIN来终止这个方向连接。当另一端收到一个FIN,它就通知其应用层对端已经终止了那个方向的数据传送。收到一个FIN只意味着在这一方向上没有数据流动。一个TCP连接在收到一个FIN后仍能发送数据,这以过程示意图如下:

最大报文段长度(MSS)

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


TCP的半关闭

半关闭:TCP连接的一端在结束其发送后还能继续接收另一端数据的能力,此即TCP的半关闭。

TCP半关闭例子:

 

TCP的状态变迁图 

1)2MSL等待状态(TIME_WAIT状态)

TIME_WAIT状态也称为2MSL等待状态。每个具体 TCP实现必须选择一个报文段最大生存时间MSL(Maximum Segment Lifetime)。它是任何报文段被丢弃前在网络内的最长时间。

TIME_WAIT状态存在的理由:

1 可靠地实现TCP全双工连接的终止;

  Client发出最后的ACK回复,但该ACK可能丢失。Server若没有收到ACK,将不断重复发送FIN。∴Client不能立即关闭,它必须确认Server接收到了该ACK。Client会在发送出ACK之后进入到TIME_WAIT状态,Client会设置一个计时器,等待2MSL的时间。如果在该时间内再次收到FIN,那么Client会重发ACK并再次等待2MSL。如果直到2MSL,Client都没有再次收到FIN,那么Client推断ACK已经被成功接收,则结束TCP连接。

2 允许老的重复分节离群的段)在网络中消逝;

  防止上一次连接中的包,迷路后重新出现,影响新连接(经过2MSL,上一次连接中所有的重复包都会消失)

    即这种2MSL等待的结果是这个TCP连接在2MSL等待期间,定义这个连接的插口(客户的IP地址和端口号,服务器的IP地址和端口号)不能再被使用。这个连接只能在2MSL结束后才能再被使用。

2)平静时间的概念

TCP在重启动后的MSL秒内不能建立任何连接,此即平静时间。然而只有极少的实现版遵守这一原则,因为大多数主机重启动的时间都比MSL秒要长。

3)FIN_WAIT_2状态

  在FIN_WAIT_2状态客户端已经发出了FIN,并且另一端也已对它进行确认。除非在实行半关闭,否则将等待另一端的应用层意识到它已收到一个文件结束符说明,并向我们发一个FIN来关闭另一方向的连接。只有当另一端的进程完成这个关闭,我们这端才会从FIN_WAIT_2状态进入TIME_WAIT状态。

  这意味着客户端可能永远保持这个状态。另一端也将处于CLOSE_WAIT状态,并一直保持这个状态直到应用层决定进行关闭。


复位报文段

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

1 产生情况一:到不存在的端口的连接请求

  当连接请求到达时,目的端口没有进程正在听。对UDP,当一个数据报到达目的端口时,该端口没在使用,它将产生一个ICMP端口不可达的信息。而 TCP则使用复位。

2 产生情况二:异常终止一个连接

  正常终止一个连接的方式是一方发送FIN,这也称为有序释放;但也有可能发送一个复位报文段而非FIN来中途释放一个连接,称此为异常释放

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

    (1)丢弃任何待发数据并立即发送复位报文段;
    (2)RST的接收方会区分另一端执行的是异常关闭还是正常关闭。应用程序使用的API必须提供产生异常关闭而不是正常关闭的手段。

3 检测半打开连接

半打开:一方已经关闭或异常终止连接而另一方却还不知道,这样的TCP连接

出现原因:

    一、任何一端的主机异常都可能导致

    二、当客户主机突然掉电而不是正常的结束客户应用程序后再关机

造成后果:

  如果客户端PC机经常掉电结束客户应用程序,但服务端并不知道客户程序已经消失了。这样再次打开客户端PC机时,启动新的客户程序,同时在服务器上也会启动一个新的服务器程序,这会导致服务器主机中产生许多半打开的TCP连接(在第23章中我们将看到使用TCP的keepalive选项能使TCP的一端发现另一端已经消失)。

同时打开

同时打开:两个应用程序同时彼此执行主动打开的情况是可能的,尽管可能性小。每一方必须发送一个SYN,且这些SYN必须传递给对方。这需要每一方使用一个对方熟知的端口作为本地端口。如主机A中的一个应用程序使用本地端口7777,并与主机B的端口8888执行主动打开。主机B中的应用程序则使用本地端口8888,并与主机A的端口7777执行主动打开。

  TCP是特意设计为了可以处理同时打开,对于同时打开它仅建立一条连接而不是两条连接(其他的协议族,最突出的是OSI运输层,在这种情况下将建立两条连接而不是一条连接)。

一个同时打开的连接需要交换 4个报文段,比正常的三次握手多一个。此外要注意的是没有将任何一端称为客户或服务器,因为每一端既是客户又是服务器

同时关闭

 

TCP服务器设计

  大多数的TCP服务器进程是并发的。当一个新的连接请求到达服务器时,服务器接受这个请求,并调用一个新进程来处理这个新的客户请求。不同的操作系统使用不同的技术来调用新的服务器进程。在Unix系统下,常用的技术是使用fork函数来创建新的进程。如果系统支持,也可使用轻型进程,即线程(thread)。

1 TCP服务器的端口号(可复用)

  对于服务器来说端口号可以公用(如telnet知名端口号23),在多个使用某个相同端口号的进程中,只有处于LISTEN的进程能够接收新的连接请求。处于ESTABLISHED的进程将不能接收SYN报文段,而处于LISTEN的进程将不能接收数据报文段。

2 限定的本地IP地址(是对服务器来说的)

  当服务器使用特定的IP地址时,这时这个服务器程序的连接仅局限于特定的IP地址,这时具有限定本地IP地址服务器对其他连接请求将拒绝

3 限定的远端IP地址

之前学UDP时我们知道UDP服务器通常在指定IP本地地址和本地端口外,还能指定远端IP地址和远端端口。

而对于TCP:

4 呼入连接请求队列

问题:当服务器正处于忙时,TCP是如何处理这些呼入的连接请求?

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

1) 正等待连接请求的一端有一个固定长度的连接队列,该队列中的连接已被TCP接受(即三次握手已经完成),但还没有被应用层所接受。注意区分TCP接受一个连接是将其放入这个队列,而应用层接受连接是将其从该队列中移出。

2) 应用层将指明该队列的最大长度,这个值通常称为积压值 (backlog)。它的取值范围是0~5之间的整数,包括0和5(大多数的应用程序都将这个值说明为5)。

3) 当一个连接请求(即SYN)到达时,TCP使用一个算法,根据当前连接队列中的连接数来确定是否接收这个连接。我们本来期望应用层说明的积压值为这一端点所能允许接受连接的最大数目,但情况并非如此。注意积压值说明的只是TCP监听的端点已被TCP接受而等待应用层接受的最大连接数。这个积压值对系统所允许的最大连接数,或者并发服务器所能并发处理的客户数,并无影响。

4) 如果对于新的连接请求,该TCP监听的端点的连接队列中还有空间,TCP模块将对SYN进行确认并完成连接的建立。但应用层只有在三次握手中的第三个报文段收到后才会知道这个新连接。另外,当客户进程的主动打开成功但服务器的
应用层还不知道这个新的连接时,它可能会认为服务器进程已经准备好接收数据了(如果发生这种情况,服务器的TCP仅将接收的数据放入缓冲队列 )。

5) 如果对于新的连接请求,连接队列中已没有空间,TCP将不理会收到的SYN 。也不发回任何报文段(即不发回RST)。如果应用层不能及时接受已被TCP接受的连接,这些连接可能占满整个连接队列,客户的主动打开最终将超时。

第19章 TCP的交互数据流

经受时延的确认

  通常TCP在接收到数据时并不立即发送ACK;相反,它推迟发送,以便将ACK与需要沿该方向发送的数据一起发送(有时称这种现象为数据捎带ACK)。绝大多数实现采用的时延为200ms,也即,TCP将以最大200ms的时延等待是否有数据一起发送。

Nagle算法

  TCP/IP协议中,无论发送多少数据,总是要在数据前面加上协议头,同时,对方接收到数据,也需要发送ACK表示确认。为了尽可能的利用网络带宽,TCP总是希望尽可能的发送足够大的数据。(一个连接会设置MSS参数,因此TCP/IP希望每次都能够以MSS尺寸的数据块来发送数据)。Nagle算法就是为了尽可能发送大块数据,避免网络中充斥着许多小数据块。
  Nagle算法的基本定义是任意时刻,最多只能有一个未被确认的小段。 所谓“小段”,指的是小于MSS尺寸的数据块,所谓“未被确认”,是指一个数据块发送出去后,没有收到对方发送的ACK确认该数据已收到。
  Nagle算法的规则(可参考tcp_output.c文件里tcp_nagle_check函数注释):
(1)如果包长度达到MSS,则允许发送;
(2)如果该包含有FIN,则允许发送;
(3)设置了TCP_NODELAY选项,则允许发送;
(4)未设置TCP_CORK选项时,若所有发出去的小数据包(包长度小于MSS)均被确认,则允许发送;
(5)上述条件都未满足,但发生了超时(一般为200ms),则立即发送。

第20章 TCP的成块数据流

滑动窗口

用三个术语来描述窗口左右边沿的运动:

  1) 称窗口左边沿向右边沿靠近为窗口合拢。这种现象发生在数据被发送和确认时。

  2) 当窗口右边沿向右移动时将允许发送更多的数据,我们称之为窗口张开。这种现象发生在另一端的接收进程读取已经确认的数据并释放了TCP的接收缓存时。

  3)  当右边沿向左移动时,我们称之为窗口收缩。

滑动窗口举例:

(1)从svr4传输8192(1024×8)个字节到bsdi的过程

理解:

1)使用TCP的滑动窗口协议时,接收方不必确认每一个收到的分组。在TCP中,ACK是累积的,即它们表示接收方已经正确收到了一直到确认序号减1的所有字节。

2)来自接收方的一个报文段确认数据并把窗口向右边滑动。这是因为窗口的大小是相对于确认序号的。

3)正如从报文段7到报文段8中变化的那样,窗口的大小可以减小,但是窗口的右边沿却不能够向左移动。

4)接收方在发送一个ACK前不必等待窗口被填满。

(2)从svr4到bsdi的另外8192字节数据的传输过程

理解:

  这一次接收方没有发送一个序号为3073的ACK,而是等待并发送序号为4097的ACK。接收方仅发送了4个ACK(报文段7、10、12和15)。最后1024字节数据的ACK出现在报文段17中,它与FIN的ACK一道发送(比较该图中的报文段17与图20-1中的报文段16和18)。

(3)快发送方和慢接收方

理解:

  发送方发送4个数据报文段去填充接收方的窗口,然后停下来等待一个ACK。接收方发送ACK(报文段8),但通告其窗口大小为0,这说明接收方已收到所有数据,但这些数据都在接收方的TCP缓冲区,因为应用程序还没有机会读取这些数据。另一个ACK(称为窗口更新)在 17.4ms 后发送,表明接收方现在可以接收另外的4096个字节的数据。虽然这看起来像一个ACK,但由于它并不确认任何新数据,只是用来增加窗口的右边沿,因此被称为窗口更新。

  发送方发送最后4个报文段(10~13),再次填充了接收方的窗口。注意到报文段13中包括两个比特标志:PUSH和FIN。随后从接收方传来另外两个ACK,它们确认了最后的4096字节的数据(从4097到8192字节)和FIN(标号为8192)。

PUSH标志

作用:

  发送方使用该标志通知接收方将所收到的数据全部提交给接收进程(而不能等待判断是否还会有额外的数据到达)。这里的数据包括与PUSH一起传送的数据以及接收方TCP已经为接收进程收到的其他数据。

  现实情况是:目前大多数的API没有向应用程序提供通知其TCP设置PUSH标志的方法。因为许多实现程序认为PUSH标志已经过时,一个好的TCP实现能够自行决定何时设置这个标志。

慢启动 

  慢启动为发送方的TCP增加了另一个窗口:拥塞窗口 (congestion window),记为cwnd。

  当与另一个网络的主机建立TCP连接时,拥塞窗口被初始化为1个报文段。每收到一个ACK,拥塞窗口就增加一个报文段(cwnd以字节为单位,但慢启动以报文段大小为单位进行增加)。发送方取拥塞窗口与通告窗口中的最小值作为发送上限。拥塞窗口是发送方使用的流量控制,而通告窗口则是接收方使用的流量控制。

  发送方开始时发送一个报文段,然后等待ACK。当收到该ACK时,拥塞窗口从1增加为2,即可以发送两个报文段。当收到这两个报文段的ACK时,拥塞窗口就增加为 4。这是一种指数增加的关系。在某些点上可能达到了互联网的容量,于是中间路由器开始丢弃分组。这就通知发送方它的拥塞窗口开得过大。

成块数据的吞吐量

1. 带宽时延乘积(通道容量)

  带宽时延乘积(bit) = bandwidth(bit/s) * round-trip time(s)

2.  拥塞

紧急方式

TCP提供了“紧急方式 (urgent mode) ”,它使一端可以告诉另一端有些具有某种方式的“紧急数据”已经放置在普通的数据流中。另一端被通知这个紧急数据已被放置在普通数据流中,由接收方决定如何处理。可以通过设置TCP首部中的两个字段来发出这种从一端到另一端的紧急数据已经被放置在数据流中的通知。 URG比特被置1,并且一个16bit的紧急指针被置为一个正的偏移量,该偏移量必须与TCP首部中的序号字段相加,以便得出紧急数据的最后一个字节的序号。

TCP本身对紧急数据知之甚少。没有办法指明紧急数据从数据流的何处开始。TCP通过连接传送的唯一信息就是紧急方式已经开始(TCP首部中的URG比特)和指向紧急数据最后一个字节的指针。其他的事情留给应用程序去处理。

紧急方式的作用:

  两个最常见的例子是Telnet和Rlogin。当交互用户键入中断键时,第26章将看到使用紧急方式来完成这个功能的例子;另一个例子是FTP,当交互用户放弃一个文件的传输时,将在第27章看到这样的一个例子。Telnet和Rlogin从服务器到客户使用紧急方式是因为在这个方向上的数据流很可能要被客户的TCP停止(也即,它通告了一个大小为0的窗口)。但是如果服务器进程进入了紧急方式,尽管它不能够发送任何数据,服务器TCP也会立即发送紧急指针和URG标志。当客户TCP接收到这个通知时就会通知客户进程,于是客户可以从服务器读取其输入、打开窗口并使数据流动。

第21章 TCP的超时与重传

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

(1)重传定时器使用于当希望收到另一端的确认

(2)坚持定时器使窗口大小信息保持不断流动,即使另一端关闭了其接受窗口

(3)保活定时器可检测到一个空闲连接的另一端何时崩溃或重启

(4)2MSL定时器测量一个连接处于TIME_WAIT状态的时间

往返时间(RTT)

由于路由器和网络流量均会变化,所以RTT可能经常会发生变化,TCP应跟踪这些变化并相应地改变其超时时间。

T C P计算往返时间并使用这些测量结果来维护一个被平滑的 RT T估计器和被平滑的均值偏
差估计器。这两个估计器用来计算下一个重传时间。许多实现对每个窗口仅测量一次 RT T。
K a r n算法在分组丢失时可以不测量 RT T就能解决重传的二义性问题。

拥塞避免算法

慢启动算法是在一个连接上发起数据流的方法,但有时会达到中间路由器的极限,此时分组会被丢弃,而拥塞避免算法是一种处理丢失分组的方法

有两种分组丢失的指示:发生超时和接收到重复的确认(如果使用超时作为拥塞指示,则需要使用一个好的 RTT算法!) 。

拥塞避免算法和慢启动算法是两个目的不同、独立的算法。但是当拥塞发生时,我们希望降低分组进入网络的传输速率,于是可以调用慢启动来作到这一点。在实际中这两个算法通常在一起实现。

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

(1)对一个给定的连接,初始化cwnd为1个报文段,ssthresh为65535个字节

(2)TCP输出例程的输出不能超过cwnd和接收方通告窗口的大小。拥塞避免是发送方使用的流量控制,而通告窗口则是接收方进行的流量控制。前者是发送方感受到的网络拥塞的估计,而后者则与接收方在该连接上的可用缓存大小有关。

(3)当拥塞发生时(超时或收到重复确认),ssthresh被设置为当前窗口大小的一半(cwnd和接收方通告窗口大小的最小值,但最少为 2个报文段)。此外,如果是超时引起了拥塞,则cwnd被设置为1个报文段(这就是慢启动)

(4)当新的数据被对方确认时,就增加cwnd,但增加的方法依赖于我们是否正在进行慢启动或拥塞避免。如果cwnd小于或等于ssthresh,则正在进行慢启动,否则正在进行拥塞避免。慢启动一直持续到我们回到当拥塞发生时所处位置的半时候才停止(因为我们记录了在步骤 2中给我们制造麻烦的窗口大小的一半),然后转为执行拥塞避免。

正如在此图中看到的那样,术语“慢启动”并不完全正确。它只是采用了比引起拥塞更慢些的分组传输速率,但在慢启动期间进入网络的分组数增加的速率仍然是在增加的。只有在达到ssthresh拥塞避免算法起作用时,这种增加的速率才会慢下来。

快速重传与快速恢复算法

若一连串收到3个或3个以上的重复ACK,就非常可能是一个报文段丢失了。于是我们就重传丢失的数据报文段,而无需等待超时定时器溢出,此即快速重传算法;接下来执行的不是慢启动算法而是拥塞避免算法,此即快速恢复算法

具体流程如下:

(1)当收到第3个重复的ACK时,将ssthresh设置为当前拥塞窗口cwnd的一半。重传丢失的报文段。设置cwnd为ssthresh加上3倍的报文段大小。

(2)每次收到另一个重复的ACK时,cwnd增加1个报文段大小并发送1个分组(如果新的cwnd允许发送)。

(3)当下一个确认新数据的ACK到达时,设置cwnd为ssthresh(在第1步中设置的值)。这个ACK应该是在进行重传后的一个往返时间内对步骤1中重传的确认。另外,这个ACK也应该是对丢失的分组和收到的第1个重复的ACK之间的所有中间报文段的确认。这一步采用的是拥塞避免,因为当分组丢失时我们将当前的速率减半。

按每条路由进行度量

较新的T C P实现在路由表项中维持许多我们在本章已经介绍过的指标。当一个 T C P连接关闭时,如果已经发送了足够多的数据来获得有意义统计资料,且目的结点的路由表项不是一

个默认的表项,那么下列信息就保存在路由表项中以备下次使用:被平滑的RTT、被平滑的
均值偏差以及慢启动门限。所谓“足够多的数据”是指16个窗口的数据,这样就可得到16个
RTT采样,从而使被平滑的RTT过滤器能够集中在正确结果的5%以内。
而且管理员可以使用route(8)命令来设置给定路由的度量:前一段中给出的三个指标以
及MT、输出的带宽时延乘积(见第20.7节)和输入的带宽时延乘积。
当建立一个新的连接时,不论是主动还是被动,如果该连接将要使用的路由表项已经有
这些度量的值,则用这些度量来对相应的变量进行初始化。

ICMP的差错

TCP是怎样处理一个给定的连接返回的ICMP的差错。TCP能够遇到的最常见的ICMP差错就是源站抑制、主机不可达和网络不可达。

一个接收到的源站抑制引起拥塞窗口cwnd被置为1个报文段大小来发起慢启动,但是慢
启动门限ssthresh没有变化,所以窗口将打开直至它或者开放了所有的通路(受窗口大
小和往返时间的限制)或者发生了拥塞。

★一个接收到的主机不可达或网络不可达实际上都被忽略,因为这两个差错都被认为是

短暂现象。这有可能是由于中间路由器被关闭而导致选路协议要花费数分钟才能稳定
到另一个替换路由。在这个过程中就可能发生这两个 I C M P 差错中的一个,但是连接
并不必被关闭。相反, T C P 试图发送引起该差错的数据,尽管最终有可能会超时(回
想图 2 1 - 1 中 T C P 在 9 分钟内没有放弃的情况) 。当前基于伯克利的实现记录发生的
I C M P 差错,如果连接超时, I C M P差错被转换为一个更合适的的差错码而不是“连接
超时” 。

重新分组

当T C P超时并重传时,它不一定要重传同样的报文段。相反, T C P允许进行重新分组而发
送一个较大的报文段,这将有助于提高性能(当然,这个较大的报文段不能够超过接收方声
明的M S S) 。在协议中这是允许的,因为 T C P是使用字节序号而不是报文段序号来进行识别它
所要发送的数据和进行确认。

第22章 TCP的坚持定时器

背景

  ACK的传输并不可靠,因为TCP不对ACK报文段进行确认,而只确认那些包含有数据的ACK报文段。若一个确认丢失了,则双方就有可能因为等待对方而使连接终止:接收方等待接受数据(∵其已经向发送方通告了一个非0的窗口),而发送方在等待允许它继续发送数据的窗口更新。

  为了防止此死锁情况的发生,发送方使用一个坚持定时器来周期性向接收方查询,以便发现窗口是否已增大。这些从发送方发出的报文段称为“窗口探查”。

  坚持计时器的截止期设置为重传时间的值,但若没有收到来自接收端的响应,则发送另一个探测报文段,并将坚持计时器的值加倍和并复位,发送端继续发送探测报文段,将坚持计时器的值加倍和复位,直到这个值增大到阈值为止(通常为 60 秒)。在此之后,发送端每隔 60s 就发送一个报文段,直到窗口重新打开为止。

糊涂窗口综合症

概念:

糊涂窗口综合症是指当发送端应用进程产生数据很慢、或接收端应用进程处理接收缓冲区数据很慢,或二者兼而有之;就会使应用进程间传送的报文段很小,特别是有效载荷很小; 极端情况下,有效载荷可能只有1个字节;传输开销有40字节(20字节的IP头+20字节的TCP头) 这种现象。

发送端引起的糊涂窗口综合症

如果发送端为产生数据很慢的应用程序服务(典型的有telnet应用),例如,一次产生一个字节。这个应用程序一次将一个字节的数据写入发送端的TCP的缓存。如果发送端的TCP没有特定的指令,它就产生只包括一个字节数据的报文段。结果有很多41字节的IP数据报就在互连网中传来传去。
解决的方法是防止发送端的TCP逐个字节地发送数据。必须强迫发送端的TCP收集数据,然后用一个更大的数据块来发送。发送端的TCP要等待多长时间呢?如果它等待过长,它就会使整个的过程产生较长的时延。如果它的等待时间不够长,它就可能发送较小的报文段。Nagle找到了一个很好的解决方法,发明了Nagle算法。
 
接收端引起的糊涂窗口综合症
接收端的TCP可能产生糊涂窗口综合症,如果它为消耗数据很慢的应用程序服务,例如,一次消耗一个字节。假定发送应用程序产生了1000字节的数据块,但接收应用程序每次只吸收1字节的数据。再假定接收端的TCP的输入缓存为4000字节。发送端先发送第一个4000字节的数据。接收端将它存储在其缓存中。现在缓存满了。它通知窗口大小为零,这表示发送端必须停止发送数据。接收应用程序从接收端的TCP的输入缓存中读取第一个字节的数据。在入缓存中现在有了1字节的空间。接收端的TCP宣布其窗口大小为1字节,这表示正渴望等待发送数据的发送端的TCP会把这个宣布当作一个好消息,并发送只包括一个字节数据的报文段。这样的过程一直继续下去。一个字节的数据被消耗掉,然后发送只包含一个字节数据的报文段。
对于这种糊涂窗口综合症,即应用程序消耗数据比到达的慢,有两种建议的解决方法。
1.Clark解决方法
Clark解决方法是只要有数据到达就发送确认,但宣布的窗口大小为零,直到或者缓存空间已能放入具有最大长度的报文段,或者缓存空间的一半已经空了。
2.延迟确认
这表示当一个报文段到达时并不立即发送确认。接收端在确认收到的报文段之前一直等待,直到入缓存有足够的空间为止。延迟的确认防止了发送端的TCP滑动其窗口。当发送端的TCP发送完其数据后,它就停下来了。这样就防止了这种症状。迟延的确认还有另一个优点:它减少了通信量。接收端不需要确认每一个报文段。但它也有一个缺点,就是迟延的确认有可能迫使发送端重传其未被确认的报文段。可以用协议来平衡这个优点和缺点,例如现在定义了确认的延迟不能超过500毫秒。

第23章 TCP的保活定时器

对保活功能是有争议的,协议专家继续在争论该功能是否应该归入运输层,或者应当完全由应用层来处理。

保活定时器是为了应对 TCP 连接双方出现长时间的没有数据传输的情况。如果客户端与服务器建立了 TCP 连接之后,客户端由于某种原因导致主机故障,则服务器就不能收到来自客户端的数据,而服务器不可能一直处于等待状态,保活定时器就是用来解决这个问题的。服务器每收到一次客户端的数据,就重新设置保活定时器,通常为2小时,如果2小时没有收到客户端的数据,服务端就发送一个探测报文,以后每隔75秒发送一次,如果连续发送10次探测报文段后仍没有收到客户端的响应,服务器就认为客户端出现了故障,就可以终止这个连接。

第24章 TCP的未来和性能

路径MTU发现

TCP的路径MTU发现按如下方式进行:

(1)在连接建立时,TCP使用输出接口或对端声明的MSS中的最小MTU作为起始的报文段大小。路径MTU发现不允许TCP超过对端声明的MSS。若对端没有指定一个MSS,则默认536。

(2)一旦选定了起始的报文段大小,在该连接上的所有被TCP发送的IP数据报都将被设置DF比特。若某个中间路由器需要对一个设置了DF标志的数据报进行分片,它就丢弃这个数据报并产生一个“ICMP不能分片”差错。

(3)若收到这个ICMP差错,TCP就减少段大小并进行重传。(当由这个ICMP差错引起的重传发生时,拥塞窗口不需要变化,但要启动慢启动)

(4)由于路由可以动态变化,∴在最后一次减少路径MTU的一段时间以后,可以尝试使用一个较大的值(直到等于对端声明的MSS或输出接口MTU的最小值)。

在对非本地目的地,默认的MSS通常为536字节,路径MTU发现可以避免在通过MTU小于576(这非常罕见)的中间链路时进行分片。对于本地目的主机,也可以避免在中间链路(如以太网)的MTU小于端点网络(如令牌环网)的情况下进行分片。但为了能使路径MTU更加有用和充分利用MTU大于576的广域网,一个实现必须停止使用为非本地目的制定的536的MTU默认值。MSS的一个较好的选择是输出接口的MTU(当然要减去IP 和TCP的首部大小)(大多数的实现都允许系统管理员改变这个默认的MSS值)。

长肥管道

长肥网络(LFN):具有大的带宽时延乘积的网络

长肥管道:运行在LFN上的TCP连接被称为长肥管道

由长肥管道会引起以下问题:

(1)TCP首部中窗口大小为16bit,从而将窗口限制在65535字节内。然而网络需要更大的窗口来提供最大的吞吐量。---解决方法:窗口扩大选项

(2)许多TCP实现对每个窗口的RTT仅进行一次测量,它们并不对每个报文段进行RTT测量,在一个长肥网络LFN上需要更好的RTT测量机制。---解决办法:时间戳选项

(3)TCP序号存在回绕问题(∵由于序号空间是有限的,在已经传输了4294967296个字节以后序号会被重用)。---解决办法:使用TCP的时间戳选项的PAWS算法(防止回绕的序号算法

提升长肥管道的性能措施1:窗口扩大选项

窗口扩大选项使TCP的窗口定义从16bit增加为32bit 。这并不是通过修改TCP首部来实现的,TCP首部仍然使用16bit,而是通过定义一个选项实现对16bit的扩大操作来完成的。于是TCP在内部将实际的窗口大小维持为 32 bit的值。

这个选项只能够出现在一个SYN报文段中,因此当连接建立起来后,在每个方向的扩大因子是固定的。为了使用窗口扩大,两端必须在它们的SYN报文段中发送这个选项。主动建立连接的一方在其SYN中发送这个选项,但是被动建立连接的一方只能够在收到带有这个选项的SYN之后才可以发送这个选项。每个方向上的扩大因子可以不同。如果主动连接的一方发送一个非零的扩大因子,但是没有从另一端收到一个窗口扩大选项,它就将发送和接收的移位记数器置为 0。这就允许较新的系统能够与较旧的、不理解新选项的系统进行互操作。

假定我们正在使用窗口扩大选项,发送移位记数为S,而接收移位记数则为R 。于是我们从另一端收到的每一个16bit 的通告窗口将被左移R位以获得实际的通告窗口大小。每次当我们向对方发送一个窗口通告的时候,我们将实际的32 bit 窗口大小右移S比特,然后用它来替换TCP首部中的16bit的值。

提升长肥管道的性能措施2:时间戳选项

时间戳选项使发送方在每个报文段中放置一个时间戳值。接收方在确认中返回这个数值,从而允许发送方为每一个收到的ACK计算RTT (我们必须说“每一个收到的 ACK”而不是“每一个报文段” ,是因为TCP通常用一个ACK来确认多个报文段) 。我们提到过目前许多实现为每一个窗口只计算一个RT T,对于包含8个报文段的窗口而言这是正确的。然而,较大的窗口大小则需要进行更好的RTT计算。

发送方在第1个字段中放置一个32bit的值,接收方在应答字段中回显这个数值。包含这个选项的TCP首部长度将从正常的20字节增加为32字节。

时间戳是一个单调递增的值。接收方只需要回显收到的内容,不需要关注时间戳单元是什么,不需要在两个主机之间进行任何形式的时钟同步。

在连接建立阶段,对这个选项的规定与前面的窗口扩大选项类似。主动发起连接的一方在它的SYN中指定选项。只有在它从另一方的SYN中收到了这个选项之后,该选项才会在以后的报文段中进行设置。

接收方TCP不需要对每个包含数据的报文段进行确认,许多实现每两个报文段发送一个ACK。如果接收方发送一个确认了两个报文段的ACK,那么哪一个收到的时间戳应当放入回显应答字段中来发回去呢?为了减少任一端所维持的状态数量,对于每个连接只保持一个时间戳的数值。选择何时更新这个数值的算法非常简单:

1)TCP跟踪下一个ACK中将要发送的时间戳的值(一个名为tsrecent的变量)以及最后发送的ACK中的确认序号(一个名为lastack的变量)。这个序号就是接收方期望的序号。

2)当一个包含有字节号lastack的报文段到达时,则该报文段中的时间戳被保存在tsrecent中

3)无论何时发送一个时间戳选项, tsrecent就作为时间戳回显应答字段被发送,而序号字段被保存在lastack中。

提升长肥管道的性能措施3:PAWS算法 防止回绕的序号

尽管时间戳选项能够更好地计算RTT,它还为发送方提供了一种方法,以避免接收到旧的报文段(即防止回绕序号)。

PAWS算法不需要在发送方和接收方之间进行任何形式的时间同步。接收方依据的就是时间戳的值应该单调递增,并且每个窗口至少增加 1。(所以如果重新出现的报文段的时间戳<最近有效的时间戳,则PAWS算法会将该报文段丢弃)

T/TCP:为事务用的TCP扩展

为事务用的TCP扩展,即T/TCP,允许一个客户/服务器的请求-应答序列在通常的情况下只使用三个报文段来完成。它避免使用三次握手,并缩短了TIME_WAIT状态,其方法是为每个主机高速缓存少量的信息,这些信息曾用来建立过一个连接。它还在包含数据报文段中使用SYN和FIN标志。

原文地址:https://www.cnblogs.com/hansonwang99/p/5024375.html