6.1 传输服务

为什么需要传输层:

  1. IP 包不可靠
  2. IP 包大小有限
  3. IP 无法区分应用程序

功能:

  • 提供高效,可靠,高效的数据传输服务
  • 进程到进程
  • 提供多个 SAP

C/S 模型

服务器:管理资源,被动接受请求

客户端:主动发送请求

image-20210610004715479

通信流程:

image-20210610004739893

寻址:端口号

作为服务访问点。位于传输层包头。

复用

不同的端口可以使用同一个底层接口。

SCTP:让一个连接可以多个接口传输,例如同时用 Wifi 和以太网传输。

差错控制:自动请求重传、滑动窗口

缓存策略:

  1. 固定缓存
  2. 动态缓存链表
  3. 大型缓存

Max-Min Fairness

基本含义:使得资源分配向量的最小分量的值最大,防止任何网络流被 “饿死’,同时在一定程度上尽可能增加每个流的速率。

原则:一个信道上,在满足最小需求的前提下,各流尽量均分带宽;

如果增加任何一个参与者 (分量) 的带宽,将导致其它的具有相同或者更少带宽的参与者 (分量) 的带宽下降,说明此时即满足了最大最小公平性。

收敛性:尽快达到理想分配

例子:假设某互联网公司净利润 120 万元,有员工 A,B,C,D 和老板 E,F,员工觉得自己至少应该分到 12 万,老板觉得给了你们工作已经是你们的福报了,你们每人六万,剩下归我和我老婆。也即需求分别是:12, 12, 12, 12, 48, 48.

结果社会主义改造来了。

第一轮:先平均分,120/6=20, 每人分到 20 万,但是员工的需求只有 12 万:

A   B   C   D   E   F
12  12  12  12  20  20

多出来 8x4=32 均分给老板:20+16 20+16 = 36:

A   B   C   D   E   F
12  12  12  12  36  36

这样员工满意了。

6.2 传输层协议基础

6.3 拥塞控制

拥塞控制

  1. 发现拥塞
  2. 通知源主机、或隐式通知(源主机自己判断)
  3. 源主机动态调节速率

image-20210610010009139

源主机如何动态调整带宽

image-20210610133353513

AIAD 方式:线性增减。

MIMD 方式:指数增减。

这两种会反复震荡,难以收敛。

AMID 方式:线性增长,指数减少,容易收敛。

image-20210610133609644

实际做法:调整窗口大小。

6.4 UDP

DNS、SNMP、DHCP、RIP、RTP 使用 UDP。

  • 无连接,不可靠
  • 简单高效
  • 基于 IP 的复用、减复用

数据包格式:

image-20210610133942176

校验和是整个数据(伪报头,报头,数据),可选。保留消息边界

伪报头:只用于校验和,不传输。传输的时候去掉 Padding。

image-20210610134945152

6.5 TCP 传输控制协议

  • 面向连接
  • 可靠传递(ARQ,自动请求重传)
    • ACK
    • 校验和
    • 超时重传
    • 序列号
    • 滑动窗口流控
  • 拥塞控制
  • 字节流,不保留消息边界(应用层自己判断)

TCP 报文段

单位 4B。

MSS:最大段长度,是报文最大数据长度。

滑动窗口大小可变,用于流量控制。

image-20210610135852673

端口号各两字节。

序号:第一个数据字节的编号。32bit。

确认序号:准备接收的下一个字节的编号。32bit。

TCP 头长:4 字节为单位。

窗口大小:接收方告诉发送方的限制字节数。

校验和:伪报头 + 整个报文段

image-20210610140246653

注意序号不是单个递增,而是根据收到的字节数递增。

控制字段

image-20210610140355623

URG:紧急数据,例如中断信号。配合紧急数据指针(Urgent pointer)使用,指向紧急数据的末尾。

PSH:立即发送数据。避免等待。

SYN:同步序列号

ACK:ACK 字段有效

