计算机网络:运输层

基本概念

运输层负责将应用层的报文(message),切分为多份并分别打包成报文段(segment),然后传递给网路层。TCP和UDP即为运输层协议。

网络层协议为IP,服务方式为尽力而为,但不保证任何东西。

多路

由于一台主机上会有多个进程使用socket,所以要思考怎么实现正确的包装和交付。

多路分解

segment中有几个字段,接收端的运输层检查这些字段,识别出socket并将数据交付到达该socket。

多路复用

从主机的不同socket收集数据块,并为每个数据块封装上首部的信息,生成segment,再将其传递到网络层。

实现

segment:

1
2
3
4
<-------------------32bit-------------------->
[source_port_number | destination_port_number]
[other_header]
[data]

UDP

为什么会选择udp?

  • 因为udp本身对数据没什么控制,与tcp不同。应用层可以更精细地控制
  • 无需连接时间
  • 无连接状态
  • 首部开销小

像视频通话、电话这种可以容忍丢包的应用,使用udp会更合适

udp的segment:

1
2
3
4
<---------32bits--------->
[source_port | desti_port]
[ length | checksum ]
[ data ]

通过校验和(checksum),udp在运输层提供差错检测,但无法恢复差错,只能丢弃问题的segment并发出警告。

TCP

可以先去看看后面的可靠数据传输原理。

客户与服务器的TCP连接需要三次握手(three-way handshake)。用户通过socket将数据传递,接着数据被TCP控制,存到发送缓存(send buffer)中。

TCP报文结构

其报文结构如下:

  • Sequence number 即为segment的序列号
  • acknowledgment number 即为segment的确认号

    主机A填充进segment的确认号是主机A期望从主机B收到的下一字节的序号。

  • Header length 用来表示报文头部的长度,当 Options 为空时,长度为20。
  • Flag field 包含6个bit。其中 ACK bit 表示应答客户端是否成功接收,RST,SYN,FIN 用于连接设置。
  • Receive window 用于流控制。

传输机制

TCP采用超时重传机制。需要有往返时间估计的方法。

RTT:TCP会在某个时刻通过发送样本RTT来测量往返时间。对于一个新测出来的RTT,会通过加权平均以前RTT的估计值和现在的RTT来更新现在RTT的估计值,再用DevRTT计算出超时间隔。

除了rbt3.0实现的基本功能外,大部分TCP还会进行以下的修改:

  • 超时间隔加倍:实现了一定程度的拥塞控制。当发生超时时,有可能是路径上有过多的packet,而此时按照原来的超时间隔重发,可能会加剧拥塞
  • 快速重传:当接收方收到一个序号大于预期的segment,说明发生丢包,接收方会向发送方产生一个冗余ACK(duplicate ACK)。如果发送方收到相同数据的3个冗余ACK,会将它作为丢包的指示,执行快速重传。

TCP还提供流量控制服务(flow-control service),注意其与拥塞控制(congestion control)不同。前者本质是一个速度匹配服务,即使发送方的发送速率与接收方的读取速率相同。后者是因为ip网络的拥塞,而遏制发送方。

TCP三次握手具体实现

  • 第一步:发送SYN segment,在首部的SYN比特被置为1,序号字段为client_isn(客户随机选择的初始序号)。
  • 第二步:服务器收到SYN sesgment,为该tcp连接分配tcp缓存和变量(可能导致洪泛攻击),向客户端发送允许连接的SYNACK segment,该segment的SYN bit被设为1,确认号字段被设为client_isn+1,序号字段被设为server_isn。
  • 第三步:客户收到SYNACK segment,为该连接分配缓存和变量,发送segment,其确认字段被设为server_isn+1,SYN bit设为0,并且可以负载需要传输的数据。

TCP结束连接

当客户进程发出关闭连接的命令,用户的TCP发出一个FIN bit为1的segment,服务器接收到后发送ACK,接着也发送一个FIN bit为1的segment,最后客户接收并发送ACK,即完成全部连接资源的释放。

TCP拥塞控制

TCP的拥塞控制机制需要额外跟踪一个变量,即拥塞窗口,用cwnd表示。假设TCP接收缓存足够大,即不会出现接收窗口大小限制发送速率,只考虑cwnd的限制。此时发送速率为cwnd/RTT byte/s

TCP感知出现拥塞:出现超时/收到3个冗余ACK

TCP拥塞控制算法

慢启动

TCP连接开始时,cwnd的值设为一个MSS,即发送速率为MSS/RTT,每当传输的segment都被确认,cwnd的大小加一倍。所以其实传输速率为指数增长,并不慢!

如果存在超时导致的丢包,则发送方将cwnd设为1并重新开始,将慢启动阈值(ssthresh)设为开始拥塞时的cwnd的一半。

当重新启动时,可能超过/到达ssthresh,会结束慢启动模式,转到拥塞避免模式,更谨慎地增加cwnd

