第5章 运输层
5.1 运输层协议概述
5.1.1 进程之间的通信
5.1.2 运输层的两个主要协议
5.1.3 运输层的端口
5.2 用户数据报协议UDP
5.2.1 UDP概述
5.2.2 UDP的首部格式
5.3 传输控制协议TCP概述
5.3.1 TCP最主要的特点
5.3.2 TCP的连接
5.4 可靠传输的工作原理
5.4.1 停止等待协议
5.4.2 连续ARQ协议
本节要点如下:
运输层向它上面的应用层提供通信服务,它属于面向通信部分的最高层,同时也是用户功能中的最低层。
当网络的边缘部分中的两台主机使用网络的核心部分的功能进行端到端的通信时,只有主机的协议栈才有运输层,而网络核心部分中的路由器在转发分组时只会用到低三层的功能。
注:这一说法与1.7.5节讲述网络体系结构时给出的路由器连接TCP/IP网络的工作原理图是一致的。
运输层实现主机间进程也就是端到端的通信:IP数据报的首部明确地标志了通信的两台主机的IP地址,但真正进行通信的实体是主机中的进程,IP协议虽然能把分组送到目的主机,但是这个分组还停留在主机的网络层而没有交付主机中的应用进程。因此,我们需要在网络体系结构的网络层之上设计一个新的层,来支持主机进程间的通信,这个新的层就是运输层。这样的表述也就意味着,通信的真正端点并不是主机而是主机中的进程,因此运输层也被认为是实现端到端通信的网络体系结构层。
运输层的复用与分用功能:由于一台机器必须支持多个进程同时的运行和通信,运输层需要有一个很重要的功能,即复用(multiplexing)和分用(demultiplexing)功能。
在发送端,“复用”功能允许不同的应用进程都可以使用同一个运输层协议传送数据;
而在接收端,“分用”功能使得同一个运输层协议能够将收到的报文分发到对应的应用进程;
注:复用和分用针对的是同一个运输层协议,不同的运输层协议各自实现自己的复用和分用功能。
下图示意了运输层的两个协议,TCP和UDP,的复用和分用。
运输层提供应用进程间的逻辑通信:“逻辑通信”的意思是,从应用层来看,只要把应用层报文交给下面的运输层,运输层就负责把该报文传送到对方的运输层,好像这种通信就是两端的运输层沿水平方向直接传送数据。但事实上这两个运输层之间并没有一条水平方向的物理连接。
这种逻辑通信示意如下图。图中表明,应用进程“感觉”数据的传送是两端运输层协议直接完成的,但真正的数据传输是沿着图中的虚线方向进行的。
注:图中还示出了IP协议和运输层协议TCP和UDP的作用范围。
传输层对数据部分就进行了差错检测 所以好像不需要网络层对数据部分差错检测 只用检查ip数据报的首部就可以
TCP/IP运输层包括两个主要的协议,即:
用户数据报协议UDP (User Datagram Protocol) [RFC 768]
传输控制协议TCP (Transmission Control Protocol) [RFC 793]
注:关于这两个协议后面将详细介绍。
本节要点如下:
使用端口标识进程:运输层需要通过复用和分用实现多个应用进程间的通信,那么我们就需要一种方法来标识进程。这个方法称为协议端口号(protocol port number),或简称为端口(port)。端口号也可以理解为是运输层的“地址”。
注:之所以称为“协议”端口号,是因为通常一种应用进程会对应一个应用层协议。
端口号是16位的:TCP和UDP采用了二进制16位的端口号,其十进制范围是0~65535,十六进制范围是0x0000~0xFFFF。
端口号的分类:
服务器端使用的端口号:服务器端实现的一个应用层协议需要公开声明其端口号,以便客户端访问,它又分为两种情况,
熟知端口号(well-known port number)或系统端口号,它是数值范围为0~1023的端口号。
它们是由IANA(The Internet Assigned Numbers Authority,互联网数字分配机构)指派的一些典型的应用层协议,如表5-2所示。下图给出了形象的示意:
注:TCP与UDP协议各自有独立的端口号。
登记端口号,即数值范围在1024~49151之间的端口号。这类端口号是为没有熟知端口号的应用程序使用的。
注:尽管使用这类端口号的标准方法是在IANA按照规定的手续登记,以防止重复,但实际也不尽然。许多用户经常自行配置使用。
客户端使用的端口号:其数值范围为49152~65535。 这类端口号在客户进程运行时自动动态选择,因此又叫做短暂端口号。
当服务器进程收到客户进程的TCP/UDP报文时,就能从报文的首部的源端口号获知客户端进程所使用的端口号,因而就能以该源端口号构造应答报文的目的端口号,就如同以收到IP地址中的源IP地址作为应答报文的目的IP地址一样。
常见端口号:
用户数据报协议UDP (User Datagram Protocol)仅提供了最小功能的运输层服务,包括:
复用和分用:通过端口号实现到使用UDP协议的应用层进程或协议的复用和分用。
差错检测:实现对包括首部和数据部分的整个报文的差错检测,这一举措增强了数据的可靠性验证,因为IP层只针对IP首部进行差错检测。
由于UDP只提供了上述最小功能的运输层服务,使得它具有如下特点:
UDP是无连接的:即发送数据之前不需要建立连接(当然,发送数据结束时也没有连接可释放),因此减少了开销和发送数据之前的时延。
UDP使用尽最大努力交付:即不保证可靠交付,也就是说它只是保持了IP的尽最大努力交付服务,没有进一步的交付保证。
UDP是面向报文的:发送方的UDP对应用程序交下来的报文,在添加首部后就向下交付IP层。UDP对应用层交下来的报文,既不合并也不拆分,而是保留这些报文的数据边界。在接收方,UDP对IP层交上来的UDP用户数据报,在去除首部后就原封不动地交付到上层的应用进程。因此,应用程序必须选择合适大小的报文,以尽量确保对应的IP数据报可以封装到一个数据链路层的帧中,从而避免IP层在传送时的分片和重装,以使IP层具有较高的效率。
UDP没有拥塞控制:因此网络出现的拥塞不会使源主机的发送速率降低。这一特点使得它适合于要求源主机以恒定速率发送数据的实时应用(如语音通信、实时视频会议等),这些应用允许在网络发生拥塞时丢失一些数据,但却不允许数据有太大的时延。所以很适合一些实时应用。所以有时候丢一点是可以的 所以传输不可靠啊
UDP的首部开销小:UDP首部只有8个字节,比TCP的20个字节的首部要短得多,因而使用UDP传输开销很小,可以获得更好的网络吞吐量。
UDP报文的首部格式如下图所示,它只包括4个字段,共8个字节。
其中的长度为包括首部和数据的整个报文的长度,最小值是8,即没有数据的情况。
|
应用层给udp多长的报文 udp就保留边界加个首部封装罢了