FIN:断开连接

ECE,CWR

ECN:拥塞通知

ECE:通知有拥塞

CWR:报告收到了拥塞通知

SACK(选择性 ACK),最多能同时确认三段范围。用于选择重传。

TCP 连接的建立

两端:

  • 服务器:被动方
  • 客户端:主动方
  1. 第一次握手:客户端提出建立请求,设置控制位 SYN=1,并携带数据的起始序号 seq:x
  2. 第二次握手:服务端响应连接,设置控制位 SYN=1, ACK=1, 并携带数据的起始序号(服务器发送序号)seq: y, 确认序号:ack: x+1(因为第一步消耗一个序号)
  3. 第三次握手:客户端确认,设置控制位 ACK=1,发送序号 seq: x+1,确认序号 ack: y+1(第二步也消耗一个序号)
sequenceDiagram
    participant client
    note right of server: tcp 三次握手
    client ->> server: syn=1
    server -->> client: syn+ack
    client ->> server: ack

    note right of server: tcp 四次挥手
    client ->> server: fin
    server -->> client: ack
    loop
       server -->> server: server handle left data 
    end
    server -->> client: fin
    client ->> server: ack

image-20210610231146657

(a):常规情况

(b):对等通信(不考虑 Client、Server 区分)的情况,两边同时建立连接。这种情况,双方约定的起始序号一样(x+1, y+1),所以不会导致建立两个连接的错误。

起始序号的确定:根据当前时钟参照

崩溃之后尚在网上的数据

通过 MSL(最大段生命期)机制,超过一定时间就销毁包,防止主机挂掉之后,包仍然在网上游荡。

image-20210611002628077

数据传输阶段

例子:

客户端连续发送两个报文段,序号分别是 8001, 9001,大小为 1000B

服务器进行累计确认,ack: 10001。seq': 15001,大小为 2000B

客户端:确认,2000B,ack: 17001,rwnd: 10000,设置窗口字节大小

TCP 连接释放

客户端释放:

  1. 客户端设置 FIN=1, seq: x
  2. 服务器设置 FIN=1, ACK=1,ack: x+1,seq: y(第一步消耗一个字节序号)
  3. 客户端设置 ACK=1,seq: x+1, ack: y+1(第二步消耗一个字节的序号)

image-20210611002706351

四步释放 - 半关闭连接:

  1. 客户端释放请求 FIN=1, seq: x, ACK=1
  2. 服务器还没发完,所以只释放 C->S 方向。所以只回应 ACK=1。seq: y, ack: x+1.
  3. 服务器继续发送数据,客户端回 ACK
  4. 服务器发完了,可以关闭了。seq: z, ack: x+1, ACK=1, FIN=1
  5. 客户端释放接收缓存,seq: x+1, ack: z+1, ACK=1

image-20210611002647512

TCP 的完整状态机

流控和滑动窗口

通过 WIN 字段通知自己的窗口剩余。当收到 WIN=0 可知对方窗口空了。之后再次收到 WIN > 0(窗口更新通知),可知可以继续发送了。

当窗口为 0 时:

  • 正常分片不能发
  • 紧急数据可以发
  • 可以定时重发 1B 报文,通知对方继续工作(防止互相等待,死锁)

实际窗口大小:

  • 受限于接收方窗口
  • 受限于拥塞状况

Nagle’s 攒一攒算法

解决效率低下(Overhead:TCP head + IP head = 40B)

发送方用 Nagle 算法改进效率:

  • 积攒到 MSS 的一半再发
  • 上一分片的 ACK 已经收到了,那么还是得发,不能让对方等着

Clark 方法

解决接收方窗口更新量太少(Silly Window Syndrome)

  • 接收方就不要通知窗口更新了,除非自己的缓存达到 MSS 或者缓存的一半。

差错控制

ARQ:

  • Checksum
  • ACK
  • 超时重发(RTO,重传超时时间)