如果检测到3个冗余ACK,TCP执行快速重传并进入快速恢复模式

拥塞避免模式

每个RTT只将cwnd的值加一个MSS,实现线性增长。结束增长和慢启动一样。不同的是,检测到3个冗余时,cwnd变为一半,将ssthresh记录为cwnd值的一半。

快速恢复

对于引起TCP进入快速恢复状态的缺失segment,对于每个冗余ACKcwnd的值增加一个MSS。出现超时则和慢启动和拥塞避免一样。

可靠数据传输原理

构造可靠数据传输协议

这里的实现比较复杂,通过rdt(reliable data transfer)协议3.0来体会各个部分的作用。注意这里使用状态机 + 伪代码的方法来描述。

rdt3.0 是一个用于可靠数据传输的协议,主要应对 丢包比特错误 的问题。它引入了超时机制、序列号和确认(ACK)机制来确保可靠性,基于 rdt2.x 的基础,增加了对丢包的处理。

以下是 rdt3.0 的伪代码描述,分为发送方和接收方。

发送方的伪代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
state: WAIT_FOR_CALL_FROM_ABOVE

while true:
if event == from_upper_layer: // 从上层接收到数据
create_packet(data, seq_num) // 创建数据包,包含数据和序列号
send(packet) // 发送数据包
start_timer() // 开始定时器
state = WAIT_FOR_ACK

state: WAIT_FOR_ACK

while true:
if event == timeout: // 超时未收到 ACK
resend(packet) // 重新发送数据包
start_timer() // 重启定时器

if event == receive_ACK and is_corrupt(ACK) == false:
if ack_seq_num == seq_num: // 收到正确的 ACK
stop_timer()
seq_num = (seq_num + 1) % 2 // 切换序列号
state = WAIT_FOR_CALL_FROM_ABOVE // 返回等待上层调用状态

接收方的伪代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
state: WAIT_FOR_0_FROM_BELOW

while true:
if event == receive_packet and is_corrupt(packet) == false:
if packet.seq_num == 0: // 收到序列号为 0 且未损坏的数据包
deliver_data(packet.data) // 传递数据到上层
send_ACK(0) // 发送 ACK 0
state = WAIT_FOR_1_FROM_BELOW // 切换到等待序列号 1 的状态
else:
send_ACK(1) // 序列号不匹配,重复发送 ACK 1 表示之前已经收到过该包

if event == receive_packet and is_corrupt(packet) == true:
// 数据包损坏,忽略不做处理(自动依赖发送方超时重传)

state: WAIT_FOR_1_FROM_BELOW

while true:
if event == receive_packet and is_corrupt(packet) == false:
if packet.seq_num == 1: // 收到序列号为 1 且未损坏的数据包
deliver_data(packet.data) // 传递数据到上层
send_ACK(1) // 发送 ACK 1
state = WAIT_FOR_0_FROM_BELOW // 切换到等待序列号 0 的状态
else:
send_ACK(0) // 序列号不匹配,重复发送 ACK 0 表示之前已经收到过该包

if event == receive_packet and is_corrupt(packet) == true:
// 数据包损坏,忽略不做处理(自动依赖发送方超时重传)

关键机制

  • 序列号: 发送方和接收方使用 0 和 1 两个序列号来区分重复数据包。
  • ACK(确认): 接收方向发送方发送 ACK,表明接收到的数据包是正确的。
  • 超时重传: 发送方在一定时间内未收到 ACK 时,会重传数据包。

流水线可靠数据传输协议

rdt3.0本质为停等协议,我们希望在等一个packet的ACK时,可以同时发送其他packet,可以看作是将packet都填充到一个流水线上。实现需要增加以下机制:

  • 增加序号范围:显然之前的0和1是不够的,每个输送的packet都需要有一个唯一的序号
  • 发送方缓存已发送但未确认的packet
  • 选择处理丢包/损失的方法:GBN(Go-Back-N),SR(Seletive Repeat)

拥塞控制原理

拥塞原因

  • 发送速度达到吞吐量上限
  • 发生超时,重传分组过多导致。当持续升高发送速率,传输速率甚至可能不增加(重传分组也在增加),浪费了传输能力。
  • 一个分组在一个路径上被舍弃掉时,在舍弃之前的传输容量被浪费了

拥塞控制方法

  • 端到端控制,端系统通过一些网络拥塞的迹象来决定拥塞控制
  • 网络辅助控制,路由器向发送方提供关于网络拥塞状态的显式信息。一般有两种反馈方式。第一种为直接反馈,直接向发送方发送阻塞分组。第二种为标记发送方到接收方的分组的某个字段来反馈。

计算机网络:运输层
https://pactheman123.github.io/2024/09/12/运输层/
作者
Xiaopac
发布于
2024年9月12日
许可协议