如果这个报文长度过大 下行到数据链路层有MTU要求 这样网络层不得不分片重装 而如果长度过小 首部就比报文相对更大 这样会影响传输效率 所以报文长度要适当
UDP数据报使用了与IP首部相同的差错检测算法,即反码算术运算差错检测算法。
但要注意的是,UDP在计算差错检测的检验和时,增加了伪首部数据,如下图所示:

注意1:
伪首部只用于进行差错检测计算,不在网络中传输。
伪首部共有12个字节,其中
源和目的IP分别占4字节。
最后的4个字节中,
第1个字节为0,
第2个字节为IP中的UDP协议号,即十进制的17,
第3-4字节为UDP报文的总长度,它与UDP报文首部中的长度值相同。
注意2:如果UDP的数据部分为奇数个字节,则在计算检验和时需要在最后填充一个值为0的字节。因为所使用的反码算术运算差错检测算法要求以2字节为单位进行计算。
如,在下面的例子中,UDP数据报的长度是15字节,因而数据部分有7个字节,于是在计算检验和时就需要在最后填充一个0字节。

发送端和接收端是不一样的 接收端就得知发送端填充的校验和了 所以要把本来全为0的校验和也参与运算
TCP在实现基本的运输层功能,即复用/分用和差错检测的基础上,实现了一些非常重要的功能,这使它成为了一个极其强大的运输层协议。
我们可以用一句话较全面地概括TCP的特点:TCP是一个可靠的、面向连接的、带有流量控制和拥塞控制的全双工运输层协议。
相对于UDP数据报面向报文的特点,TCP是面向字节流的。
下面的两个图解释了TCP面向字节流的特点:
在发送端,TCP提供了发送缓存来暂存应用进程交付的数据,TCP按照自己的策略对发送缓存中的数据构造TCP报文向网络层交付,这使得TCP发送的数据块与应用层交付的数据块相互独立。
在接收端,TCP提供了接收缓存来暂存TCP收到的数据,接收端应用进程从TCP接收缓存获取数据的分块和速率也与TCP接收数据块的大小和速率相互独立。
![]() |
![]() |
一个TCP连接涉及到通信的两个端点,如下图所示:

