计算机网络 运输层
本文将介绍计算机网络中的运输层。
一、运输层的功能
网络层为两台主机之间提供通信服务(点到点);
运输层为两台主机中的应用程序之间提供通信服务(端到端)
运输层会对收到的报文进行差错检测
具有复用和分用的功能
运输层向高层用户屏蔽了下层的细节,表现为一条端到端的逻辑通信信道
- 当运输层使用面向连接的 TCP 协议时,运输层会向上层提供一个全双工的可靠信道
- 当运输层使用无连接的 UDP 协议时,运输层会向上层提供一个不可靠的信道
根据使用的协议不同,传输单位分别为 TCP 报文段和 UDP 用户数据报
二、端口
正如 IP 地址标识主机一样,端口用于标识主机上的应用程序。两个计算机中的应用程序要相互通信,不仅需要知道双方的 IP 地址,同样还要知道双方的端口号。
端口又被称为套接字、socket、插口,它由端口号和 IP 地址组合而成。
三、复用和分用
- 复用:所有应用程序都可以将数据交给同一个运输层
- 分用:运输层会在接收到数据后将其分别交付给对应的应用程序
三、UDP
UDP 在网络层的基础上增加了复用、分用、差错检测。
UDP 具有以下特点:
- 无连接:在发送数据之前不需要建立连接,减少了开销和时延
- 支持一对一、一对多、多对一和多对多通信
- 不保证可靠:尽最大努力交付,不保证传输的可靠性
- 面向报文:对于上下层的数据,UDP 不会对其进行划分或合并,而是原封不动地交付
- 没有拥塞控制:UDP 会以恒定的速率发送数据,而不关心网络是否拥塞
- 首部开销小
四、TCP
1. 特点
面向连接:传送数据前,需要先建立连接,传送数据后,需要释放连接
一对一:每条 TCP 连接有且只有两个端点
可靠交付:通过 TCP 连接传送的数据可以保证无差错、不丢失、不重复、按序到达
面向字节流:TCP 会将上层的报文视为一连串的无结构的字节流,根据网络情况将其划分或合并后传输
如果数据过大,TCP 可能划分为多块再发送;
如果数据过小,TCP 可能等待累积足够的量后再一起发送
全双工通信:TCP 允许通信双方在任何时候都能发送数据
连接的两端都设有发送缓存和接收缓存,用于临时存放双向通信的数据。
- 在发送时,只需要将数据 “丢给” TCP 的缓存,TCP 会在合适的时候将数据发送出去
- 在接收时,TCP 将收到的数据放入缓存,应用程序可以在合适的时候读取数据
2. 端口与连接
每条 TCP 连接有且只有两个端点,每个 TCP 连接唯一地被两个端口所确定。
3. TCP 报文段首部
TCP 传送的数据单元是报文段,一个报文段可以分为首部和数据两部分。
其中,首部包含:
源端口、目的端口:各占两个字节
序号 seq:在 TCP 连接中传送的字节流中,每一个字节都会按照顺序编号,报文段首部的序号位应填入报文段第一个字节的序号
序号范围为 0 ~ 2^32 - 1
确认号 ack:表示期望收到对方下一个报文段的序号值
B 正确收到 A 发送的报文段,其 seq 为 501,数据长度是 200,这表明 B 正确收到了 501 ~ 700,因此应该发送 ack 值为 701 的确认报文段。
ack 为 N 时表示:到 N-1 为止的所有数据都已正确收到
数据偏移:指出数据起始处的位置的序号和报文段序号的差值,实际上是指出首部长度
保留:保留位
紧急 URG:该字段用于告诉系统该报文段中有紧急数据,应该尽快传送
确定 ACK:置为 1 时,表示 ack 有效
推送 PSH:置为 1 时,表示接收方应该尽快交还响应
复位 RST:置为 1 时,表示 TCP 连接中出现严重差错,必须释放连接后重新建立
同步 SYN:在连接建立时用于同步序号
停止 FIN:用于释放连接
窗口:表示接收方目前允许对方发送的数据量
校验和
紧急指针:当 “紧急 URG” 为 1 时才有意义,指向本报文段中的紧急数据
选项:最大报文段长度、窗口扩大选项、时间戳、选择确认等信息
4. 可靠传输
(1) 停止等待协议
停止等待,就是发送完一个分组之后就停止发送,等待对方的确认,在收到确认后再继续发送。
无差错时:
- A 发送分组,等待确认
- B 收到分组,发送确认
- A 收到确认,继续发送
发生差错时:
A 发送分组
B 接收到分组后发现其在传输过程中发生差错,丢弃后什么也不做
在可靠传输的协议中,也可以在检测出有差错时发送 “否认报文”,这样做的好处是能够让发送方及早知道出现了差错,但由于这样做会使协议复杂化,现在实用的可靠传输协议都不使用这种 “否认报文” 了。
A 发送分组后超过一定时间没有收到确认,重新发送分组
又被称为超时重传
B 收到分组,发送确认
A 收到确认,继续发送
确认丢失:
- A 发送分组,等待确认
- B 收到分组,发送确认,确认丢失
- A 发送分组后超过一定时间没有收到确认,重新发送分组
- B 收到重复分组,丢弃,重传确认
- A 收到确认,继续发送
确认迟到:
- A 发送分组,等待确认
- B 收到分组,发送确认,确认迟到
- A 发送分组后超过一定时间没有收到确认,重新发送分组
- B 收到分组,发送确认
- A 收到确认,继续发送
- A 收到迟到的确认,收下但什么也不做
(2) 连续 ARQ 协议
连续 ARQ 协议会维护一个滑动窗口,滑动窗口内的分组将被 “连续发送”,等待对方的确认,移动滑动窗口继续发送。
接收方会采用 “累积确认” 的方式,只发送对按序到达的最后一个分组的确认,表示 “这个确认的分组之前的分组都已正确收到”
- 全收到情况:
- A 发送 1、2、3、4、5 分组
- B 收到全部分组,发送 5 分组的确认
- A 收到 5 分组的确认,移动滑动窗口,继续发送 6、7、8、9、10 分组
- 半收到情况:
- A 发送 1、2、3、4、5 分组
- B 收到1、2、4、5分组,发送 2 分组的确认
- A 收到 2 分组的确认,移动滑动窗口,继续发送 3、4、5、6、7 分组
(3) TCP 的可靠传输实现
TCP 的可靠传输类似连续 ARQ 协议,但是更加复杂。
TCP 双方各自会维护两个以字节为单位的窗口,分别叫做发送窗口和接收窗口。
假设有 A 和 B,讨论 A 作为发送方、B 作为接收方的情况(实际上 TCP 是全双工的,A 和 B 都同时是发送方和接收方)。
发送窗口:
A 会根据 B 的报文段中的窗口(决定窗口大小)和确认号(决定窗口位置)维护一个发送窗口,如图:
- 对于发送窗口左侧的数据,它们已经确认发给接收方,因此会被丢弃
- 对于发送窗口内的数据,每隔一段时间,发送方会将它们连续发送
- 对于发送窗口右侧的数据,它们不允许被发送,因为接收方暂时无法接收它们
接收窗口:
B 会维护一个接收窗口,并将接收窗口的大小通知给 A。
- 对于接收窗口左侧的数据,它们已经交付,因此会被丢弃
- 对于接收窗口内的数据,接收方会接收数据并暂存在窗口中,一旦构成 “从最左侧开始的连线”,便可以发送按序到达的最后一个数据的确认,更新接收窗口
- 对于接收窗口右侧的数据,不允许接收
5. 流量控制
(1) 什么是流量控制?
如果发送方将数据发送得过快,接收方可能来不及接收只能丢弃,导致这个过程中存在大量的无用流量。
(2) 流量控制的实现
接收方向发送方发送数据时,可以通过报文段首部中的窗口说明接收窗口的大小,从而让发送方调整发送窗口的大小。
6. 拥塞控制
(1) 什么是拥塞?
在计算机网络中的链路容量(即带宽)、交换节点中的缓存和处理机等,都是网络的资源。在某段时间,若对网络中某一资源的需求超过了该资源所能提供的可用部分,网络的性能就会变化,这种情况就是拥塞。
(2) 拥塞控制与流量控制
拥塞控制:
- 防止过多的数据注入到网络中,进而导致网络过载
- 全局性的
流量控制:
- 防止发送过多数据而接收方来不及接收
- 点对点的通信量的控制
(3) 拥塞窗口
TCP 的拥塞控制基于窗口,发送方将根据网络的拥塞程度维护一个 “拥塞窗口” 变量,它将影响发送窗口的大小。
发送窗口大小 = min(拥塞窗口, 接收方窗口)
(4) 拥塞窗口变化原则
- 如果网络未发生堵塞,应该将拥塞窗口增加,以提升网络的利用率
- 如果网络发生堵塞,应该将拥塞窗口减小,以缓解网络压力
(5) 对拥塞的判断
只要接收方的确认报文段超时,便可以认为网络出现了拥塞。
(6) 拥塞窗口增长的两个阶段
慢开始阶段:
如果一个 TCP 连接刚建立,此时并不清楚网络的情况,应该从小到大逐渐增大注入到网络中的数据量,即从小到大逐渐增大拥塞窗口
此阶段拥塞窗口将成倍增长
拥塞避免阶段:
- 该阶段的目的是让拥塞窗口缓慢增大
- 此阶段拥塞窗口将线性增长
(7) 避免拥塞误判
快重传机制:该机制的目的是区分拥塞和报文段缺失
- 接收方一旦接收到报文段,无论是否连续的,都应该立即发送目前已收到的最大报文段的确认
- 当发送方一旦连续收到三个重复确认后,便可以确定出现报文段缺失而非网络拥塞,发送方便可以立即进行重传
快恢复:一旦通过快重传机制确定是报文段缺失,发送方就可以快速增大拥塞窗口值
7. TCP 的连接建立
(1) 具体流程
TCP 建立连接的过程常被称为 三次握手,但它更准确的叫法是 三报文握手。
three way (three message) handshake
– RFC 793
在 TCP 协议中,主动发起连接请求的一端称为客户端,被动接收连接请求的一端称为服务端。
具体连接流程如下:
最初,服务端和客户端都会是 CLOSE(关闭) 状态
服务端创建传输控制块 TCB,准备接收客户端的连接请求,进入 LISTEN(监听) 状态
客户端创建传输控制块 TCB
第一个报文:客户端向服务端发送 “客户端同步报文段”,其中
SYN = 1; ACK = 0; seq = x
,进入 SYN-SENT(同步已发送) 状态SYN = 1; ACK = 0
表示此报文段为 “客户端同步报文段”seq = x
表示客户端发送的字节流的初始序号为 x
第二个报文:服务端如果同意建立连接,应该向客户端发送 “服务端确认 + 同步报文段”,其中
SYN = 1; ACK = 1; seq = y; ack = x + 1
,进入 SYN-RCVD(同步收到) 状态SYN = 1; ACK = 1
表示此报文段为 “服务端确认 + 同步报文段”seq = y
表示服务端发送的字节流的初始序号为 yack = x + 1
表示已经确认收到来自客户端的 “客户端同步报文段”
第三个报文:客户端收到服务端的 “服务端确认 + 同步报文段” 后,应该发送 “客户端确认报文段”,其中
ACK = 1; seq = x + 1; ack = y + 1
,进入 ESTABLISHED(已建立连接) 状态ACK = 1
表示 ack 有效seq = x + 1
表示客户端发送的字节流的初始序号为 x + 1ack = y + 1
表示已经确认收到来自服务端的 “服务端确认 + 同步报文段”
服务端收到 “客户端确认报文段”,进入 ESTABLISHED(已建立连接) 状态
两者可以开始通信
(2) 三报文 = 四报文
第二个报文实际上是 “服务端确认报文段” 和 “服务端同步报文段” 的结合,为方便合并成一个。
1 |
|
(3) 为什么需要那么多报文?
- 第一个报文发送且被接收后,服务端可以得出结论:客户端发送能力正常
- 第二个报文发送且被接收后,客户端可以得出结论:服务端接收能力、发送能力正常
- 第三个报文发送且被接收后,服务端可以得出结论:客户段接收能力正常
三个报文是让双方都能确保对方可信的最小代价。
8. TCP 的连接释放
(1) 具体流程
TCP 连接释放的过程通常被称作 四次挥手。
假设连接双方为 A 和 B,具体释放流程如下:
最初,A 和 B 都处于 ESTABLISHED(已建立连接) 状态
第一个报文:A 的上层应用程序要求 A 释放连接,A 发送 “连接释放报文段”,进入 FIN-WAIT-1(终止等待 1) 状态,等待 B 的确认
“连接释放报文段” 中
FIN = 1; seq = u
,FIN = 1
表示希望释放连接seq = u
表示已传输数据的最后一个字节的序号为 u - 1
第二个报文:B 收到 “连接释放报文段” 后,通知上层应用程序,发出 “确认报文段”,进入 CLOSE-WAIT(关闭等待) 状态
“确认报文段” 中
ACK = 1; seq = v; ack = u + 1
,ACK = 1
表示 ack 有效seq = v
表示已传输数据的最后一个字节的序号为 v - 1ack = u + 1
表示已经确认收到来自 A 的 “连接释放报文段”
TCP 连接进入半关闭状态,A 不再发送、B 不再接收、B 仍可发送、A 仍会接收
A 收到来自 B 的 “确认报文段”,进入 FIN-WAIT-2(终止等待 2) 状态,等待来自 B 的 “连接释放报文段”
第三个报文:若 B 的上层应用程序已没有要发送的数据,它会要求 B 释放连接,B 发送 “连接释放报文段”,进入 LAST-ACK(最后确认) 状态,等待 A 的确认
“连接释放报文段” 中
FIN = 1; ACK = 1; seq = w; ack = u + 1
,FIN = 1
表示希望释放连接ACK = 1
表示 ack 有效seq = w
表示已传输数据的最后一个字节的序号为 w - 1w >= v
,因为 B 的 CLOSE-WAIT ~ LAST-ACK 期间可能会继续发送一些数据ack = u + 1
表示已经确认收到来自 A 的 “连接释放报文段”这里必须重复之前已经发送过的确认号
第四个报文:A 收到 B 的 “连接释放报文段” 后,应该发送 “确认报文段”,进入 TIME-WAIT(时间等待) 状态,经过时间等待器 TIME-WAIT timer 后,A 进入 CLOSED 状态,撤销 TCB,结束 TCP 连接
“确认报文段” 中
ACK = 1; seq = u + 1; ack = w + 1
,ACK = 1
表示 ack 有效seq = u + 1
表示最后一次传输就是第一个报文,其序列号是 uack = w + 1
表示已经确认收到来自 B 的 “连接释放报文段”
B 收到 A 的 “确认报文段” 后,进入 CLOSED 状态,撤销 TCB,结束 TCP 连接
(2) 三次挥手
如果关闭的被动方 没有数据要继续发送 && 开启 TCP 延迟确认机制
,那么第二次、第三次挥手将会合并传输。
(3) 为什么有 TIME_WAIT ?
TIME_WAIT 出现在请求连接断开的发起方。
在 TCP 请求中,断开连接需要四次挥手,从而保证双方都希望关闭连接。
TIME_WAIT 发生在:A 发送关闭连接请求,接收到 B 的关闭连接确认,从 A 方出发的关闭流程结束;然后 A 接收到 B 的关闭连接请求,发送确认,并进入 TIME_WAIT 状态。
其存在是为了更加可靠的关闭连接,因为如果 A 的确认在请求中丢失,并且 A 直接关闭连接,则 B 将无法正常关闭连接。而如果 A 多等待一会儿,便可以在 B 重新尝试关闭连接时继续发出确认,帮助 B 关闭连接。
(4) 服务端大量 TIME_WAIT 的解决方案
- 如果是服务端上的爬虫程序、反向代理程序大量发起请求导致的,应该考虑修改配置使得服务器能够快速回收和重用 TIME_WAIT 的请求
- 如果是客户端往服务器的连接被服务器关闭导致的,应该避免使用短连接
参考
- 《计算机网络》