6.3 TCP的数据编码与确认
TCP协议采用了许多与数据链路层类似的机制来保证可靠的数据传输,如采用序列号、确认、滑动窗口协议等。只不过TCP协议的目的是为了实现端到端结点之间的可靠数据传输,而数据链路层协议则为了实现相邻结点之间的可靠数据传输。
首先,TCP要为所发送的每一个报文段加上序列号,保证每一个报文段能被接收方接收,并只被正确地接收一次。
其次,TCP采用具有重传功能的积极确认技术作为可靠数据流传输服务的基础。这里,“确认”是指接收端在正确收到报文段之后向发送端回送一个确认(ACK)信息。发送方将每个已发送的报文段备份在自己的发送缓冲区里,而且在收到相应的确认之前是不会丢弃所保存的报文段的。“积极”是指发送方在每一个报文段发送完毕的同时启动一个定时器,假如定时器的定时期满而关于报文段的确认信息尚未到达,则发送方认为该报文段已丢失并主动重发。为了避免由于网络延迟引起迟到的确认和重复的确认,TCP规定在确认信息中捎带一个报文段的序号,使接收方能正确地将报文段与确认联系起来。
第三,采用可变长的滑动窗口协议进行流量控制,以防止由于发送端与接收端之间的不匹配而引起数据丢失。这里所采用的滑动窗口协议与数据链路层的滑动窗口协议在工作原理上是完全相同的,惟一的区别在于滑动窗口协议用于传输层是为了在端到端结点之间实现流量控制,而用于数据链路层是为了在相邻结点之间实现流量控制。TCP采用可变长的滑动窗口,使得发送端与接收端可根据自己的CPU和数据缓存资源对数据发送和接收能力来作出动态调整,从而灵活性更强,也更合理。
6.4 传输效率与流量控制
6.4.1 滑动窗口机制
TCP 采用大小可变的滑动窗口进行流量控制。窗口大小的单位是字节。
在 TCP 报文段首部的窗口字段写入的数值就是当前给对方设置的发送窗口数值的上限。发送窗口在连接建立时由双方商定。但在通信的过程中,接收端可根据自己的资源情况,随时动态地调整对方的发送窗口上限值(可增大或减小)。
举例:见下图
情况(a):
发送端要发送 900 字节长的数据,划分为 9 个 100 字节长的报文段,而发送窗口确定为 500 字节。发送端只要收到了对方的确认,发送窗口就可前移。发送 TCP 要维护一个指针。每发送一个报文段,指针就向前移动一个报文段的距离。
情况(b):
发送端已发送了 400 字节的数据,但只收到对前 200 字节数据的确认,同时窗口大小不变。现在发送端还可发送 300 字节。
情况(c):
发送端收到了对方对前 400 字节数据的确认,但对方通知发送端必须把窗口减小到 400 字节。现在发送端最多还可发送 400 字节的数据。
6.4.2 窗口与TCP的流量控制
1. 流量控制
TCP采用大小可变的滑动窗口机制实现流量控制功能。窗口的大小是字节。在TCP报文段首部的窗口字段写入的数值就是当前给对方设置发送窗口的数据的上限。
在数据传输过程中,TCP提供了一种基于滑动窗口协议的流量控制机制,用接收端接收能力(缓冲区的容量)的大小来控制发送端发送的数据量。
在建立连接时,通信双方使用SYN报文段或ACK报文段中的窗口字段捎带着各自的接收窗口尺寸,即通知对方从而确定对方发送窗口的上限。在数据传输过程中,发送方按接收方通知的窗口尺寸和序号发送一定量的数据,接收方根据接收缓冲区的使用情况动态调整接收窗口尺寸,并在发送TCP报文段或确认段时稍带新的窗口尺寸和确认号通知发送方。
如图所示。设主机A向主机B发送数据。双方确定的的窗口值是400。设一个报文段为100字节长,序号的初始值为1(即SEQ=1)。在图15.8中,主机B进行了三次流量控制。第一次将窗口减小为300字节,第二次将窗口又减为200字节,最后一次减至零,即不允许对方再发送数据了。这种暂停状态将持续到主机B重新发出一个新的窗口值为止。
6.6.1 TCP报文段的格式
TCP的协议数据单元被称为报文段(Segment),TCP通过报文段的交互来建立连接、传输数据、发出确认、进行差错控制、流量控制及关闭连接。报文段分为两部分,即报文段头和数据,所谓报文段头就是TCP为了实现端到端可靠传输所加上的控制信息,而数据则是指由高层即应用层来的数据。下图给出了TCP报文段头的格式。其中有关字段的说明如下:
6.6.2 序号与确认
TCP不是按传送的报文段来编号。TCP将所要传送的整个报文(这可能包括许多个报文段)看成是一个个字节组成的数据流,然后对每一个数据流编一个序号。在连接建立时,双方要商定初始序号。TCP就将每一次所传送的报文段中的第一个数据字节的序号,放在 TCP首部的序号字段中。
TCP的确认是对接收到的数据的最高序号(即收到的数据流中的最后一个序号)表示确认。但返回的确认序号是已收到的数据的最高序号加1。也就是说,确认序号表示期望下次收到的第一个数据字节的序号。
由于TCP能提供全双工通信,因此通信中的每一方都不必专门发送确认报文段,而可以在传送数据时顺便把确认信息捎带传送。
若发送方在规定的设置时间内没有收到确认,就要将未被确认的报文段重新发送。接收方若收到有差错的报文段,则丢弃此报文段而并不发送否认信息。若收到重复的报文段,也要将其丢弃,但要发回(或捎带发回)确认信息。这与数据链路层的情况相似。
6.10 TCP连接的建立
1. 建立连接
在源主机想和目的主机通信时,目的主机必须同意,否则TCP连接无法建立。为了建立一个TCP连接,两个系统需要同步其初始TCP序号ISN。序号用于跟踪通信顺序并确保多个包传输时没有丢失。初始序号是TCP连接建立时的起始编号。为了确保TCP连接的成功建立,TCP采用了一种称为三次握手的方式,三次握手方式使得“序号/确认号”系统能够正常工作,从而使它们的序号达成同步。如果三次握手成功,则连接建立成功,可以开始传送数据信息。
其三次握手分别为:
第一步:源主机A的TCP向主机B发出连接请求报文段,其首部中的SYN(同步)标志位应置为1,表示想与目标主机B进行通信,并发送一个同步序列号X(例:SEQ=100)进行同步,表明在后面传送数据时的第一个数据字节的序号是X+1(即101)。SYN同步报文会指明客户端使用的端口以及TCP连接的初始序号。
第二步:目标主机B的TCP收到连接请求报文段后,如同意,则发回确认。在确认报中应将ACK位和SYN位置1,表示客户端的请求被接受。确认号应为X+1(图15.5中为101),同时也为自己选择一个序号Y。
第三步:源主机A的TCP收到目标主机B的确认后要向目标主机B给出确认,其ACK置1,确认号为Y+1,而自己的序号为X+1。TCP的标准规定,SYN置1的报文段要消耗掉一个序号。
运行客户进程的源主机A的TCP通知上层应用进程,连接已经建立。当源主机A向目标主机B发送第一个数据报文段时,其序号仍为X+1,因为前一个确认报文段并不消耗序号。
当运行服务进程的目标主机B的TCP收到源主机A的确认后,也通知其上层应用进程,连接已经建立。至此建立了一个全双工的连接。
2. 传送数据
位于TCP/IP分层模型的较上层的应用程序传输数据流给TCP。TCP接收到字节流并且把它们分解成段。假如数据流不能被分成一段,那么每一个其它段都被分给一个序列号。在目的主机端.这个序列号用来把接收到的段重新排序成原来的数据流。
如图给出了两台主机在成功建立连接后传输数据的示例。
(1) 主机A使用滑动窗口发送全部的四个段到主机B。这是第一步。不幸的是,只有段l03、105和106成功地到达了生机B(参看②)。
(2) 因为段103和104是连续的,所以主机B返回一个确认给主机A,通知主机A它只成功地接收到了第103段,在它的确认中主机B使用它期待得到的下一个序列号作为确认(参看③通过给出序列号104)。
(3) 主机A接到主机B的报文后,重新发送段104、105和106(参看④)。虽然主机B已经成功地收到了段105和106,但是根据协议规定,也必须重新发送。
(4) 当主机2成功地收到这些段以后.主机B返回一个确认给主机A(参看⑥),并根据序列号把它们重组成原来的数流。把它传输到高层应用程序。
6.11 TCP连接的拆除
一个TCP连接建立之后,即可发送数据,一旦数据发送结束,就需要关闭连接。由于TCP连接是一个全双工的数据通道,一个连接的关闭必须由通信双方共同完成。当通信的一方没有数据需要发送给对方时,可以使用FIN段向对方发送关闭连接请求。这时,它虽然不再发送数据,但并不排斥在这个连接上继续接收数据。只有当通信的对方也递交了关闭连接的请求后,这个TCP连接才会完全关闭。
在关闭连接时,既可以由一方发起而另一方响应,也可以双方同时发起。无论怎样,收到关闭连接请求的一方必须使用ACK段给予确认。实际上,TCP连接的关闭过程也是一个三次握手的过程。
在关闭连接之前,为了确保数据正确传递完毕,仍然需要采用“三次握手”的方式来关闭连接,如图15.7所示。
其三次握手分别为:
第一步:源主机A的应用进程先向其TCP发出连接释放请求,并且不再发送数据。TCP通知对方要释放从A到B这个方向的连接,将发往主机B的TCP报文段首部的终止比特FIN置1,其序号X等于前面已传送过的数据的最后一个字节的序号加1。
第二步:目标主机B的TCP收到释放连接通知后即发出确认,其序号为Y,确认号为X+1,同时通知高层应用进程,如图15.7中的箭头①。这样,从A到B的连接就释放了,连接处于半关闭状态,相当于主机A向主机B说:“我已经没有数据要发送了。但如果还发送数据,我仍接收。”此后,主机B不再接收主机A发来的数据。但若主机B还有一些数据要发送主机A,则可以继续发送。主机A只要正确收到数据,仍应向主机B发送确认。
第三步:若主机B不再向主机A发送数据,其应用进程就通知TCP释放连接,如图15.7中的箭头②。主机B发出的连接释放报文段必须将终止比特FIN和确认比特ACK置1,并使其序号仍为Y,但还必须重复上次已发送过的ACK=X+1。主机A必须对此发出确认,将ACK置1,ACK=Y+1,而自己的序号是X+1。这样才把从B到A的反方向的连接释放掉。主机A的TCP再向其应用进程报告,整个连接已经全部释放。
6.16 重发机制
重发机制是TCP中最重要的、最复杂的问题之一。TCP每发送一个报文段,就设置一次定时器。只要定时器设置的重发时间到而还没有收到确认,就要重发这一报文段。大家知道,TCP是在一个互连网的环境下工作。发送的报文段可能只经过一个高速率的局域网,但也可能是经过多个低速率的广域网。报文段的端到端的时延会相差很多倍。那么,定时器的重发时间究竟应设置为多大?
TCP环境
报文往返时间不定、有很大差别
A、B在一个局域网络,往返时延很小
A、C在一个互联网内,往返时延很大
因此,A很难确定一个固定的、与B、C通信都适用的定时器时间
6.16.1 自适应重传算法
TCP采用了一种自适应算法。这种算法记录每一个报文段发出的时间,以及收到相应的确认报文段的时间。这两个时间之差就是报文段的往返时延。将各个报文段的往返时延样本加权平均,就得出报文段的平均往返时延T。
6.17 TCP的拥塞控制技术
采用滑动窗口机制还可对网络进行拥塞控制,将网络中的分组(TCP报文段作为其数据部分)数量维持在一定的数量之下,当超过该数值时,网络的性能会急剧恶化。传输层的拥塞控制有慢开始(Slow-Start)、拥塞避免(Congestion Avoidance)、快重传(Fast Retransmit)和快恢复(Fast Recovery)四种算法。
拥塞: 大量数据报涌入同一交换节点(如路由器),导致该节点资源耗尽而必须丢弃后面到达的数据报时,就是拥塞。
6.17.1 慢开始和拥塞避免
发送端的主机在确定发送报文段的速率时,既要根据接收端的接收能力,又要从全局考虑不要使网络发生拥塞。
因此,每一个 TCP 连接需要有以下两个状态变量:
接收端窗口 rwnd (receiver window) 又称为通知窗口(advertised window)。
这是接收端根据其目前的接收缓存大小所许诺的最新的窗口值,是来自接收端的流量控制。接收端将此窗口值放在 TCP 报文的首部中的窗口字段,传送给发送端。
拥塞窗口 cwnd (congestion window) 是发送端根据自己估计的网络拥塞程度而设置的窗口值,是来自发送端的流量控制。
在以太网的环境下,当发送端不知道对方窗口大小的时候,便直接向网络发送多个报文段,直至收到对方通告的的窗口大小为止。但如果在发送方和接收方有多个路由器和较慢的链路时,就可能出现一些问题,一些中间路由器必须缓存分组,并有可能耗尽存储空间,这样就会严重降低TCP连接的吞吐量。这时采用了一种称为慢启动的算法,慢启动为发送方的TCP增加一个拥塞窗口(发送端根据自己估计的网络拥塞程度而设置的窗口值,是来自发送端的流量控制),当与另一个网络的主机建立TCP连接时,拥塞窗口被初始化为1个报文段(即另一端通告的报文段大小),每收到一个ACK,拥塞窗口就增加一个报文段(以字节为单位)。发送端取拥塞窗口与通告窗口中的最小值作为发送上限。拥塞窗口是发送方使用的流量控制,而通告窗口则是接收方使用的流量控制。开始时发送一个报文段,然后等待ACK。当收到该ACK时,拥塞窗口从1增加为2,即可发送两个报文段。当收到这两个报文段的ACK时,拥塞窗口就增加为4。这是一种指数增加的关系。在某些互联网的中间某些点上可能达到了互联网的容量,于是中间路由器开始丢弃分组,这就通知发送方它的拥塞窗口开得过大。
1. 慢开始和拥塞避免
为了解决一条TCP连接会因等待重传计时器的超时而空闲较长的时间,可以采用快重传和快恢复的拥塞控制算法。
(1) 当 TCP 连接进行初始化时,将拥塞窗口置为 1。并设置慢启动门限值
(2) 发送端的发送窗口不能超过拥塞窗口 cwnd 和接收端窗口 rwnd 中的最小值。我们假定接收端窗口足够大,因此现在发送窗口的数值等于拥塞窗口的数值。
(3) 发送端若收到了对所有发出报文段的确认,就在下次发送时将拥塞窗口加倍,可见拥塞窗口从1开始,按指数规律增长。若出现超时,将当前拥塞窗口值减半,作为新的门限窗口值,同时拥塞窗口再次变为1
(4) 拥塞窗口重新从1开始按指数规律增长。但当增长到新的门限值时,就每次将拥塞窗口加1,使拥塞窗口按线性规律增长。
2、快重传和快恢复
有时一条TCP连接会因等待重传计时器的超时而空闲较长的时间。为此以后又增加了两个新的拥塞控制算法。这就是快重传和快恢复。
下面结合一个例子来说明快重传的工作原理。
假定发送端发送了一个报文段M1~M4共4个报文段。假定由于网络拥塞使M3丢失了。接收端后来收到下一个M4,发现其序号不对,但仍收下放在缓存中,同时发出确认,不过发出的是重复的ACK2共(不能够发送ACK4,因为ACK4表示M4和M3都已经收到了)。发送端接着发送M5和M6。接收端收到了M5和M6后,也还要分别发出重复的ACK2。这样,发送端共收到了接收端的四个ACK2,其中三个是重复的。快重传算法规定,发送端只要一连收到三个重复的ACK即可断定有分组丢失了,就应立即重传丢失的报文段M3而不必继续等待为M3设置的重传计时器的超时。
与快重传配合使用的还有快恢复算法。当不使用快恢复算法时,发送端若发现网络出现拥塞就将拥塞窗口降低为1,然后执行慢开始算法。但这样做的缺点是网络不能很快地恢复到正常的工作状态。快恢复算法可以较好地解决这一问题,其具体步骤如下:
(1)当发送端收到连续三个重复的ACK时,就重新按照前面讲过的“乘法减小”重新设置慢开始门限ssthresh。这一点和慢开始算法是一样的。
(2)与慢开始不同之处是拥塞窗口cwnd不是设置为1,而是设置为ssthresh+3×MMS。
(3)若收到的重复的ACK为n个(n>3),则将cwnd设置为ssthresh+n×MMS。
(4)若发送窗口值还容许发送报文段,就按拥塞避免算法发送报文段。
(5)若收到了确认新的报文段的ACK,就将cwnd缩小到ssthresh。
6.17.2 IP层对改善TCP性能的支持
路由器报文丢弃策略与拥塞控制
(1) 早期路由器使用尾部每端丢弃(TAIL-DROP) 策略来处理拥塞:
若报文到来时队列已满,则丢弃该报文。
所以,路由器丢弃队列的尾部。
(2) 尾部丢弃带来的问题 全局性同步(Global synchronization)
路由器不是丢弃一个连接的n个报文段,而是丢弃n个连接的各一个报文段,造成所有 N 个连接的发送端同时进入慢起动。
慢启动会显著降低流量。
(3) 解决办法
随机丢弃策略 (Random Early Discard、RED)
RED策略
随机丢弃策略 (Random Early Discard、RED)
1)RED 尽可能避免末端丢弃
2)RED 用两个阈值分别表示队列的位置:Tmin 与 Tmax
RED 基本操作:
-若目前队列的报文数小于 Tmin,则把新报文加入队列中。
-若目前队列的报文数大于 Tmax,丢弃新报文。
-若目前队列的报文数在Tmin 与 Tmax之间,根据一概率来随机决定是否丢弃此报文。
(1) Tmin、Tmax 的選擇
1)Tmin 必须大到确保输出链路有高使用率
2)Tmax - Tmin必须大于在一个tcp往返时间内增加的队列大小(例如 Tmax 为 Tmin 的两倍)
(2) p的选择
p不是常数,而随每个报文计算出来的:
1)线性方法 队列报文数< Tmin ,丢弃概率为0;
队列报文数Tmax – 丢弃概率为1
队列报文数介于Tmin 、Tmin 时,p 在 0 和 1 之间线性变化。
若突发性只是短时间的,丢弃报文是不明智的。
线性方式不能处理突发性需求
(2) 利用平均队列长度Avg来确定p
这种策略可以在队列满时p较大;而在突发情况下不会丢弃数据
(3) 加权平均队列大小
avg = (1 – γ) * Old_avg + γ * Current_queue_size
其中 γ 介于 0 和 1 之间。
若 γ足够小 (例如 0.002),平均值会呈长期稳定的趋势 (亦即 old_avg),
从而不受突发通讯的影响。
(4) 修正
以字节为单位来度量队列的大小,
则更合适 较小数据段丢弃的概率比较大数据段丢弃的概率更低
结论:RED的效果在避免拥塞控制,其效果是很好的
6.18 糊涂窗口综合症
当发送端应用进程产生数据很慢、或接收端应用进程处理接收缓冲区数据很慢,或二者兼而有之;就会使应用进程间传送的报文段很小,特别是有效载荷很小。
设想这种情况:
主机 A 接收端的缓存已满,而 A 的交互式的应用进程一次只从缓存中读取一个字符(这样就在缓存产生1个字节的空位子),然后向发送端 B 发送确认,并通知窗口为1个字节(但发送的数据报是40字节长,20字节的IP头+20字节的TCP头), A-->B 。
接着,发送端 B 又发来1个字符(但发来的数据报是41字节长), A<--B 。
接收端A 发回确认, A-->B,仍然通知窗口为1个字节。
这样进行下去,使网络的效率很低。
这种现象就叫糊涂窗口综合症
6.18.1 TCP坚持定时器
坚持定时器的原理是简单的,当TCP服务器收到了客户端的0滑动窗口报文的时候,就启动一个定时器来计时,并在定时器溢出的时候向向客户端查询窗口
是否已经增大,如果得到非零的窗口就重新开始发送数据,如果得到0窗口就再开一个新的定时器准备下一次查询。通过观察可以得知,TCP的坚持定时器使用
1,2,4,8,16……64秒这样的普通指数退避序列来作为每一次的溢出时间。
6.18.2 糊涂窗口综合症
TCP的窗口协议,会引起一种通常叫做糊涂窗口综合症的问题,具体表现为,当客户端通告一个小的非零窗口时,服务器立刻发送小数据给客户端并充满其缓冲区,一来二去就会让网络中充满小TCP数据报,从而影响网络利用率。
6.18.3 糊涂窗口综合症的避免
对于发送方和接收端的这种糊涂行为。TCP给出了一些建议(或者是规定)。
· 接收方不通告小窗口。通常的算法是接收方不通告一个比当前窗口大的窗口(可以为0),除非窗口可以增加一个报文段大小(也就是将要接收的MSS)或者可以增加接收方缓存空间的一半,不论实际有多少。
· 发送方避免出现糊涂窗口综合症的措施是只有以下条件之一满足时才发送数据: ( a )可以发送一个满长度的报文段; ( b )可以发送至少是接收方通告窗口大小一半的报文段; ( c )可以发送任何数据并且不希望接收ACK(也就是说,我们没有还未被确认的数据)或者该连接上
不能使用Nagle算法。
ok,现在我们回忆一下,可以发现TCP的很多规定都是为了在一次传送中发送尽量多的数据,例如捎带ACK数据报文的策略,Nagle算法,重传时发送包含原数据报文的策略,等等。
七、小结
网络最本质的活动是实现分布在不同地理位置的主机之间的进程通信;
传输层的主要功能就是为网络环境中分布式进程通信提供服务;
网络中应用程序进程间相互作用的模式是客户/服务器(client/server)模式;
Internet传输层采用了TCP协议与UDP协议;
TCP是一种面向连接的、可靠的传输层协议,它在网络层IP服务的基础上,向应用层提供面向连接、可靠的流传输;
UDP是一种无连接的、不可靠的传输层协议。