TCP连接的一端由端系统的IP地址和端口号唯一确定,这两个数字合起来常称为套接字 (socket) 或插口,常以如下方式表示:
套接字 socket ::= (IP地址 : 端口号) ,如:套接字 socket = (200.200.200.200 : 80)
这样一个TCP连接就可以由两个套接字唯一地表示:
TCP 连接 ::= {socket1, socket2} = {(IP1: port1),(IP2: port2)}
TCP 连接 = {(100.100.100.100: 49152),(200.200.200.200 : 80)}
注:上述的符号“::=”意为“定义为”。
假如传输信道是理想的,即满足如下条件:
信道不产生差错。
不管发送方以多快的速度发送数据,接收方总是来得及处理收到的数据。
在这样的理想信道条件下,不需要采取任何措施就能够实现可靠传输。
然而,实际的传输信道不可能具备以上两个理想条件。IP层仅提供“尽力传递”服务,也就是说,IP层不能保证报文正确、及时、按序地交付到目的主机,也就是IP层不能保证可靠传输。尽管像视频、语音类的通信允许存在一定程度的传输差错或丢包,但是,我们绝大部分时候却需要可靠传输。
于是,我们需要对TCP协议进行聪明的设计,以实现在不可靠IP层上的可靠传输层服务。

可靠传输的基本思想是:确认+重传。
即,发送端对于每个发送的报文都需要接收端发回确认报文,如果在给定的时间内没有收到确认报文,则即重传该报文。
要实现“确认+重传”的可靠传输,需要一些必须的辅助要素,包括:报文的缓存、报文的编号、以及超时计时器等。
我们先用简单的停止等待(stop-and-wait)协议,简称为停等协议,来说明可靠传输的基本思想。
停等协议有如其名:发送端每发送完一个报文,就停下来等待接收端的确认,如果接收端确认到了,再发送下一个报文。
注:确认报文简称为ACK,来自于确认英文单词acknoledgement的前3个字母。
如果停等协议正常执行,则情况将如下图所示:
![]() |
注:我们遵循了计算机中编号从0开始的规范,但要到稍后才会说明为什么要对报文编号。
然而,传输的分组可能会出现两个问题:
出现差错,即接收端收到了分组,但是差错检测检验未通过;
传输的分组在途中丢失,未到达接收端。
在这两种情况下,接收端都不可能向发送端发回ACK。
发送端需要采取机制,判断接收端没有及时发回ACK,这种机制就是超时计时器(time-out timer)。
发送端发送出去报文后,就启动一个超时计时器(通常为倒计时计时器),如果超时计时器时间到了(即减到0),发送端没有收到ACK,则发送端就重传此前发送的分组。如下图所示。
![]() |
注:超时计时器的时间通常取为比往返时间RTT稍大一些的时间,这样就可以保证在ACK报文正常到达前不会重传。其实超时计时器时间的选取是一个很复杂的问题,我们将在5.6.2节介绍一种自适应的TCP超时计时器选取方法。
而为了能够重传,发送端发送出去报文后,就不能将该报文清除,而是要将该报文缓存起来,以便重传。
还有一种发送端收不到ACK的情况,即接收端正常收到了报文,且正常发送了ACK,但是ACK在传输过程中丢失了。这种情况下,发送端也将会因为超时计时器时间到而进行报文重传。如下图所示。
![]() |
然而,这种情况下,接收端会收到重复的报文。要使传输可靠,接收端就应该有机制判断出来一个报文是重复的报文还是新的报文。这种机制就是报文编号。有了报文编号,接收端就可以通过编号判断出收到的报文是新的还是重复的,如果是重复的,它就将该报文抛弃,但还是要发出确认号,因为发送端发送了重复报文后,会继续等待对重复报文的确认。
对于停等协议来说,报文编号只要使用一位二进制数字,即交替使用0号报文和1号报文,就可以了。
上面的ACK机制是接收端收到什么编号的报文,就对什么编号的报文进行确认。实际可靠传输协议中使用了更为科学的ACK确认号机制,即确认报文中携带“期望接下来收到的报文序号(expected datagram sequence number)”。在该机制下,接收端会维护一个期望接下来要收到的报文编号,当该编号的报文收到后,它就更新期望的编号,并以ACK报文告诉发送端接下来该发送的报文号。
在以期望收到的报文序号为确认号的机制下,接收端需要设置一个期望接收的报文编号的状态。在此机制下,前述的各种情况可重述如下:
正常情况
收到有差错报文的情况
报文丢失的情况
确认丢失的情况
下面我们专门研究确认迟到的情况,即某个较早时刻的确认报文在较晚时刻确认报文之后到达发送端的情况。
经过分析,确认迟到共有三种情况:
情况1:先于相同编号的确认报文到达,这时会提前发送期望的报文,但协议仍然会正确工作,即使相同编号的确认报文丢失,协议也会正确工作。
情况2:先于不同编号的确认报文到达,这时只要抛弃迟到的确认,上述协议仍然会正确工作。
情况3:后续相同编号的报文丢失,但对应编号的早期的确认报文却延迟到达,这种情况下协议会失败。
注:后面我们将会看到,TCP的可靠传输实现中使用了32位的报文首字节数据编号,由于32位编号空间非常大,使得发生上述第3种情况成为不可能,因而避免了协议的失败。
上述的停等协议设计完成后就会在通信的两端自动运行,即重传的请求是自动进行的,因而这种可靠传输协议常称为自动重传请求ARQ (Automatic Repeat reQuest)协议。
直觉告诉我们,停等协议是一个效率很低的协议。
这种低效率我们可以用形式化的方法表达一下。
我们将前述的图横过来画成下面的样子,并为各部分标上时间符号,就可以得到如下所示的信道利用率公式:
![]() |
上述公式中,为报文的发送时延,RTT为发送端和接收端间的往返时间,
为确认报文的发送时延。
利用率计算公式的由来是:在的时间里只有
的时间里在传输有效的数据,因而信道利用率就是
和
之间的比值。
教材P216页上部给出了信道利用率的实际计算例子,同学们要认真学习一下,该例子还将作为Excel作业要求同学们进行练习。
为解决停等协议的效率问题,人们提出了流水线式的传输方式。
其基本思路是:发送方连续地发送报文,接收方每收到一个报文就发回一个确认,如果报文没有差错、不丢失、确认也不丢失、确认也不延迟到达,则这种方式就会有形如下图的传输过程。

