大纲要求核心考点传输层提供的服务传输层的功能传输层寻址与端口端口的基本概念端口号套接字无连接服务和面向连接服务UDP 的主要特点TCP 的主要特点UDPUDP 的数据报UDP 的基本概念UDP 数据报的组成UDP 校验TCPTCP 报文段TCP 的连接管理TCP 可靠传输TCP 数据编号与确认TCP 的重传机制TCP 流量控制TCP 拥塞控制的基本概念拥塞控制的 4 种算法慢开始算法的原理拥塞避免算法的原理快重传算法快恢复算法
大纲要求
(一)传输层提供的服务
- 传输层的功能
- 传输层寻址与端口
- 无连接服务与面向连接服务
(二)UDP
- UDP 数据报
- UDP 校验
(三)TCP
- TCP 报文段
- TCP 连接管理
- TCP 可靠传输
- TCP 流量控制与拥塞控制
核心考点
- (★★★★★)TCP 的流量控制和拥塞控制机制
- (★★★★)TCP 的连接和释放
- (★★★)TCP 报文格式、UDP 数据报格式
传输层提供的服务
传输层的功能
从通信和信息处理的角度看,传输层是 5 层参考模型中的第 4 层,它向上面的应用层提供通信服务。它属于面向通信部分的最高层,同时也是用户功能中的最低层。
传输层为两台主机提供了应用进程之间的通信,又称为端到端通值。由于网络层协议是不可靠的,会使分组丢失、失序和重复等,所以派出传输层为数据传输提供可靠的服务,如图 5-1 所示。
在图 5-1 中,整个主机可以看成是一栋寝室楼,而端口指出的那个小方块就可以看成是一个寝室,其中是一个个应用进程。

可能疑问点∶很多考生会问,既然传输层的 UDP 是不可靠的,为什么又说传输层提供可靠的服务?
解析∶某一层是否是可靠的,确实取决于这一层使用的是什么协议。例如,以前说数据链路层可靠,是因为链路层使用了 HDLC 协议。而现在因为一般在链路上传输不会出现错误,所以淘汰了 HDLC 协议,都使用无连接的 PPP(PPP 和 HDLC 请参考数据链路层的讲解)。现在数据链路层又是不可靠的。当然,网络层也一样不可靠,因为使用了无连接的 IP。
类推到传输层,由于现在的可靠传输交给数据链路层和网络层已经不可能,自然这份可靠的任务就落在了传输层身上。确实传输层中的 UDP 是不可靠的,因为使用 UDP 不能保证数据报都能正确地到达目的地,UDP 发现错误之后可以选择丢弃,也可以选择向应用层报告错误。但是关键还是要由用户自己来选择,如果用户选择了 TCP(如 FTP 软件),自然传输层就是可靠的。但是,如果用户使用了 UDP(如 QQ 软件、视频会议软件等),传输层就不可靠。所以说传输层是否可靠与传输层使用的协议有很大关系,但是一般默认传输层是可靠的。
传输层的功能如下∶
- 提供应用进程间的逻辑通信(网络层提供主机之间的逻辑通信) “逻辑通信” 的意思是传输层之间的通信好像是沿图 5-2 所示水平方向传送数据,但事实上这两个传输层之间并没有一条水平方向的物理连接。
- 差错检测 对收到报文的首部和数据部分都进行差错检测(网络层只检查 IP 数据报首部,并不检查数据部分)。
- 提供无连接或面向连接的服务 根据应用的不同,如有些数据传输要求实时性(如实时视频会议),传输层需要有两种不同的传输协议,即面向连接的 TCP 和无连接的 UDP。TCP 提供了一种可靠性较高的传输服务,UDP 则提供了一种高效率的但不可靠的传输服务。
- 复用和分用 复用是指发送方不同的应用进程都可以使用同一个传输层协议传送数据。分用是指接收方的传输层在剥去报文的首部后能够把这些数据正确交付到目的应用进程。与在第 3 章讲解的 4 种多路复用原理上差不多,只是形式上改变了。

以上的功能是针对整个传输层而言的,而对于面向连接的服务还有以下两个功能。
- 连接管理 通常把连接的定义和建立的过程称为握手。例如,TCP 的 “三次握手” 机制。
- 流量控制与拥塞控制 以对方和网络普遍接受的速度发送数据,从而防止网络拥塞造成数据的丢失。
疑问 1∶在传输层应根据什么原则来确定使用面向连接服务还是无连接服务?
解析∶需要根据上层应用程序的性质来区分。
例如,在传送文件时要使用文件传送协议(FTP),而文件的传送必须是可靠的,不能有错误或者丢失,因此在传输层就必须使用面向连接的 TCP。但是若应用程序是要传送分组话音或视频点播信息,那么为了保证信息传输的实时性,传输层就必须使用无连接的 UDP。
疑问 2∶应用进程看见的好像在两个传输层实体之间有一条端到端的逻辑通信信道,怎么理解?
故事助记∶例如,现在有两栋 7 层楼房,张三站在其中一栋的 4 楼,李四站在对面那栋楼房的 4 楼,并且招手示意让张三过去。根据常理,张三肯定必须先下到一楼,再从地面走向另外一栋楼,然后爬楼梯到 4 楼。没过一会儿,张三突然出现在李四的面前。在李四眼里,好像这两栋楼的第 4 层是相连的,可以直接走过来。其实不是这样,只是李四没有看到张三爬楼梯的细节而已。
疑问 3∶TCP 是面向连接的,但 TCP 使用的 IP 却是无连接的。这两种协议都有哪些主要的区别?
解析∶这个问题很重要,一定要弄清楚。TCP 是面向连接的,但 TCP 所使用的网络则可以是面向连接的(如 X.25 网络,已经淘汰,在此仅作为例子,不用了解)也可以是无连接的(如现在大量使用的 IP 网络)。选择无连接网络就使得整个系统非常灵活,当然也带来了一些问题。表 5-1 是 TCP 与 IP 向上提供的功能和服务的比较。

