5 运输层¶
5.1 运输层概述¶
在计算机网络中,物理层、数据链路层和网络层共同解决了主机之间通过异构网络互联的问题,实现了主机到主机的通信。
然而,实际进行通信的真正实体是运行在通信两端主机中的应用进程(如图中主机A上的AP1、AP2与主机B上的AP3、AP4)。尽管网络层负责将数据从源主机传输到目的主机,但无法直接支持不同主机上进程之间的通信。
因此,运输层(传输层)的主要任务就是为运行在不同主机上的应用进程提供端到端的逻辑通信服务,即实现进程间的直接通信。运输层协议也被称为端到端协议
TCP/IP 运输层中有两个重要的协议,分别是 TCP 和 UCP
运行在计算机上的进程通常使用进程标识符(Process Identification, PID)进行标识,但由于互联网中的计算机可能运行不同的操作系统(如 Windows、Linux、macOS),而这些系统采用的 PID 格式并不统一,因此无法直接用于跨平台的网络通信。为了使不同操作系统上的应用进程能够基于网络实现互操作,必须采用一种统一的方法来标识和区分应用层进程。
TCP/IP 体系结构中的运输层通过引入端口号来解决这一问题:端口号是一个长度为 16 比特的数值,取值范围为 0~65535,用于唯一标识和区分同一台主机上运行的不同应用进程,从而实现跨平台、跨系统的端到端通信。
端口号只具有本地意义,即端口号只是为了标识本计算机网络协议栈应用层中的各应用进程。在因特网中,不同计算机中的相同端口号是没有关系的,即相互独立。另外,TCP和UDP端口号之间也是没有关系的。
运输层端口号的应用
当用户在PC(192.168.0.1)上输入网址 www.porttest.net 时,由于浏览器只知道该网站的域名而不知道其IP地址,首先会通过DNS服务器(192.168.0.2)进行域名解析:PC向DNS服务器发送查询请求,获取 www.porttest.net 对应的IP地址(如192.168.0.3),这一过程涉及DNS协议和可能的ARP解析。
获得目标IP后,PC开始与Web服务器建立连接:它使用TCP协议发起三次握手,将源端口(如随机分配的50000)和目的端口(通常为HTTP的80端口)写入TCP报文段,并封装成IP数据报(协议字段值为6),通过交换机发送至Web服务器;服务器收到后,根据IP首部的协议字段判断由TCP处理,再依据目的端口号将数据分用到相应的Web服务进程;随后双方进入数据传输阶段,客户端请求网页内容,服务器返回HTML等响应数据
5.2 UDP 和 TCP 的对比¶
UDP是无连接的协议,在数据传输前不需要建立任何逻辑连接,发送方直接将用户数据封装成用户数据报并发送到网络中,接收方收到后即可处理,整个过程简单高效但不保证可靠性;而TCP是面向连接的协议,通信前必须通过“三报文握手”建立逻辑连接,确保双方都准备好通信,之后才进行数据传输,在传输完成后还需通过“四报文挥手”释放连接。
这里的“连接”指的是端到端的逻辑连接关系,而非物理链路
UDP支持单播、多播和广播,TCP仅支持单播
UDP是面向应用报文的,TCP是面向字节流的
UDP是一种无连接、不可靠的传输协议,发送方将数据封装成用户数据报后直接发送,不保证数据是否到达接收方。若数据在传输过程中因网络问题发生丢失、误码或乱序,UDP不会进行重传、纠错或排序,仅丢弃错误数据包
TCP是一种面向连接、可靠的传输协议,通过三次握手建立连接,并在传输过程中实现差错检测、重传机制、流量控制和拥塞控制,能够纠正误码、处理丢失、乱序和重复的数据,确保数据按序、完整地送达接收方,从而向上层提供“面向连接的可靠传输服务”
5.3 TCP¶
5.3.1 TCP报文段的首部格式¶
TCP报文段的首部由固定部分(20字节)和可变选项部分组成
- 序号(Sequence Number) 占32比特,标识本报文段数据载荷的第一个字节在字节流中的序号,用于确保数据按序传输;
- 确认号(Acknowledgment Number) 也占32比特,表示期望收到对方下一个报文段的第一个字节的序号,同时也是对之前已接收数据的确认
- 确认标志位ACK 表示确认号是否有效,只有当ACK=1时确认号才有效,且在TCP连接建立后所有报文段都必须将ACK置为1。
- 数据偏移 占4比特,该字段的取值以4字节为单位。指出TCP报文段的数据载荷部分的起始处距离TCP报文段的起始处有多远,这实际上指出了TCP报文段的首部长度。
- 窗口字段占16比特,以字节为单位,用于指示发送方在当前时刻可以向接收方发送的数据量,即接收方的接收窗口大小。避免接收方因缓冲区满而丢弃数据,从而实现流量控制。
- 检验和 占16比特,用来检查整个TCP报文段在传输过程中是否出现了误码
TCP报文段的检验和字段用于检测传输过程中是否发生错误,其计算基于一个称为“伪首部”的结构。
伪首部由源IP地址、目的IP地址、保留字节(置为0)、协议字段(对TCP为6)和TCP报文段长度组成,它并不实际发送,仅用于校验计算。计算时,首先将TCP首部中检验和字段置为0,然后将伪首部、TCP首部和数据载荷三部分按2字节为单位进行划分,若总长度为奇数则在末尾补一个“全0”字节;接着对所有2字节块执行反码算术求和,并将结果取反码,得到最终的检验和值,写入TCP首部的检验和字段。接收方收到报文后重复该过程,若结果为全0,则说明数据未出错。
伪首部仅仅只用于校验,并不传输
- 同步标志位SYN 用于TCP“三报文握手”建立连接。当SYN=1且ACK=0时,表明这是一个TCP连接请求报文段。对方若同意建立连接,则应在响应的TCP报文段的首部中使SYN=1且ACK=1。
- 终止标志位FIN 用于TCP“四报文挥手”释放连接。当FIN=1时,表明此TCP报文段的发送方已经将全部数据发送完毕,现在要求释放TCP连接。
-
**复位标志位RST ** 用于复位TCP连接。当RST=1时,表明TCP连接中出现严重差错,必须释放连接,然后再重新建立连接。RST置1还用来拒绝一个非法的TCP报文段或拒绝打开一个TCP连接。
-
推送标志位PSH 用于控制数据的立即交付。在正常情况下,发送方可能为了提高效率而延迟发送数据,等待积累更多数据后再发送,接收方也可能会将接收到的数据暂存缓冲区以批量处理;但在交互式通信中(如远程登录),应用进程希望输入的命令能立即被对方处理并返回响应。此时,发送方可通过将PSH置为1,通知接收方“请立即把当前收到的数据交给应用层”,无需等待缓冲区填满。接收方收到PSH=1的报文后,会立即将数据提交给应用进程,从而实现快速响应,提升交互体验。
- 紧急标志位URG和紧急指针字段用于实现紧急数据的快速传输。当URG=1时,表示本报文段包含紧急数据,此时紧急指针字段有效;紧急指针是一个16比特的值,以字节为单位,指出在报文段数据载荷中紧急数据的长度,即从数据起始处到紧急数据末尾的偏移量。发送方将紧急数据“插队”到发送缓存最前面,并立即封装成报文段发送,无需等待缓冲区满。接收方收到URG=1的报文后,会根据紧急指针的值提取出紧急数据,并直接交付给应用进程,而不必在接收缓存中排队等待,从而确保关键信息(如中断信号)能被优先处理
- 选项字段(长度可变,最大40字节)用于扩展协议功能,支持多种高级特性。其中,最大报文段长度(MSS)选项指明TCP数据载荷部分的最大长度(不包括首部),用于协商双方能接受的单个数据段大小,避免IP分片;窗口扩大选项通过增加窗口大小的表示位数,支持更大的接收窗口,从而提高吞吐率和网络利用率;时间戳选项用于精确计算往返时延(RTT),并实现防止序号绕回(PAWS)机制,确保在长时间连接中不会因序号重复而产生混淆;选择确认选项(SACK)则允许接收方告知发送方哪些数据段已收到、哪些丢失,从而实现更高效的重传机制,提升传输可靠性与性能。
- 填充 用于若选项字段的长度加上20字节固定首部的长度不能被4字节整除时,需要填充相应数量的比特0,以确保首部长度能被4字节整除。
5.3.2 “三报文握手”建立TCP连接¶
TCP运输连接有以下三个阶段:
- 通过“三报文握手”来建立TCP连接。
- 基于已建立的TCP连接进行可靠的数据传输。
- 在数据传输结束后,还要通过“四报文挥手”来释放TCP连接。
这一节阐述 “三报文握手” 建立TCP连接的过程
客户端从CLOSED状态开始,主动发送一个SYN=1、seq=x的连接请求报文段,进入SYN-SENT状态;
服务器收到后,若同意建立连接,则回复一个SYN=1、ACK=1、seq=y、ack=x+1的确认报文段,进入SYN-RCVD状态;
TCP规定同步标志位SYN被设置为1的报文段(例如TCP连接请求报文段和TCP连接请求确认报文段)不能携带数据,但要消耗掉一个序号。
TCP规定普通的TCP确认报文段可以携带数据,但如果不携带数据,则不消耗序号。
客户端收到后,再发送一个ACK=1、seq=x+1、ack=y+1的确认报文段,双方均进入ESTABLISHED状态,连接正式建立,之后可进行可靠的数据传输。
我们这个过程是否有疑问,为什么还需要第三个报文段?回应确认之后就直接建立连接不就行了吗
如果采用两次握手,当客户端发送的连接请求报文因网络延迟而未能及时到达服务器时,该报文可能在后续某个时刻突然到达。此时,服务器误认为是新的连接请求,会发送连接确认并进入“连接已建立”状态,但客户端早已放弃该连接,不会回应,导致服务器白白维持一个无效连接,造成资源浪费。
而三次握手通过要求客户端对服务器的确认进行再次确认(ACK),确保双方都明确对方当前有建立连接的意愿,从而有效防止此类问题,避免资源被错误占用。
5.3.2 “四报文挥手”释放TCP连接¶
当客户端主动关闭连接时,它发送一个FIN=1、ACK=1的报文段,进入FIN-WAIT-1状态;
服务器收到后回复ACK=1,确认收到关闭请求,客户端进入FIN-WAIT-2状态,服务器则进入CLOSE-WAIT状态,表示可以继续接收数据但不再发送。
当服务器完成数据传输后,也发送一个FIN=1、ACK=1的报文段,进入LAST-ACK状态;客户端收到后回复ACK=1,进入TIME-WAIT状态,并等待2MSL(最大报文生存时间)以确保最后一个ACK被对方收到,防止旧连接的报文干扰新连接,之后才彻底关闭连接。服务器在收到最终确认后进入CLOSED状态。
TCP 保活计时器
当TCP连接建立后,若双方长时间没有数据交换(例如客户端主机出现故障或网络中断),连接可能处于“空闲但无响应”的状态。为防止此类无效连接长期占用资源,服务器端会启动保活计时器(通常为2小时)。当计时器到期时,服务器开始发送TCP探测报文段,每隔75秒发送一次,用于检测对方是否仍然存活。若连续发送10个探测报文段后仍无客户端响应,服务器将认为客户端所在主机已发生故障,于是主动关闭该连接,释放相关资源。
5.3.3 TCP的流量控制¶
在TCP连接中,发送方可能持续发送大量数据,而接收方的应用程序可能因忙于其他任务,无法及时从接收缓存中取走数据。若发送速度过快,接收缓存将被填满并发生溢出,导致数据丢失。为避免这种情况,TCP提供了流量控制(Flow Control)机制,通过接收方在报文段中通告其当前可用的接收窗口大小(rwnd),动态通知发送方可发送的数据量。发送方根据该窗口值调整发送速率,确保不会超过接收方的处理能力,从而防止接收缓存溢出,保障数据可靠传输。
主机A向主机B发送数据,初始时B的接收窗口(rwnd)为400,表示可接收最多400字节的数据。A连续发送多个报文段,当B接收到前两个报文段后,其接收缓存逐渐填满,可用空间减少,于是向A发送ACK=201、rwnd=300的确认报文,通知A后续只能发送300字节的数据。A收到该信息后,调整发送窗口大小为300,避免继续发送过多数据导致接收方缓存溢出。此后A仅在允许范围内发送数据,确保发送速率与接收方处理能力匹配。
当接收方B的接收缓存已满时,会向发送方A发送一个rwnd=0的报文段,表示暂时无法接收数据;此时A停止发送。但如果这个零窗口通知在传输过程中丢失,B仍处于等待A发送数据的状态,而A因未收到非零窗口通知也一直等待,双方陷入互相等待的死锁局面。
为打破这一僵局,TCP为每个连接设置了持续计时器(Persistent Timer):只要一方收到对方的零窗口通知,就启动该计时器;当计时器超时后,发送方会主动发送一个仅携带1字节数据的“探测报文段”,询问对方当前窗口大小;若对方回复的窗口仍为0,则重新启动计时器;若窗口变为非零,则恢复正常通信。
A发送的零窗口探测报文段到达B时,如果B此时的接收窗口值仍然为0,那么B根本就无法接受该报文段,又怎么会针对该报文段给A发回确认呢?
实际上TCP规定:即使接收窗口值为0,也必须接受零窗口探测报文段、确认报文段以及携带有紧急数据的报文段
如果零窗口探测报文段丢失了,还会打破死锁的局面吗?
回答是肯定的。因为零窗口探测报文段也有重传计时器, 当重传计时器超时后,零窗口探测报文段会被重传。




