显然,在这种理想状态下,信道利用率可以达到100%。
然而,这种理想情况几乎是不存在的:报文可能会出现差错、可能会丢失、确认也可能会丢失、确认也可能会延迟到达。
为此,可行的流水线传输方式还需要进行仔细的设计,以解决这些情况。下面的连续ARQ协议就是一个较实际也是较好的解决方案。
本节要点如下:
滑动窗口协议和连续ARQ协议之间的关系:连续ARQ协议以滑动窗口协议为基础。
滑动窗口协议:滑动窗口协议的关键是在发送端设置一个待发送报文的窗口,窗口内的报文可以以流水线方式连续地发送出去,而不必等待单个报文的确认。如下图所示。
当第1个报文的确认到达后,窗口就向前滑动一个报文,这样进入窗口的新的报文就可以发送出去,不必等到它前面报文确认的到来。
注:为提高效率,接收端常采用累积确认的方式,即收到一个报文后不是立即发送确认,而是等待一小段时间,这样如果收到连续到达的报文,就只发送最后一个报文的确认,接收端据此判断此前的报文均已全部收到。上面的图(c)就示意了这种情况,接收端发回对4号报文的确认,发送端据此判断,4号报文及其前面的报文均已收到,于是将窗口向前滑动3个报文,从而使得7、8、9号报文进入窗口,因而可以连续地发送出去。
连续ARQ协议:连续ARQ协议指的是基于滑动窗口协议的自动重传请求协议,即在滑动窗口协议的基础上,当出现报文差错、报文丢失、确认丢失等情况时,实现自动重传的机制。
GBN协议:连续ARQ中的一种协议是GBN(Go-Back-N, 回退N)协议。首先连续ARQ为每个发出的报文设置超时计时器,当遇到报文差错、报文丢失、确认丢失时,该报文相应的超时计时器就会超时,GBN的处理方法是:重新将窗口中出现超时的报文到窗口最后的报文全部依次重传。
第5章 运输层
5.5 TCP报文段的首部格式
5.6 TCP可靠传输的实现
5.6.1 以字节为单位的滑动窗口
5.6.2 超时重传时间的选择
5.6.3 选择确认SACK
TCP的PDU称为报文段(segment),其格式如下图所示:

教材P217-221页对TCP报文段进行了全面和详细的解释,同学们应仔细阅读以掌握。
下面对给出一些补充性的要点解释:
TCP报文段首部也分成固定部分和可变部分。其中固定部分为20字节,可变部分长度在0~40字节之间,且必须是4的整数倍。
关于序号:
TCP报文段序号是其中的数据在整个发送过程中的字节顺序号,这一点与此前介绍可靠传输原理时将整个报文进行顺序编号是不同的,但不会影响可靠传输机制。这一点也呼应了此前关于TCP协议是面向字节流的说法。
报文段序号占4字节,因而序号范围是,即0~4,294,967,295,共
(大约
)个编号。
当TCP建立连接时,通信的两端会各自在范围内随机选择一个序号作为本次通信的第一个字节的序号。
此后,TCP循环使用之间的编号,为实现此目的,TCP使用MOD
的运算计算下一个报文的序号。
注:MOD运算为取模运算,简单的理解即是针对某个除数的除法运算的余数。一般地,MOD N运算的结果范围是0~N-1。因此MOD 运算的结果范围将会是0~
。
假设当前TCP报文的编号(即数据部分第1个字节的编号)为,当前报文共有
字节的数据,则下一个报文的编号将会是:
。
例如:假设发送端A当前TCP报文段序号为
,该报文有
字节,则下一个报文段
的序号为
;假设
中含有13个字节,则下一个报文段
的序号将会是
。
关于确认号:
首先确认号是对已经接收到的对方报文(即数据)的确认,因而确认号是与对方的序号有关的,而序号是本方的报文序号,鉴于通信的双方各自取随机的0~之间的数作为初始的报文序号,这两个序号从数值上是没有任何关系的。
只有当下面的控制位中的ACK=1时,报文中的确认号才有效。
确认号可以在正常发送数据时携带,不一定单独发送,这种方法叫做捎带确认(piggy-back),显然捎带确认可以提高传输的效率。
TCP确认号也是采用“期望接下来接收的报文序号”(expected sequence number)机制。因此针对上面的例子,接收端B收到A发送来的报文后,发送的确认号将是
,语义是“我接下来希望接收序号为4,294,967,292的报文”。
TCP的确认也将是累积的。如在上述的例子中,接收端B收到报文后,可能不立即发送确认,而是等收到
后再发送确认,这时的确认号就会是
。发送方收到确认号为10的报文后,就会认为所发送的编号
到
之间的25个数据字节均被发送方正常收到了,接下来可以从编号为
的字节开始发送数据。
由于4字节0~的编号范围足够的大,不用担心因编号绕回而带来的协议错误。
关于数据偏移:
“数据偏移”的直观意思是报文中数据开始的位置,而数据开始前的部分为报文的首部,因而“数据偏移”也就是首部长度。
“数据偏移”与IP数据报中的“首部长度”非常相似,都是4个二进制数据位,都是以4字节为单位的。因而首部只有固定部分的20字节时,数据偏移的值将会是5(0101)。由于数据偏移的最大值是1111,即15,因而首部最多可以有60个字节,即首部的可变部分最多可以有40个字节。又因为数据偏移是以4字节为单位的,可变部分如果有,长度必须是4的整数倍,如果实际不是4的整数倍,则需要填充到4的整数倍。
关于控制位:报文结构中列出了6个控制位,其中特别需要掌握的控制位如下,
ACK(ACKnoldegement,确认)控制位:用来说明确认号有效的;
SYN(Synchronization,同步)控制位:用来建立连接的;
FIN(FINish,结束)控制位:用来关闭(也称为释放)连接的;
RST(ReSeT,重置)控制位:用来重置连接的,也能关闭连接。
关于窗口:窗口是用于进行流量控制的,我们将在5.7节流量控制中详细解释。
关于检验和:
检验和是用来对整个报文进行差错检测的;
使用的算法与IP数据报首部和UDP相同,即反码算术运算差错检测算法;
与UDP检验和计算一样,在计算检验和时,也要给报文加上12字节的伪首部。 第四个字段协议号为6 而UDP的是17
TCP协议允许使用报文段首部的可变部分来为通信设置选项。
TCP的选项分成两类:
一类是单字节的,该字节中的值说明选项的类型(kind)。
另一类是多字节的,它由1字节的类型、1字节的总选项长度和0或多字节的数据部分组成,如下图所示。参见TCP Options。
常见的TCP选项:
Kind=0:选项列表结束(End of option list)选项。(注:该选项实际中几乎没有实现和使用)。
Kind=1:无操作(No-Operation)选项,用于填充以保证4字节边界。
Kind=2:最大报文段长度(Maximum Segment Size, MSS)选项,用于设定TCP报文段的最大数据量,对于数据链路层为以太网的情况,该值通常为1460(0x05B4)。
Kind=3:窗口比例因子(Window Scale Factor)选项。报文段中用于进行流量控制的“窗口”项是一个2字节16位的值,数值范围为0~,即最大不超过64K字节。要使用更大的窗口就需要设置窗口比例因子选项,该选项给出了窗口数值向左移位的量,即实际窗口的大小为“窗口”中值乘以
,其中f为窗口比例因子的数值。标准规定,f应取0~14之间的值。