显然,TCP 所提供的功能和服务要比 IP 所能提供的功能和服务多得多。这是因为 TCP 使用了诸如确认、滑动窗口、计时器等机制,因而可以检测出有差错的报文、重复的报文和失序的报文。
传输层寻址与端口
端口的基本概念
前面讲过数据链路层按 MAC 地址寻址,网络层按 IP 地址寻址,而传输层是按端口号寻址的。
端口就是传输层服务访问点,端口能够让应用层的各种应用进程将其数据通过端口向下交付给传输层以及让传输层知道应当将其报文段中的数据向上通过端口交付给应用层的相应进程。
从这个意义上讲,端口就是用来标识应用层的进程。换句通俗的话,端口就类似于寝室号,而寝室里面住着应用进程。
注意∶以上讲解的端口都是软件端口。
软件端口与硬件端口的区别∶硬件端口是不同硬件设备进行交互的接口(如交换机和路由器的端口),而软件端口是应用层的各种协议进程与传输实体进行层间交互的一种地址。
端口号
由于同一时刻一台主机上会有大量的网络应用进程在运行,所以需要有大量的端口号来标识不同的进程。
端口用一个 16 位端口号进行标识,共允许有 216=65536 个端口号。端口号只具有本地意义,即端口号只是为了标识本计算机应用层中的各进程。
例如,主机 A 的 8080 号端口和主机 B 的 8080 号端口是没有任何联系的。根据端口号范围可将端口分为 3 类。
- 熟知端口(保留端口)∶数值一般为 0~1023。当一种新的应用程序出现时,必须为它指派一个熟知端口,以便其他应用进程和其交互。常见的几个熟知端口见表 5-2。