累计 ACK:多等一会儿,没有多的包了,再进行确认。

快速重传:发送方连续收到几个相同的 ACK,这时提前重传那个 ACK 对应的报文,而不是等超时再重传。

数据破坏:TCP 采用的方案是不回任何信息,让对方超时重传。

ACK 丢失:由于采用累计确认,所以丢失的不需要重发。

计时器管理

  • 重传计时器
  • 持久计时器(persistence):用于零窗口互相等待时打破死锁。当得知零窗口时启动。
  • 存活计时器(keep-alive):心跳包
  • 等待计时器(time-wait):释放连接的主动方等待一段时间,等数据都超时再关闭连接。

重传时间(RTT)的设定

  • 太长:低效
  • 太短:重复发包

解决方法:

动态调整。new RTT = a (RTT) + (1-a)(new sample),a 是平衡因子。

重传超时(RTO)

RTTVAR = b(RTTVAR) + (1-b)|SRTT-Sample|

RTO = SRTT + 4*RTTVAR

Karn 算法:重传的包不用于估计,每次重传,RTO 加倍。

拥塞控制

隐式通知,TCP 发送方自己看着办。发送窗口受限于拥塞窗口大小。TCP 调整速率基于 AIMD 方法。

拥塞窗口(CWND):网络目前能消化的字节数。

发送窗口大小取 MIN (接收方 WIN, CWnd)

发送方有效窗口:最大窗口 - 发送未确认 = 最大窗口 - 发送的 + 确认的

拥塞检测:通过丢包判断(超时未收到 ACK),没有拥塞就增加速率,有拥塞就降低速率。

ACK 时钟:反应低速链路的速率情况。发送方依据它,来避免连接低速路由时排队严重。平滑数据量。

拥塞控制算法

这篇文章 不错。

慢启动(Slow-Start)法:初始 CWnd=1。每收到一个 ACK,CWND«1。指数级增长速率。当连接建立时,拥塞窗口被初始化为一个报文段大小,每收到一个 ACK,拥塞窗口就会增加一个报文段,发送方取拥塞窗口与通过窗口的最小值作为发送的上限。

拥塞窗口说白了就是阈值

【例子】 假设回环时间 10ms,无拥塞,接收窗口大小 24KB,分片大小 2KB,多久能满窗口发送?

【分析与解答】

初始 CWND=2KB,需要经过左移(平方) $\log_2 (12)≈3.58$ 次,向上取整 4 次,每次耗时 10ms,所以需要大概 40ms 达到最大窗口速率。

【例子】 假设拥塞窗口 18KB,最大分片大小 1KB。传着传着超时了,后来四个包都成功传输,此时窗口大小多大?

【分析与解答】 阈值 18KB,超时,阈值减半,变成 9KB,重新慢启动。1«4=16>9,因此经过四次成功传输,窗口大小变成了 9KB

在 TCP 的拥塞控制算法中,从慢启动开始,拥塞窗口按指数增长,即如果每一轮发送均收到 ACK,则拥塞窗口变为 2 倍;拥塞窗口增加到阈值时,增长速度减缓,改为线性增长(AI:每一轮发送成功后,拥塞窗口增加一个 MSS) 。当发生超时时,阈值=当前拥塞窗口值的一半,而拥塞窗口则降为 1 个 MSS,开始新的慢启动。

AIMD 法:收到 ACK,CWND+=1/CWND (缓慢增快速减).

一般有一个阈值,结合使用两种方法。达到阈值,进入拥塞避免(Congestion Avoidance)阶段,用 AIMD 法。直到拥塞开始。

阈值也是动态调整的,便于收敛。

TCP Tahoe:连续三个重复 ACK,或者超时之后,就从慢启动重来。

image-20210611012017091

TCP Reno 快速恢复:连续收到三个重复 ACK,执行快速重传,然后进入快速恢复,阈值减少到二分之一窗口大小,CWND=Threshold。

6.6 性能

6.7 延迟网络