Kind=4:允许选择重传(SACK Permitted)选项。此选项将在5.6节TCP可靠传输中说明。
Kind=5:选择重传(Selective ACK,SACK)选项。此选项将在5.6节TCP可靠传输中说明。
Kind=8:时间戳(Time Stamp)选项。这是一个10字节的选项,用于实现如下两项功能:
用来计算往返时间RTT。发送方在发送报文段时把当前时钟的时间值放入时间戳(TimeStamp)字段,接收方在确认该报文段时把时间戳字段值复制到时间戳回送回答(TimeStamp Echo Reply)字段。这样,发送方在收到确认报文后,就可以准确地计算出RTT来。
用于处理TCP序号超过 的情况,这又称为防止序号绕回PAWS (Protect Against Wrapped Sequence numbers)。 前已述及,TCP 报文段的序设计为4字节32位的数,一个序号在发送完
字节的数据后,就会被重复,这在速率不是很高的网络中不成问题,例如对于1.5Mbps的网络,序号重复需要6小时以上,但是对于高速的2.5Gbps的网络,则不到14秒钟就会重复。为了使接收方能够把新的报文段和迟到很久的报文段区分开,可以通过在报文段中增加时间戳选项的方法来辅助。

本节讨论TCP协议中可靠传输的具体实现方法。为方便讨论,假定数据传输只在一个方向进行,即A向B发送数据,而B发回确认。这样就可以将问题简化为只考虑两个窗口,即A的发送窗口和B的接收窗口。
注意1:这里说的以字节为单位的滑动窗口并不是说TCP一次一个字节地发送数据,而是说TCP的报文以其中数据第一个字节的序号进行编号,而不是以整个报文为单位进行编号。如,当前报文的编号为100,说明当前报文的数据的第一个字节的编号是100,如果该报文中包含50个字节的数据,则下一个报文的编号将会是100+50=150,而不是100+1=101,因为其中第一个数据字节的编号为150。
注意2:教材以类似如下的图示说明TCP的以字节为单位的滑动窗口,
但同学们不要被此误导:
首先不要认为发送端将31-41之间的数据一个字节一个字节地发送出去,尽管有时一个TCP报文段会仅携带一个字节的数据;
其次不要认为发送端用一个报文将31-41之间的数据发送出去,而接收端可能会收到其中的31号、34号、35号字节,而丢失32号、33号字节。
正确的理解是:发送端将数据组织到TCP报文段中,一个报文段一个报文段地发送,而接收端或者完整地收到一个报文段,或者完全收不到一个报文段,特别是可能不会连续地收到报文段。
为了更加清楚正确地表达TCP以字节为单位的滑动窗口,我们将上面的图示进行了一些调整,并以如下所示的要点描述TCP的以字节为单位的滑动窗口机制:
假设以A作为发送端、B作为接收端建立了TCP连接,在连接建立阶段,B向A报告的窗口大小为20字节,则A端建立20字节的发送缓存,B端建立20字节的接收缓存。如下图所示。A端基于连续ARQ协议,连续地发送了4个报文段,分别是包括第31、32共2字节的31号报文段,包括第33~35共3字节的33号报文段,包括第36~39共4字节的36号报文段,和包括第40、41共2字节的40号报文段。发送了这些报文后,根据连续ARQ协议,A端会为每个报文启动一个超时计时器。而且,A端的发送窗口变为以三个参量描述的状态,其中,
~
(不含
)为整个发送窗口,
~
(不含
)间为已发送出去但正在等待确认的数据,而
~
(不含
)间为可以发送的数据。

假设B收到了第31号报文,则因为该报文包括两个字节,它会将接收窗口向前移动2个字节,并且向A发回确认,确认号为33,表示它希望接下来接收第33号字节开始的数据。
A收到B发来的33号确认后,就会认为33号字节之前的数据(不包括33号)均已正常收到,于是,它将发送窗口向前移动两个字节,使第51、52号字节的数据成为可以发送出去的数据。同时将变量的值均增加2,但保持
的值不变(假定没有再发送TCP报文段)。