- 登记端口∶数值为 1024~49151。它是为没有熟知端口号的应用程序使用的,使用这类端口号必须在 IANA 登记,以防止重复。
- 客户端端口或短暂端口∶数值为 49152~65535。由于这类端口号仅在客户进程运行时才动态选择,所以称为短暂端口或临时端口。通信结束后,此端口就自动空闲出来,以供其他客户进程使用。
注意∶1)和 2)又称为服务端端口。
套接字
一台拥有 IP 地址的主机可以提供许多服务,如 Web 服务、FTP 服务、SMTP 服务等,这些服务完全可以通过一个 IP 地址来实现。
故事助记∶例如,A 宿舍的一个同学要分别送不同的东西给 B 宿舍不同的同学,就类似使用 Web 服务、FTP 服务、SMTP 服务等多种服务。当然仅仅知道这些东西是送给 B 宿舍的同学是不够的,就好像只知道 IP 地址,所以说需要知道不同的东西送到 B 宿舍的哪个寝室(假设寝室都是单人间,住在这里面的人就相当于一个进程),寝室号码就类似端口号。因此,要确定一个东西送到哪里,需要宿舍号 + 寝室号。就好像如果要实现 FTP 服务就需要知道主机的 IP 地址和该应用程序的端口号,这样才能区分不同的服务。但是需要注意的是,端口并不是一一对应的。例如,你的计算机作为客户机访问一台 WWW 服务器时,WWW 服务器使用 “80” 端口与你的计算机通信,但你的计算机则可能使用 “50000” 这样的端口。所以只有通过 IP 地址和端口号才能唯一确定一个连接的端口,称为套接字,即
套接字 =(主机 IP 地址,端口号)
它唯一地标识了网络中的某台主机上的某个应用进程。
无连接服务和面向连接服务
关于无连接服务与面向连接服务的基本概念请参考 1.2.4 小节。
传输层提供了两种类型的服务∶无连接服务和面向连接服务,相应的实现分别是用户数据报协议(UDP)和传输控制协议(TCP)。
当采用 TCP 时,传输层向上提供的是一条全双工的可靠的逻辑信道;
当采用 UDP 时,传输层向上提供的是一条不可靠的逻辑信道。
UDP 的主要特点
- 传送数据前无需建立连接,数据到达后也无需确认。
- 不可靠交付。
- 报文头部短,传输开销小,时延较短。
TCP 的主要特点
- 面向连接,不提供广播或多播服务。
- 可靠交付。
- 报文段头部长,传输开销大。
总结 1∶UDP 数据报与 IP 分组的区别。
解析∶IP 分组要经过互联网中许多路由器的存储转发,但 UDP 数据报是在传输层的端到端抽象的逻辑信道中传送的,UDP 数据报只是 IP 数据报中的数据部分(见图 5-3),对路由器是不可见的。
总结 2∶TCP 连接和网络层的虚电路的区别。
解析∶TCP 报文段是在传输层抽象的端到端逻辑信道中传送的,对路由器不可见。TCP 中所谓的连接只是在 TCP 的 TCB(见下解释)中存储了对端的地址信息,并且记录连接的状态,通过重发之类来保证可靠传输,并没有一条真正的物理连接。而电路交换是真正建立一条物理连接,考生不要弄混。另外,虚电路建立的也不是一条真正的物理连接。
注意∶在网络传输层,TCP 模块中有一个 TCB(传输控制模块,Transmit Contol Block。它用于记录 TCP 运行过程中的变量。对于有多个连接的 TCP,每个连接都有一个 TCB。TCB 结构的定义包括这个连接使用的源端口、目的端口、目的 IP、序号、应答序号、对方窗口大小、自己窗口大小、TCP 状态等。
可能疑问点∶当应用程序使用面向连接的 TCP 和无连接的 IP 时,这种传输是面向连接的还是面向无连接的?
解析∶这个问题应该回答都是。因为这要从不同的层次来看,在传输层是面向连接的,而在网络层是面向无连接的。

UDP
UDP 的数据报
UDP 的基本概念
UDP 和 TCP 最大的区别在于它是无连接的,UDP 其实只在 IP 的数据报服务之上增加了端口的功能(为了找到进程)和差错检测的功能。
虽然 UDP 用户数据报只能提供不可靠的交付,但 UDP 在某些方面有其特殊的优点。
- 发送数据之前不需要建立连接。
- UDP 的主机不需要维持复杂的连接状态表。
- UDP 用户数据报只有 8 个字节的首部开销。
- 网络出现的拥塞不会使源主机的发送速率降低(没有拥塞控制)。这对某些实时应用(如 IP 电话、实时视频会议)是很重要的。
- UDP 支持一对一、一对多、多对一和多对多的交互通信。
UDP 数据报的组成
UDP 数据报有两个字段∶数据字段和首部字段。首部字段有 8B,由 4 个字段组成,如图 5-4 所示。
- 源端口∶占 2B。前面已经说了使用 16bit 来表示端口号,所以需要 2B 长度。
- 目的端口∶占 2B。
- 长度∶占 2B。 注意∶尽管使用 2B 来描述 UDP 数据报的长度,但是一般来说 UDP 限制其应用程序数据为 512B 或更小。
- 校验和∶占 2B,用来检测 UDP 用户数据报在传输中是否有错(既检验首部,又检验数据部分),如果有错,就直接丢弃。若该字段为可选字段,当源主机不想计算校验和时,则直接令该字段为全 0。
检验范围∶伪首部、UDP 数据报的首部和数据。其中,在计算校验和时临时生成伪首部。
具体校验和的计算方法参考 5.2.2 小节。
UDP 校验
UDP 校验只提供差错检测。在计算校验和时,要在 UDP 用户数据报之前临时加上 12B 的伪首部,如图 5-4 所示。
其中,伪首部包括源 IP 地址字段、目的 IP 地址字段、全 0 字段、协议字段(UDP 固定为 17)、UDP 长度字段(图 5-5 假设用户数据报的长度是 15B)。一定要记住伪首部只用于计算和验证校验和,其既不向下传送,也不向上递交。
注意∶
- 校验的时候若 UDP 数据报数据部分的长度不是偶数字节,则需要填入一个全 0 字节,如图 5-5 所示。但是此字节和伪首部一样,是不发送的。
- 如果 UDP 校验和校验出 UDP 数据报是错误的,可以丢弃,也可以交付给上层,但是需要附上错误报告 ,即告诉上层这是错误的数据报。
- 通过伪首部,不仅可以检查源端口号、目的端口号和 UDP 用户数据报的数据部分,还可以检查 IP 数据报的源 IP 地址和目的地址。
不难看出,图 5-5 这种简单的差错检验方法的检错能力并不强,但它的好处是简单,处理起来较快。

按二进制反码计算后,当无差错时其结果应该为全 1;否则就表明有差错出现,接收方就应该丢弃这个 UDP 报文。
可能疑问点∶什么是按二进制反码运算求和?


以上答案其实是错误的,由于最高位产生了进位,所以要对最后的结果加 1,最后的答案应该为 0011011011001101。
TCP
TCP 报文段
最麻烦的知识点来了,不是说它难,而是很快就容易忘记,希望下面的总结能帮助快速记住 TCP 报文段的各个字段含义。
在讲 IP 数据报的时候,讲过 IP 数据报分为首部和数据部分,IP 数据报的功能全部体现在首部,而 TCP 报文段也分为首部和数据两部分。
同理,TCP 的全部功能也都体现在首部的各个字段中,所以重点讲解 TCP 报文段的首部。讲解首部之前,还是先声明一点∶讲解 IP 数据报时就说过,首部默认是 20B(如果题目中没有说明),不要认为是 60B,记忆方式和 IP 数据报类似,TCP 报文段也用 4 位表示首部长度,所以可以表示 15 个单位的长度(0000 不包括),然后根据记忆方式∶首饰 = 首 4,即最小单位是 4B,所以可以表示 60B 的长度,在 IP 数据报中称为首部长度,而在 TCP 报文段中称为数据偏移。下面对照图 5-6 所示 TCP 报文的首部格式详细讲解各个字段。
- 源端口和目的端口∶各占 2B。与 UDP 一样,TCP 的首部当然也有源端口和目的端口。
- 序号∶占 4B。尽管从应用层交付下来的是 TCP 报文段,但是 TCP 是面向字节流的(就是说 TCP 传送时是按照一个个字节来传送的),所以在一个 TCP 连接中传送的字节流需要编号,这样才能保证按序交付。
如,某报文段的序号从 301 开始,而携带的数据共有 100B。这就表明本报文段数据的第一个字节的序号是 301,最后一个字节的序号是 400。显然,下一个报文段(如果还有)的数据序号应当从 401 开始,即下一个报文段的序号字段应为 401,这个字段名也称为 “报文段序号”。
- 确认号∶占 4B。TCP 是含有确认机制的,所以接收端需要给发送端发送确认号,这个确认号只需记住一点∶若确认号等于 N,则表明到序号 N-1 为止的所有数据都已经正确收到。
例如,B 正确收到了 A 发送过来的一个报文段,其序号字段值是 501,而数据长度是 200B (序号 501~700),这表明 B 正确收到了 A 发送的到序号 700 为止的数据。因此,B 期望收到 A 的下一个数据序号是 701,于是 B 将发送给 A 的确认报文段中的确认号设置为 701。注意,现在的确认号不是 501,也不是 700,而是 701。
- 数据偏移∶占 4 位。前面已经讲过,这里的数据偏移不是 IP 数据报中分片的那个数据偏移,而是表示首部长度,千万不要混淆。占 4 位可表示 0001~1111 一共 15 种状态,而基本单位是 4B,所以数据偏移确定了首部最长为 60B。
- 保留字段∶占 6 位。保留为今后使用,但目前应置为 0,该字段可以忽略不计。
- 紧急 URG∶当 URG=1 时,表明紧急指针字段有效。它告诉系统此报文段中有紧急数据,应尽快传送(相当于高优先级的数据)。就好像有一个等待红灯的超长车队,此时有一辆救护车过来,属于紧急事件,救护车就可以不用等红灯了,直接从边上绕过所有的车。但是紧急 URG 需要和紧急指针配套使用,比如说有很多救护车过来,现在就需要一个紧急指针指向最后一辆救护车,一旦最后一辆救护车过去之后,TCP 就告诉应用程序恢复到正常操作,也就是说数据从第一字节到紧急指针所指字节就是紧急数据。
- 确认比特 ACK∶只有当 ACK=1 时,确认号字段才有效;当 ACK=0 时,确认号无效。TCP 规定,一旦连接建立了,所有传送的报文段都必须把 ACK 置 1。
- 推送比特 PSH∶TCP 收到推送比特置 1 的报文段,就尽快地交付给接收应用进程,而不再等到整个缓存都填满后再向上交付。
可能疑问点∶看到这里肯定有考生会有这样的疑惑,在 TCP 首部中,URG 是紧急比特,而当 URG=1 时,表示紧急指针有效,也就是能发送紧急数据。而 PSH 的目的也是发送紧急数据,那么 URG 和 PSH 到底有什么区别?
解析∶URG=1,表示紧急指针指向报文内数据段的某个字节(数据从第一字节到指针所指字节就是紧急数据),不进入接收缓冲(前面讲了待上交的数据要先进入接收缓存,然后再交付给应用层。而这里就直接交给上层进程,余下的数据都是要进入接收缓冲的)。一般来说,TCP 是要等到整个缓存都填满了后再向上交付,如果 PSH=1,就不用等到整个缓存都填满,直接交付,但是这里的交付仍然是从缓冲区中交付的,URG 是不经过缓冲区的,千万记住。
- 复位比特 RST∶当 RST=1 时,表明 TCP 连接中出现严重差错(如由于主机崩溃或其他原因),必须释放连接,然后再重新建立传输连接。
- 同步比特 SYN∶同步比特 SYN 置为 1,表示这是一个连接请求或连接接受报文,后面 TCP 连接会详细讲到。
- 终止比特 FIN∶释放一个连接。当 FIN=1 时,表明此报文段的发送端的数据已发送完毕,并要求释放传输连接。
- 窗口字段∶占 2B。窗口字段用来控制对方发送的数据量,单位为字节(B)。记住一句话∶窗口字段明确指出了现在允许对方发送的数据量。
- 校验和字段∶占 2B。校验和字段检验的范围包括首部和数据两部分。在计算校验和时,和 UDP 一样,要在 TCP 报文段的前面加上 12B 的伪首部(只需将 UDP 伪首部的第 4 个字段的 17 改为 6,其他和 UDP 一样)。
- 紧急指针字段∶占 2B。前面已经讲过紧急指针指出在本报文段中的紧急数据的最后一个字节的序号。
- 选项字段∶长度可变。TCP 最初只规定了一种选项,即最大报文段长度 MSS。MSS 告诉对方 TCP∶“我的缓存所能接收的报文段的数据字段的最大长度是 MSS 字节。”
- 填充字段∶为了使整个首部长度是 4B 的整数倍。
可能疑问点∶在 TCP 报文段的首部中只有端口号而没有 IP 地址。当 TCP 将其报文段交给 IP 层时,IP 怎样知道目的 IP 地址呢?
解析∶显然,仅从 TCP 报文段的首部是无法得知目的 IP 地址的。因此,TCP 必须告诉 IP 层此报文段要发送给哪一个目的主机(给出其 IP 地址)。此目的 IP 地址填写在 IP 数据报的首部中。
TCP 的连接管理
TCP 的传输连接分为 3 个阶段∶连接建立、数据传送和连接释放。
TCP 传输连接的管理就是使传输连接的建立和释放都能正常地进行。
TCP 把连接作为最基本的抽象,每一条 TCP 连接有两个端点,TCP 连接的端点不是主机,不是主机的 IP 地址,不是应用进程,也不是传输层的协议端口。TCP 连接的端点叫作套接字(Socket)或插口。端口号拼接到 IP 地址即构成了套接字。
每一条 TCP 连接唯一地被通信两端的两个端点(两个套接字)所确定。
例如,TCP 连接∶={socket1,socket2)={(IP1∶port1),(IP2∶port2)}。
TCP 的连接和建立都是采用客户 / 服务器方式。
主动发起连接建立的应用进程叫作客户(Client),被动等待连接建立的应用进程叫作服务器(Server)。
TCP 传输连接的建立采用 “三次握手” 的方法,如图 5-7 所示。下面从一个实例来分析建立连接的过程。
- 第一步∶客户机 A 的 TCP 向服务器 B 发出连接请求报文段,其首部中的同步位 SYN=1(TCP 规定,SYN 报文段不能携带数据,但要消耗一个序号),并选择序号 seq=x,表明传送数据时的第一个数据字节的序号是 x。
- 第二步∶服务器收到了数据报,并从 SYN 位为 1 知道这是一个建立连接的请求。如果同意,则发回确认。B 在确认报文段中应使 SYN=l,ACK=1,其确认号 ack=x+1,自己选择的序号 seq=y。注意,此时该报文段也不能携带数据(助记∶因为有 SYN=1,所以不能带数据)。
- 第三步∶A 收到此报文段后向 B 给出确认,其 ACK=1,确认号 ack=y+1。A 的 TCP 通知上层应用进程,连接已经建立。B 的 TCP 收到主机 A 的确认后,也通知其上层应用进程,此时 TCP 连接已经建立,ACK 报文可以携带数据(没有 SYN 字段),如果不携带数据则不消耗序号。

采用 “三次握手” 的方法,目的是为了防止报文段在传输连接建立过程中出现差错。通过 3 次报文段的交互后,通信双方的进程之间就建立了一条传输连接,然后就可以用全双工的方式在该传输连接上正常地传输数据报文段了,下面先看两个例子。

解析∶C。不管是连接还是释放,SYN、ACK、FIN 的值一定是 1,排除 A 和 D 选项。确认号是甲发送的序列号加 1,ack 的值应该为 11221(11220 已经收到,期待接收 11221。另外需要提醒的一点是,乙的 seq 值是主机随意给的,和甲的 seq 值没有任何关系,请参考下面的总结。

解析∶B。首先应该计算出第 2 个段的第 1 个字节的序号。第 3 个段的第 1 个字节序号为 900,由于第 2 个段有 400B,所以第 2 个段的第 1 个字节的序号为 500。
另外,由于确认号就是期待接收下一个 TCP 段的第 1 个字节序号,所以主机乙发送给主机甲的确认序号是 500 一旦数据传输结束,参与传输的任何一方都可以请求释放传输连接。
在释放连接过程中,发送端进程与接收端进程要通过 4 次 TCP 报文段来释放整个传输连接,在此分成 4 步来详细讲解。
第一步∶如图 5-8 所示,数据传输结束后,通信双方都可释放连接。现在 A 的应用进程先向其 TCP 发出连接释放报文段,并停止再发送数据,主动关闭 TCP 连接。A 将连接释放报文段首部的 FIN 置 1,其序号 seq=u,等待 B 的确认。这里要注意,因为 TCP 是双工的,也就是说,可以想象一对 TCP 连接上有两条数据通路。当发送 FIN 报文时,发送 FIN 的一端就不能发送数据,也就是关闭了其中一条数据通路,但是对方还是可以发送数据的。

第二步∶如图 5-9 所示,B 发出确认,确认号 ack=u+1,而这个报文段自己的序号 seq=v。TCP 服务器进程通知高层应用进程。从 A 到 B 这个方向的连接就释放了,TCP 连接处于半关闭状态。B 若发送数据,则 A 仍要接收。

第三步∶如图 5-10 所示,若 B 已经没有要向 A 发送的数据,其应用进程就通知 TCP 释放连接。
第四步∶如图 5-11 所示,A 收到连接释放报文段后,必须发出确认。在确认报文段中,ACK=1,确认号 ack=w+1,自己的序号 seq=u+1。
提醒∶TCP 连接必须经过时间 2MSL 后才真正释放,如图 5-12 所示。
总结∶
(1)连接建立分为 3 步∶
SYN=1,seq=x。
SYN=1,ACK=1,seq=y,ack=x+1。
ACK=1,seq=x+1,ack=y+1。
(2)连接释放分为 4 步∶
FIN=1,seq=u。
ACK=1,seq=v,ack=u+1。
FIN=1,ACK=1,seq=w,ack=u+1。
ACK=1,seq=u+1,ack=w+1。



可能疑问点∶为什么 TCP 在建立连接时不能每次都选择相同的、固定的初始序号?
解析∶如果 TCP 在建立连接时每次都选择相同的、固定的初始序号,那么设想以下的情况。
- 假定主机 A 和 B 频繁地建立连接,传送一些 TCP 报文段后,再释放连接,然后又不断地建立新的连接、传送报文段和释放连接。
- 假定每一次建立连接时,主机 A 都选择相同的、固定的初始序号,如选择 1。
- 假定主机 A 发送出的某些 TCP 报文段在网络中会滞留较长的时间,以致造成主机 A 超时重传这些 TCP 报文段。
- 假定有一些在网络中滞留时间较长的 TCP 报文段最后终于到达了主机 B,但这时传送该报文段的那个连接早已释放了,而在到达主机 B 时的 TCP 连接是一条新的 TCP 连接。
这样,工作在新的 TCP 连接下的主机 B 就有可能会接收在旧的连接传送的、已经没有意义的、过时的 TCP 报文段(因为这个 TCP 报文段的序号有可能正好处在现在新的连接所使用的序号范围之中),结果产生错误。
因此,必须使得迟到的 TCP 报文段的序号不处在新的连接所使用的序号范围之中。这样,TCP 在建立新的连接时所选择的初始序号一定要和前面的一些连接所使用过的序号不一样。因此,不同的 TCP 连接不能使用相同的初始序号。
可能疑问点∶为什么不采用 “两次握手” 建立连接?
解析∶“三次握手” 完成两个重要的功能,既要双方做好发送数据的准备工作(双方都知道彼此已准备好),也要允许双方就初始序列号进行协商,这个序列号在握手过程中被发送和确认。
如果把 “三次握手” 改成 “两次握手”,就可能发生死锁。
例如,考虑计算机 A 和 B 之间的通信,假定 A 给 B 发送一个连接请求分组,B 收到了这个分组,并发送了确认应答分组。按照 “两次握手” 的协定,B 认为连接已经成功地建立了,可以开始发送数据分组。可是,A 在 B 的应答分组在传输中被丢失的情况下,将不知道 B 是否已准备好,也不知道 B 发送数据使用的初始序列号,A 甚至怀疑 B 是否收到自己的连接请求分组。在这种情况下,A 认为连接还未建立成功,将忽略 B 发来的任何数据分组,只等待连接确认应答分组。而 B 在发出的分组超时后,重复发送同样的分组。这样就形成了死锁,如图 5-13 所示。
可能疑问点∶为什么不采用 “三次握手” 释放连接,且发送最后 “一次握手” 报文后要等待 2MSL(最长报文寿命)的时间呢?
解析∶首先,为了保证 A 发送的最后一个确认报文段能够到达 B。如果 A 不等待 2MSL,A 返回的最后确认报文段丢失,则 B 不能进入正常关闭状态,而 A 此时已经关闭,也不可能再重传。

其次,防止出现 “已失效的连接请求报文段”。A 在发送完最后一个确认报文段后,再经过 2MSL 可保证本连接持续的时间内所产生的所有报文段从网络中消失。造成错误的情形与上文不采用 “两次握手” 建立连接的原因所述的情形相同。
TCP 可靠传输
滑动窗口机制在第 3 章已经详细讲解过,在此不再重复讲解。下面仅做一个简单的总结。
TCP 数据编号与确认
TCP 是面向字节的。TCP 将所要传送的报文看成是字节组成的数据流,并使每一个字节对应于一个序号。在连接建立时,双方要商定初始序号。TCP 每次发送的报文段的首部中的序号字段数值表示该报文段中的数据部分的第一个字节的序号。
TCP 的确认是对接收到的数据的最高序号表示确认。接收端返回的确认号是已收到的数据的最高序号加 1。因此,确认号表示接收端期望下次收到的数据中的第一个数据字节的序号。
补充知识点∶选择确认 SACK。
解析∶不知道大家还记不记得,在讲解连续 ARQ 协议时,将其分为后退 N 帧协议和选择重传协议。
在后退 N 帧协议中,如果帧不按序到达,直接丢弃后面的,没有使用选择确认。在选择重传协议中,先把后面有序的帧存在接收缓冲区中,等到前面失序的帧到达后,一起按序交付,这里就用到选择确认 SACK。
可能疑问点∶在使用 TCP 传送数据时,如果有一个确认报文段丢失了,那么会不会一定引起对方数据的重传?
解析∶如果使用了累积确认就不一定。例如,现在采用累积确认,A 发送了 1、2、3、4、5 号帧给 B,但是 ACK3 丢失了(1 和 2 号帧都接收到了,期望接收到 3 号帧),此时发送方正准备重发 1 和 2 号帧,却接收到了 ACK6,也就是 1~5 号帧对方都收到了,所以就不要重发了。
TCP 的重传机制
TCP 每发送一个报文段,就对这个报文段设置一次计时器。只要计时器设置的重传时间到了规定时间还没有收到确认,那么就要重传该报文段。
由于 TCP 的下层是一个互联网环境,IP 数据报所选择的路由变化很大,所以传输层的往返时延的方差也很大。为了计算超时计时器的重传时间,TCP 采用了一种自适应的算法。
- 记录每个报文段发出的时间以及收到相应的确认报文段的时间。这两个时间之差就是报文段的往返时延。
- 将各个报文段的往返时延样本加权平均,就得出报文段的平均往返时延(RTT)。
- 每测量到一个新的往返时延样本,就按下式重新计算一次平均往返时延。
RTT=(1-a)x(旧的 RTT)+a x(新的往返时延样本)
在上式中,0≤a<1。若 a 很接近于 1,表示新算出的平均往返时延 RTT 和原来的值相比变化较大,即 RTT 的值更新较快。若选择α接近于 0,则表示加权计算的 RTT 受新的往返时延样本的影响不大,即 RTT 的值更新较慢,一般推荐 a 取 0.125。
计时器的超时重传时间(RTO)应略大于上面得出的 RTT,即
RTO=βxRTT(其中β>1)
注意∶谢希仁的教材有更复杂求 RTO 的公式,都无须掌握,只需知道计时器的 RTO 应略大于上面得出的 RTT 即可。
补充知识点∶Karn 算法。
解析∶当出现超时,源主机在重传报文段后,收到了确认报文段,但该确认报文段有可能是对后来重传报文段的确认,也有可能是对先前发送报文段的确认,如何进行判定?
由于重传的报文段和原来的报文段完全一样,所以源主机在收到确认后就无法做出正确的判定,而正确的判定与确定加权 RTT 的值关系很大。
Kam 提出了一个算法∶在计算加权 RTT 时,== 只要报文段重传了,就不采用其作为往返时间样本。这样得出的加权 RTT 和 RTO 就较准确。
修正的 Kam 算法∶报文段每重传一次,就把 RTO 增大一些。
新的 RTO= y x(旧的 RTO)
系数 y 的典型值是 2。当不再发生报文段的重传时,才根据报文段的往返时延更新加权 RTT 和 RTO 的数值。
可能疑问点∶是否 TCP 和 UDP 都需要计算往返时间 RTT?
解析∶往返时间 RTT 只是对传输层的 TCP 很重要,因为 TCP 要根据 RTT 的值来设置超时计时器的超时时间。
UDP 没有确认和重传机制,因此 RTT 对 UDP 没有什么意义。
因此,不要笼统地说 “往返时间 RTT 对传输层来说很重要”,因为只有 TCP 才需要计算 RTT,而 UDP 不需要计算 RTT。
可能疑问点∶假定在一个互联网中,所有的链路的传输都不出现差错,所有的节点也都不会发生故障。试问在这种情况下,TCP 的 “可靠交付” 的功能是否就是多余的?
解析∶不是多余的。TCP 的 “可靠交付” 功能在互联网中起着至关重要的作用。至少在以下所列举的情况下,TCP 的 “可靠交付” 功能是必不可少的。
- 每个 IP 数据报独立地选择路由,因此在到达目的主机时有可能出现失序。
- 由于路由选择的计算出现错误,导致 IP 数据报在互联网中转圈。最后数据报首部中的生存时间 TTL 的数值下降到零,这个数据报在中途就被丢弃了。
- 在某个路由器突然出现很大的通信量,以致路由器来不及处理到达的数据报,因此有的数据报被丢弃。
以上列举的问题表明,必须依靠 TCP 的 “可靠交付” 功能才能保证在目的主机的目的进程中收到正确的报文。
TCP 流量控制

一般来说,人们总是希望数据传输得更快一些。但如果发送方把数据发送得过快,接收方就可能来不及接收,这就会造成数据的丢失。
流量控制(Flow Control)就是让发送方的发送速率不要太快,既要让接收方来得及接收,也不要使网络发生拥塞。利用滑动窗口机制可以很方便地在 TCP 连接上实现流量控制,请看图 5-14 所示的例子。

注意∶
- TCP 为每一个连接设有一个持续计时器。只要 TCP 连接的一方收到对方的零窗口通知,就启动持续计时器。若持续计时器设置的时间到期,就发送一个零窗口探测报文段(仅携带 1B 的数据),而对方就在确认这个探测报文段时给出了现在的窗口值。若窗口仍然是零,则收到这个报文段的一方就重新设置持续计时器。若窗口不是零,则死锁的僵局就可以打破了。
- 可以用不同的机制来控制 TCP 报文段的发送时机。 第一种机制∶TCP 维持一个变量,它等于最大报文段长度(MSS)。只要缓存中存放的数据达到 MSS 字节时,就组装成一个 TCP 报文段发送出去。 第二种机制∶由发送方的应用进程指明要求发送报文段,即 TCP 支持的推送(push)操作(前面将其和紧急指针做过比较,这里不再解释)。 第三种机制∶发送方的一个计时器期限到了,这时就把当前已有的缓存数据装入报文段(但长度不能超过 MSS)发送出去。

解析∶D。ACKn 的意思是前 n-1 号的帧都已经收到,请发送方继续发送第 n 号帧。在本题中,主机甲发送的第一个段的序号为 200~499,第二个段的序列号为 500~999,主机乙正确接收到两个段后,应该希望主机甲接下来发送 1000 号帧,所以主机乙发送给主机甲的确认序列号是 1000。
TCP 拥塞控制的基本概念
在某段时间,若对网络中某资源的需求超过了该资源所能提供的可用部分,网络的性能就要变坏———产生拥塞(Congestion)。出现资源拥塞的条件是
对资源需求的总和 > 可用资源
若网络中产生拥塞,网络的性能就要明显变坏,整个网络的吞吐量将随输入负荷的增大而下降。
拥塞控制与流量控制的性质对比∶
- 拥塞控制所要做的只有一个目的,就是使得网络能够承受现有的网络负荷。
- 拥塞控制是一个全局性的过程,涉及所有的主机、所有的路由器以及与降低网络传输性能有关的所有因素。
- 流量控制往往指在给定的发送端和接收端之间的点对点通信量的控制。
- 流量控制所要做的就是抑制发送端发送数据的速率,以便使接收端来得及接收。
- 拥塞控制是很难设计的,因为它是一个动态的(而不是静态的)问题。
- 当前网络正朝着高速化的方向发展,这很容易出现缓存不够大而造成分组的丢失。但分组的丢失是网络发生拥塞的征兆而不是原因。
- 在许多情况下,正是拥塞控制本身成为引起网络性能恶化甚至发生死锁的原因,这点应特别引起重视。
拥塞控制又分为闭环控制和开环控制。
- 开环控制方法就是在设计网络时事先将有关发生拥塞的因素考虑周到,力求网络在工作时不产生拥塞。
- 闭环控制是基于反馈环路的概念。属于闭环控制的有以下几种措施∶
①监测网络系统以便检测到拥塞在何时、何处发生。
②将拥塞发生的信息传送到可采取行动的地方。
③调整网络系统的运行以解决出现的问题。
以上是拥塞控制的一些基本概念,下面详细讲解拥塞控制的方法。
拥塞控制的 4 种算法
发送端的主机在确定发送报文段的速率时,既要根据接收端的接收能力,又要从全局考虑不要使网络发生拥塞。因此,TCP 要求发送端维护以下两个窗口。
- 接收端窗口 rwnd∶接收端根据其目前接收缓存大小所许诺的最新的窗口值,反映了接收端的容量。由接收端将其放在 TCP 报文的首部的窗口字段通知发送端,如图 5-14 所示。
- 拥塞窗口 cwnd∶发送端根据自己估计的网络拥塞程度而设置的窗口值,反映了网络的当前容量。
发送端发送窗口的上限值应当取接收端窗口 rwnd 和拥塞窗口 cwnd 这两个变量中较小的一个,即应按以下公式确定∶发送窗口的上限值 = Min 【rwnd,cwnd】
从这个式子可以看出∶
当 rwnd<cwnd 时,发送窗口的上限值是接收方的接收能力限制发送窗口的最大值。
反之,当 cwnd<rvnd 时,发送窗口的上限值是网络的拥塞限制发送窗口的最大值。
也就是说,rwnd 和 cwnd 中较小的一个控制发送方发送数据的速率。
注意∶接收方总是有足够大的缓存空间,因而发送窗口的大小由网络的拥塞程度来决定,也就是说可以将发送窗口等同为拥塞窗口。

解析∶A。发送窗口的上限值 = Min{接收窗口,拥塞窗口},于是此时发送方的发送窗口 = Min{4000,2000}=2000B,而主机甲向主机乙连续发送两个最大段后,只收到第一个段的确认,所以此时主机甲还可以向主机乙发送的最大字节数为 2000B-1000B=1000B。
接收窗口的大小可以根据 TCP 报文首部的窗口字段通知发送端,而发送端怎么去维护拥塞窗口呢?
这就是下面要讲解的慢开始算法和拥塞避免算法。
慢开始算法的原理
- 在主机刚刚开始发送报文段时可先设置拥塞窗口 cwnd=1,即设置为一个最大报文段长度 MSS 的数值。
- 在每收到一个对新的报文段的确认后,将拥塞窗口加 1,即增加一个 MSS 的数值。注意∶这里是说每收到 1 个对新的报文段的确认后,将拥塞窗口加 1,而第二次会收到 2 个确认,第三次会收到 4 个确认,依此类推,可以知道每经过一个传输轮次,拥塞窗口就加倍,如图 5-15 所示。

- 用这样的方法逐步增大发送端的拥塞窗口 cwnd,可以使分组注入到网络的速率更加合理。
补充知识点∶什么是传输轮次?
解析∶使用慢开始算法后,每经过一个传输轮次,拥塞窗口 cwnd 就加倍。一个传传输轮次所经历的时间其实就是往返时间 RTT。传输轮次更加强调把拥塞窗口 cwnd 所允许发送的报文段都连续发送出去,并收到了对已发送的最后一个字节的确认。
例如,拥塞窗口 cwnd=4,这时的往返时间 RTT 就是发送方连续发送 4 个报文段并收到这 4 个报文段的确认总共经历的时间。
使用慢开始算法后,每经过一个传输轮次,拥塞窗口 cwnd 就加倍,即 cwnd 的大小呈指数形式增长。这样慢开始一直把拥塞窗口 cwnd 增大到一个规定的慢开始门 ssthresh(阈值),然后改用拥塞避免算法。
拥塞避免算法的原理
为防止拥塞窗口 cwnd 的增长引起网络阻塞,还需要一个状态变量,即慢开始门限 ssthresh,其用法如下∶
当
cwnd<ssthresh
时,使用慢开始算法。当
cwnd>ssthresh
时,停止使用慢开始算法,改用拥塞避免算法。cwnd=ssthresh
时,既可以使用慢开始算法,也可以使用拥塞避免算法。其中,拥塞避免算法的做法是,发送端的拥塞窗口 cwnd 每经过一个往返时延 RTT 就增加一个 MSS 的大小,通常表现为按线性规律增长。
无论在慢开始阶段还是在拥塞避免阶段,只要发送方判断网络出现拥塞(其根据就是没按时收到确认),就要把慢开始门限 ssthresh 设置为出现拥塞时的发送窗口值的一半(但不能小于 2),然后把拥塞窗口 cwnd 重新设置为 1,执行慢开始算法。
这样做的目的就是要迅速减少主机发送到网络中的分组数,使得发生拥塞的路由器有足够时间把队列中积压的分组处理完毕。以上具体过程通过下面一系列过程图来详细讲解。
- 当 TCP 连接进行初始化时,将拥塞窗口设置为 1,如图 5-16 所示。图中的窗口单位不使用字节而使用报文段。慢开始门限的初始值设置为 16 个报文段,即 ssthresh=16。发送端送窗口不能超过拥塞窗口 cwnd 和接收端窗口 rwnd 中的最小值。假定接收端窗口足够因此现在发送窗口的数值 = 等于拥塞窗口的数值 ==。

- 在执行慢开始算法时,拥塞窗口 cwnd 的初始值为 1,发送第一个报文段 M,如图 5-17 所示。

- 发送端每收到一个确认,就把 cwnd 加 1,于是发送端可以接着发送 M1 和 M2 两个报文段,如图 5-18 所示。

- 接收端共发回两个确认。发送端每收到一个对新报文段的确认,就把发送端的 cwnd 加 1。现在 cwnd 从 2 增大到 4,并可接着发送后面的 4 个报文段,如图 5-19 所示。
5.

- 发送端每收到一个对新报文段的确认,就把发送端的拥塞窗口加 1,因此拥塞窗口 cwnd 随着传输轮次按指数规律增长,如图 5-20 所示。
- 当拥塞窗口 cwnd 增长到慢开始门限值 ssthresh 时(当 cwnd=16 时),就改为执行拥塞避免算法,拥塞窗口按线性规律增长,如图 5-21 所示。

- 假定拥塞窗口的数值增长到 24 时,网络出现超时,表明网络拥塞了,如图 5-22 所示。

- 更新后的 ssthresh 值变为 12(发送窗口数值 24 的一半),拥塞窗口重新设置为 1,并执行慢开始算法,如图 5-23 所示。
- 当 cwnd=12 时改为执行拥塞避免算法,拥塞窗口按线性规律增长,每经过一个往返时延就增加一个 MSS 的大小,如图 5-24 所示。
总结∶
- 乘法减小。它是指不论在慢开始阶段还是拥塞避免阶段,只要出现一次超时(出现一次网络拥塞),就把慢开始门限值 ssthresh 设置为当前的拥塞窗口值的一半。当网络频繁出现拥塞时,ssthresh 值就下降得很快,以减少注入到网络中的分组数。

- 加法增大。它是指执行拥塞避免算法时,在收到对所有报文段的确认后(经过一个往返时间),就把拥塞窗口 cwnd 增加一个 MSS 大小,使拥塞窗口缓慢增大,以防止网络过早出现拥塞。
- 拥塞避免并非指完全能够避免了拥塞。利用以上的措施要完全避免网络拥塞还是不可能的。拥塞避免是说在拥塞避免阶段把拥塞窗口控制为按线性规律增长,使网络比较不容易出现拥塞。

解析∶C。当拥塞窗口为 16KB 时发生了超时,慢开始门限值将变成 8KB,发送窗口变为 1KB。
下面逐一列出各个 RTT 之后的拥塞窗口大小。
- 开始重传∶此时拥塞窗口为 1KB。
- 第一次 RTT 结束∶执行慢开始算法,此时拥塞窗口为 2KB。
- 第二次 RTT 结束∶执行慢开始算法,此时拥塞窗口为 4KB。
- 第三次 RTT 结束∶执行慢开始算法,此时拥塞窗口为 8KB。
- 第四次 RTT 结束∶由于第三次 RTT 结束的时候拥塞窗口的大小已经和慢开始门限值相等,因此此时应该结束使用慢开始算法,转而使用拥塞避免算法,故此时拥塞窗口为 8KB+1KB=9KB。
快重传算法
首先要求接收方每收到一个失序的报文段后就立即发出重复确认。这样做可以让发送方及早知道有报文段没有到达接收方。发送方只要连续收到 3 个重复确认就应当立即重传对方尚未收到的报文段,如图 5-25 所示。不难看出,快重传并非取消重传计时器,而是在某些情况下可更早地重传丢失的报文段。

快恢复算法
- 当发送端收到连续 3 个重复的确认时,就执行 “乘法减小” 算法,把慢开始门限 ssthresh 设置为当前拥塞窗口的一半。但接下去不执行慢开始算法。
- 由于发送方现在认为网络很可能没有发生拥塞,所以现在不执行慢开始算法,即拥塞窗口 cwnd 现在不设置为 1,而是将慢开始门限 ssthresh 设置为当前拥塞窗口的一半,然后开始执行拥塞避免算法(“加法增大”),使得拥塞窗口缓慢地线性增大,如图 5-26 所示。

快恢复具体算法如下∶
- 当发送端收到连续 3 个重复的 ACK 时,就重新设置慢开始门限 stresh(拥塞窗口的一半)。
- 与慢开始的不同之处是拥塞窗口 cwnd 不是设置为 1,而是设置为新的 sthresh。
- 若发送窗口值还允许发送报文段,就按拥塞避免算法继续发送报文段。
补充知识点∶有些快重传实现用的是另一种算法,考试的时候肯定会说明,为了全面地复习,所以将另一种算法也罗列出来,仅供参考,算法如下∶
- 当发送端收到连续 3 个重复的 ACK 时,就重新设置慢开始门限 ssthresh(拥塞窗口的一半)。
- 与慢开始的不同之处是拥塞窗口 cwnd 不是设置为 1,而是设置为 ssthresh+3 x MSS。
- 若收到的重复的 ACK 为 n 个(n>3),则将 cwnd 设置为 ssthresh+ n x MSS。
- 若发送窗口值还允许发送报文段,就按拥塞避免算法继续发送报文段。