假设,在下一时刻,B端接收到了不连续的36号报文,则它将保持接收窗口不变,但要向A发送对编号33的确认,注意不是对编号40的确认,以告知发送方33号报文仍然未收到。
A端收到对33号的重复确认时(注意上面的第2步中A端已经接收到过一次对33号报文的确认),或者33号报文的超时计时器时间到了时,会立即重发33号报文。
B端收到A端重发的33号报文后,会根据累积确认的规则,向A端发送对40号报文的确认,同时将当前接收窗口的左边界移动到40的位置上。
A端收到对40号报文的确认后,也将当前发送窗口的左边界移动到40的位置上。
TCP要实现可靠通信,需要在两端设置缓存与窗口,在这里我们说明缓存与窗口的关系。
缓存是在TCP连接建立时确定的,它是计算机存储器中一段固定的存储空间,在一次TCP连接有效的期间内不会变化。而窗口则是缓存中的一部分,它是滑动的,在TCP运行期间会频繁地变化。
特别需要注意的是:缓存和窗口都是以循环方式使用的,而实现循环的方法就是前述的求模运算(即MOD运算)。
下图示出了发送缓存与发送窗口的一般情况。
![]() |
图中,~
之间为发送缓存,它是内存中一块固定的存储空间。
~
之间为发送窗口,
也是最新的确认号;
~
之间为已经发送但等待确认的数据;
~
之间为可以发送但尚未发送的数据;
为发送端应用进程写入发送缓存的最后一个字节。
注意:的位置也可能会出现在
~
之间,表示发送方应用进程写入的数据不满发送窗口的情况,这时可以发送但尚未发送的数据就只能是
~
之间的数据。
发送缓存中的数据与发送窗口也可能会出现在发送缓存中绕回的情况,如下图所示。
![]() |
这时,尽管的数值比
都大,
~
之间仍然为发送窗口,但要注意是绕回意义上的发送窗口,
仍然是最新的确认号;
~
之间仍然为已经发送但等待确认的数据;
~
之间仍然为可以发送但尚未发送的数据;
仍然为发送端应用进程写入发送缓存的最后一个字节。
注:处理“绕回”问题的计算方法是求模运算(即MOD运算)。
下图示出了接收缓存与接收窗口的一般情况。
![]() |
图中,~
之间为接收缓存,它是内存中一块固定的存储空间。
~
之间为接收窗口;
为接收应用进程接下来要取的数据;
~
之间为按序收到的已经确认的数据;
是最新的确认号;
为未按序到达的数据。
接收缓存中的数据与接收窗口也可能会出现在接收缓存中绕回的情况,如下图所示。
![]() |
这时,尽管的数值比
大,
~
之间仍然为接收窗口,但要注意是绕回意义上的接收窗口,
仍然是最新的确认号;
为接收应用进程接下来要取的数据;
~
之间为按序收到的已经确认的数据;
为未按序到达的数据。
注:处理“绕回”问题的计算方法是求模运算(即MOD运算)。
TCP的发送方在规定的时间内没有收到确认就会因为超时而重传已发送的报文段。这种重传的概念是很简单的,但重传的超时时间的选择却是TCP最复杂的问题之一。这是因为:
如果把超时重传时间设置得太短,就会引起很多报文段的不必要重传,使网络负荷增大。
但若把超时重传时间设置得过长,则又会使网络的空闲时间增大,引致传输效率的降低。
一个比较合理的选择是使超时时间比通信两端的往返时间RTT稍大一些。然而要将这个想法落地还需要解决很实际的问题:
RTT是一个随机性很大的量,因为TCP下面的IP互联网层是一个尽力传递的服务层,携带TCP报文段的IP数据报可能只经过一个高速率的局域网,也可能经过多个低速率的网络,并且每个IP数据报所选择的路由很可能不同,这就会造成RTT是一个随时间不断变化甚至变化很大的量,如下图中的Sample-RTT(即样本RTT)所示。
为避免直接使用样本RTT而带来的超时计时器时间“过山车”式的剧烈变化,我们需要有一种平滑机制来获得依赖于样本RTT但又平缓变化的RTT估计,如上图中的Estimated RTT所示。
“使超时时间比通信两端的往返时间RTT稍大一些”是一个很好的注意,但大多少呢?这也需要有切实可行的计算方法。
要获得RTT估计,首先需要获得样本RTT,或瞬时RTT,这是通过测量一个TCP报文段从发出到收到确认的时间间隔来获得的。
为了获得平滑的RTT估计,TCP采用了一种加权平均计算方法。该计算方法包括三个组成部分:
以滑动加权平均法计算RTT的估计值。该方法包括两个步骤:
开始时刻测量一次样本RTT值(通常是建立连接的握手阶段测量的值),并直接用
作为首次的估计值
,即:
此后每当测量一次RTT的样本值,就用下式计算RTT的估计值:,
说明:
与教材不同,我们用RTTS表示样本RTT,其中S来自于样本的英文Sample;我们用RTTE表示估计的RTT,其中E来自于估计的英文Estimate。
当时,估计的RTT由上一轮估计的RTT和本轮测量得到的RTT以因子
加权平均得到。RFC 6298推荐的
值为
,即0.125,也就是估计的RTT更多地依赖于上一轮估计的RTT,测量的样本RTT在估计的RTT中占比很低,因而估计的RTT会比样本RTT要平滑得多。
以RTT估计值RTTE和RTT偏差的估计值RTTDE计算重传超时时间RTO。RFC 6298建议以下式计算重传超时时间RTO:
确切地说应该是:,
即,RTO的值取比估计的RTT值大出4倍的RTT偏差估计值。
RTT的偏差RTTD(字母D来自于偏差的英文Deviation)也以滑动加权平均法计算估计值。该方法同样包括两个步骤:
开始时刻测量一次样本RTT值(通常是建立连接的握手阶段测量的值),并用
的一半作为RTTD的首次估计值
,即:
此后每当测量一次RTT的样本值,就用下式计算RTTD的估计值:,
其中为绝对值。
说明:推荐的值为
,即0.25,也就是估计的RTTD更多地依赖于上一轮估计的RTTD,样本偏差
在估计的RTTDE中占比很低,因而估计的RTTDE会比样本偏差要平滑得多。
重传报文RTT测量可能不准确的问题:在5.4节可靠传输原理中曾经介绍过,重传报文可能会出现确认迟到问题,如下图所示。图中A端收到第一个ACK1时,无法判断是首次发送还是重传的的ACK,这就造成了该次ACK测量的不准确问题。

下图从另一个角度说明了以重传报文的ACK测量RTT所带来的问题。

Karn针对此问题提出了一个想法,称为Karn算法:在计算加权平均的RTT时,只要报文段重传.了,就不采用其往返时间样本。这样得出的加权平均RTTE和RTO就去除了重传报文ACK的不确定性问题,使得计算更准确了一些。
Karn算法带来的问题:然而,此Karn算法又会引起新的问题。如果因网络拥塞的原因导致报文段的时延突然增大了很多,则在原来得出的重传时间内,不会收到确认报文段。于是就重传报文段。但根据Karn算法,不会考虑重传报文段的往返时间样本。这样,超时重传时间就无法及时更新以适应网络的变化,导致过多的重传,从而降低了网络的效率和吞吐量。
为此,提出了Karm算法的修正算法:报文段每重传一次,就把超时重传时间RTO增大一些。典型的做法是取新的重传时间为旧的重传时间的2倍。当不再发生报文段的重传时,再根据上面给出的滑动加权平均法计算超时重传时间。实践证明,这种策略较为合理和有效。
前已述及,TCP接收端在收到不连续的报文段时,会将不连续的报文段保存下来,并发送迄今为止连续收到字节流的下一个期望接收到的字节号。
如下图所示,当36号报文早于33号报文收到时,接收端会将36号报文保存下来,但发送对33号报文的确认。
![]() |
一个更具代表性的情况如下图所示,当前连续收到数据的下一个字节号是1001,但后面又收到了不连续的1501~3000和3501~4500字节,按照前述的确认发送机制,收到1501号和3501号报文后,都要发送对1001的确认,那么发送端收到对1001的重复确认后,接下来该怎样发送呢?
如果按照前述的GBN策略,则发送端要从1001开始重传后面所有已经发送过的数据。然而这存在传输效率问题,因为1501号和3501号报文已经被接收端收到了。
![]() |
TCP以SACK(Selective ACK,选择确认)机制为这一问题提供了很好的解决方案。
TCP的SACK机制分成两个组成部分:
在TCP连接建立阶段,双方使用Kind=4的允许选择重传(SACK Permitted)选项协商双方启用SACK机制,该选项只有类型Kind和长度Length2个字段共2个字节,没有数据部分。
在数据传输阶段,TCP使用下图所示的Kind=5的SACK选项报告未按序收到的数据块。该选项中的数据部分以块首字节号、块尾字节号的方式报告接收方未按序收到的连续数据块,其中块首字节号和块尾字节号均为4字节长,即每个块要占8个字节。由于TCP选项部分最多40个字节,因而一个TCP报文段的SACK选项最多可以说明4个数据块,这时该SACK选项的长度将是34字节。
例:当接收端收到如下图所示的不连续的数据块时,它发回的TCP报文段的确认号将会是1001(ACK控制位置1)。其SACK选项将是18字节长,其中包括两个已收到的不连续数据块的说明,分别对应首尾字节号1501~3000和3501~4500。
![]() |
原文:https://www.cnblogs.com/ranzhong/p/13055230.html