1. 运输层

运输层协议的位置:网络边缘/端系统

  • 发送:运输层将从发送方进程接收到报文转换为运输层分组,然后传递给网络层
  • 接收:运输层从网络层接收到的运输层分组提取数据,然后传递给接收方进程

运输层分组严格来说又叫报文段(segment)

区分运输层协议和网络层协议

  • 运输层协议的功能:提供了运行在不同主机上的进程之间的逻辑通信,如TCP和UDP
  • 网络层协议的功能:提供了主机之间的逻辑通信,如IP

类比

  • 应用层报文 = 信件
  • 进程 = 家庭成员
  • 主机 = 家庭
  • 运输层协议 = 家庭代表
  • 网络层协议 = 邮政服务

理解

  • 有两个家庭A和B,家庭成员之间需要通过信件互相通信,家庭A指派成员a作为家庭代表,家庭B指派成员b作为家庭代表,负责收集全部家庭成员的信去发送,同时将接收到的信分发给各个家庭成员,信件则通过邮政服务实现家庭到家庭之间的传递
  • a和b都是在各自的家庭进行工作的,并没有参与邮政服务的工作,因此运输层协议只是工作在端系统中
  • 邮政服务提供的是家庭到家庭之间的信件传递,对应于主机间的逻辑通信;a和b提供的是A家庭成员和B家庭成员之间的信件传递,对应于进程间的逻辑通信

端到端原则(end-to-end principle):是计算机网络设计中的一种设计哲学,强调将特殊功能和错误处理放置在网络边缘(应用层和运输层),而不是网络核心(网络层和链路层)

  • 简化网络设计:减少网络中间节点的复杂性,避免在网络中间引入过多的处理逻辑
  • 提升可靠性:将关键的错误检测和恢复功能放在终端设备上,使得网络的核心部分可以保持高效和简洁

与在较高级别提供特殊功能的代价相比,在较低级别上设置特殊功能可能是冗余的,不起作用的,甚至毫无意义和价值的

2. 多路复用与多路分解

多路分解(demultiplexing):将报文段中的数据交付到正确的套接字

多路复用(multiplexing):在源主机从不同套接字收集数据块,并为每个数据块封装上首部信息从而生成报文段,然后将报文段传递到网络层

上述类比

  • 多路复用:家庭代表收集每个家庭成员的信,然后一起投递到邮局
  • 多路分解:家庭代表从邮局取回很多信,然后分发给对应的家庭成员

多路的要求

  • 套接字需要有唯一的标识符:区分在同一网络节点上运行的不同进程
  • 报文段需要有特殊字段来指示交付的套接字:确保数据准确地传递到正确的进程

套接字(socket):是应用层和运输层之间的接口,相当于从网络向进程传递数据和从进程向网络传递数据的门户

  • UDP套接字:二元组(目的IP,目的端口号)
  • TCP套接字:四元组(源IP,源端口号,目的IP,目的端口号)

UDP源端口号由运输层自动分配,也可以通过bind函数手动关联一个端口号

3. UDP

3.1 UDP性质

UDP性质

  • 无连接的:发送方和接收方的运输层实体之间没有握手,发送方将分组传递给网络层之后就不再关心
  • 不可靠的:提供最少的服务,除了复用/分解功能和少量的差错检测,几乎没有对IP增加别的东西,不提供数据重传或顺序保证

UDP优势

  • 应用层控制更加精细:UDP不干涉也不关心数据发送过程,应用层可以完全自由选择发送的数据和发送的时间
  • 无需连接建立:UDP不需要建立连接,省略了连接建立、拆除和维护的步骤,减少了通信延迟
  • 无连接状态:UDP是不可靠的,不维护连接状态,也不跟踪连接的状态信息,减少了接收方和发送方的资源消耗
  • 分组首部开销小:UDP仅有8字节首部,而TCP有20字节的首部,节省带宽

UDP劣势:缺乏拥塞机制

  • 路由器分组溢出:当许多用户同时发送大数据量的UDP分组时,路由器和交换机可能会受到严重的负荷,造成网路性能下降和丢包
  • 挤垮TCP会话:UDP造成的网络拥塞会触发TCP的拥塞控制机制,从而大大减小TCP发送方的速率

3.2 UDP报文段

结构:首部4个字段,每个字段2字节

  • 源端口号:标识发送进程
  • 目的端口号:标识接收进程
  • 长度:整个报文段长度,即首部+数据的字节数
  • 检验和:验证数据在传输过程中是否发生了错误

检验和(checksum)

  • 发送方
    1. 填充与求和:将UDP报文段中所有16比特字相加,如果数据部分的长度不是16位的整数倍,则填充0
    2. 回卷:如果和超过16位,则将溢出的部分重新加回
    3. 反转:将最终的和按位取反
  • 接收方
    • 填充、求和、回卷:与上述一样
    • 检验:如果和是1111111111111111则表示报文段没有出差错,否则只要有一个比特是0则表示报文段出错

UDP检验和性质

  • 仅仅提供了差错检测功能,但是它对差错恢复无能为力,因此UDP接收方在发现差错时只是丢弃受损报文段,并没有提供可靠数据传输
  • 符合端到端原则的设计理念

4. 可靠数据传输原理

4.1 可靠数据传输协议

可靠数据传输协议(reliable data transfer protocol,rdt):

  • 可靠:没有数据损坏和数据丢失,并且数据按发送顺序进行交付
  • 抽象:可靠信道只是运输层为上层实体提供的抽象服务,实际上是通过较低层的不可靠信道传输,经过运输层的处理后才能确保数据的可靠性

基本函数

函数名调用者作用
rdt_send(data)应用层指示运输层发送方从应用层中获取数据
rdt_rcv(packet)网络层指示运输层接收方从网络层获取分组
make_pkt(data)运输层发送方将数据加上首部封装成分组
extract(packet, data)运输层接收方从分组中取出数据
udt_send(packet)运输层发送方将分组发送到网络层
deliver_data(data)运输层接收方将数据交付给应用层

有限状态机(Finite-State Machine,FSM):由状态、状态转移、事件和操作组成

  • 圆:状态
  • 箭头:状态转移,取决于事件和操作
  • 横线上方:引起状态转移的事件
  • 横线下方:实现状态转移的操作
  • Λ\Lambda:空,表示没有事件或没有操作

4.2 rdt1.0

rdt1.0:底层信道是完全可靠,不需要进行差错检测和修复

状态事件操作状态转移
发送端等待调用应用层调用封装分组并发送转移到自身
接收端等待调用网络层调用提取数据,交付应用层转移到自身

4.3 rdt2.x

4.3.1 rdt.2.0

自动重传协议(Automatic Repeat reQuest,ARQ)

  • 检测:发送方会将检测字段封装进分组中,接收方会根据检测字段进行差错检测
  • 反馈:肯定确认(positive acknowledgement,ACK)否定确认(negative acknowledgement,NCK),接收方需要向发送方发送ACK分组或NCK分组
  • 重传:接收方接收到NCK分组后,需要重传该分组

停等协议(stop-and-wait):发送方不会发送新分组,直到发送方收到来自接收方的肯定确认

rdt2.0:实现了差错检测,接收方通过ACK和NAK告知发送方分组情况

函数名调用者作用
isNAK(rcvpkt)和isACK(rcvpkt)运输层发送方判断发送的分组是否被正确接收
notcorrupt(rcvpkt)和corrupt(rcvpkt)运输层接收方判断接收的分组是否有差错

发送端

状态事件操作状态转移
等待调用应用层调用封装分组并发送转移到“等待确认”
等待确认接收ACK不执行任何操作转移到“等待调用”
等待确认接收NAK重传分组转移到自身

接收端

状态事件操作状态转移
等待调用分组没有差错提取数据,交付应用层,发送ACK转移到自身
等待调用分组出现差错发送NAK转移到自身

4.3.1 rdt.2.1

rdt2.0的局限性:没有考虑到ACK或NAK分组受损的可能性

不合适的处理方案

方案情况结果
发送方利用新分组询问接收方新分组也有可能差错无限循环确认
发送方可以修复差错需要增加足够的检验和比特首部过大
发送方重传分组可能在ACK的情况下依旧重传冗余分组

rdt2.1:让发送方对分组进行编号,将序号封装在首部字段,接收方仅需要根据序号就可以判断当前分组是否是冗余分组

函数名调用者作用
has_seq1(rcvpkt)和has_seq0(rcvpkt)运输层接收方确认当前分组的序号
notcorrupt(rcvpkt)和corrupt(rcvpkt)运输层接收方和运输层发送方判断接收的分组是否有差错

发送端

状态事件操作状态转移
0号等待调用应用层调用封装0号分组并发送转移到“0号等待确认”
0号等待确认接收NAK或者分组出现出错重传0号分组转移到自身
0号等待确认接收ACK并且分组没有出错不执行任何操作转移到“1号等待调用”
1号等待调用应用层调用封装1号分组并发送转移到“1号等待确认”
1号等待确认接收NAK或者分组出现出错重传1号分组转移到自身
1号等待确认接收ACK并且分组没有出错不执行任何操作转移到“0号等待调用”

接收端

状态事件操作状态转移
0号等待调用分组出现差错发送NAK转移到自身
0号等待调用分组没有差错但是收到1号分组发送ACK转移到自身
0号等待调用分组没有差错并且收到0号分组提取数据,交付应用层,发送ACK转移到“1号等待调用”
1号等待调用分组出现差错发送NAK转移到自身
1号等待调用分组没有差错但是收到0号分组发送ACK转移到自身
1号等待调用分组没有差错并且收到1号分组提取数据,交付应用层,发送ACK转移到“0号等待调用”

对称性:不同序号下操作逻辑是完全一致的

4.3.2 rdt2.2

rdt2.2:与rdt2.1的唯一差别在于ACK和NAK也封装了序号

4.4 rdt3.0

rdt2.x的局部性:如果分组丢失,那么发送方和接收方都无法做出响应来处理

rdt3.0:采用基于时间的重传机制

  • 重传:从发送方的观点来看,只要能处理好冗余分组,重传是一种万能的方案
  • 基于时间:依靠倒计时定时器(countdown timer)
    • 启动:每次发送一个分组都需要启动倒计时定时器
    • 中断:如果在指定时间量内没有得到肯定确认中断倒计时定时器,并重传分组
    • 重置:如果在指定时间量内得到肯定确认,重置倒计时定时器

发送端

状态事件操作状态转移
0号等待调用应用层调用封装0号分组并发送,启动定时器转移到“0号等待确认”
0号等待确认接收1号ACK或者分组出现出错不执行任何操作转移到自身
0号等待确认超时重传0号分组,启动定时器转移到自身
0号等待确认接收0号ACK并且分组没有出错中断计时器转移到“1号等待调用”
1号等待调用应用层调用封装1号分组并发送,启动定时器转移到“1号等待确认”
1号等待确认接收0号ACK或者分组出现出错不执行任何操作转移到自身
1号等待确认超时重传1号分组,启动定时器转移到自身
1号等待确认接收1号ACK并且分组没有出错中断计时器转移到“0号等待调用”

比特交替协议(alternating-bit):分组序号在0和1之间交替

5. 流水线

流水线技术

rdt3.0的局限性:由于停等协议,导致吞吐量很低

假设可用吞吐量R=1Gbps,RTT=30ms,分组长L=8000bits,忽略ACK的发送时延
发送方相邻两个分组的发送间隔是RTT+L/R=30.008ms
实际吞吐量为8000bits/30.008ms≈267kbps
宽带利用率只有可怜的267kbps/1Gbps=0.0267%(这还是在分组没有任何差错,不考虑协议处理时间,路由器的排队时延的情况下)

流水线技术(pipelining):允许发送方发送多个分组而无需等待接收方的肯定确认

  • 必须增加序号范围:跟踪多个分组,每个分组具有唯一标识符
  • 发送方和接收方需要缓存分组:发送方需要缓存尚未确认的分组以便重传,接收方也需要缓存尚未排序分组以便按顺序交付
  • 处理差错恢复:回退N步和选择重传

5.2 回退N步

回退N步协议(Go-Back-N,GBN):允许发送方发送多个分组而不需要等待确认,但流水线中未确认分组数量不能超过最大允许数N

  • 发送方基序号(base):最早未确认的分组序号
  • 下一个序号(nextseqnum):最小未使用的分组序号
  • 窗口长度(window size):长度为N,允许的已经发送但未被确认的最大分组数
  • 接收方基序号/期望序号(expectedseqnum):接收方期望收到的分组序号

如果分组序号字段的比特数是k,那么序号范围就是[0,2^k^],对序号的计算必须使用模2运算
初始化base,nextseqnum和expectedseqnum都为1

序号范围

  • [0,base-1]:已经发送且已经被确认的分组
  • [base,nextseqnum-1]:已经发送但还没有被确认的分组
  • [nextseqnum,base+N-1]:还没有发送的分组
  • [base+N,…]:不能进行操作的分组

GBN实现手段

  • 窗口判断:发送方通过if(nextseqnum<base+N)判断窗口是否已满,若窗口已满,发送方可能会将数据返回给上层并通知,也可能缓存数据等待窗口空闲
  • 累积确认(cumulative acknowledgement):接收方不会发送NAK,只会发送封装了expectedseqnum的ACK,表示已经正确接收到包括n在内的n之前的所有分组
  • 超时记录:发送方只使用一个定时器,用来记录最早的已发送但还未被确认的分组
  • 失序丢弃:接收方会丢弃一个正确接收但失序的分组,而不会缓存它

失序丢弃的可解释性:接收方在期望接收到n的时候接收到了n+1,哪怕接收方缓存了n+1,但是发送方不知道,因此发送方会在传送n之后再次传送n+1,接收方缓存n+1就没有意义了

GBN的本质(重点)

  • 发送方
    • 不处理损坏的ACK
    • 一直递增nextseqnum直到窗口已满
    • 一直发送nextseqnum直到超时才回退到base进行发送
    • base只在接收到正确的ACK才递增
  • 接收方
    • 不会发送NAK
    • 一直发送封装了最新正确分组序号的ACK
    • expectedseqnum只在接受到期望序号的分组才递增

发送端:

事件操作
应用层调用判断窗口是否已满
定时器超时重启定时器,并回退发送序号[base,nextseqnum-1]的分组
接收到损坏的ACK什么都不做
接收到正确的ACK更新base为ACK中的序号+1,如果没有需要发送的分组则中断定时器,否则重启定时器

接收端:

事件操作
收到的分组没有错误且是期望序号提取数据,交付应用层,封装并发送ACK,递增expectedseqnum
收到的分组有错误或乱序丢弃分组,封装并发送上一个ACK分组

5.3 选择重传

GBN协议的局限性:发送方必须从出错的分组开始重新发送所有后续分组,这可能导致大量无用数据的重传,特别是在高丢包率的网络中,带宽利用率会降低

选择重传协议(Selective Repeat,SR):发送方仅重传那些它怀疑在接收方出错的分组,接收方接收所有正确的分组,其中乱序的分组将被缓存

  • 发送方需要标记发送窗口中哪些分组已经被确认
  • 接收方需要标记接收窗口中哪些分组已经被缓存

接收窗口

  • [0,rcv_base-1]:已经交付的分组
  • [rcv_base,buffer_start-1]:期望但还未收到的分组
  • [buffer_start,buffer_end]:失序且被缓存的分组
  • [buffer_end+1,rcv_base+N-1]:可以接收的分组
  • [rcv_base+N,…]:不可使用的分组

基序号的变化

  • 发送窗口
    • 若发送窗口的基序号分组被确认且窗口内存在尚未被确认的分组,则基序号需要更新为窗口中最近未被确认的分组序号
    • 若发送窗口的基序号分组被确认且窗口内全部分组都被确认,则基序号需要向后移动N个位置
  • 接收窗口
    • 若接收窗口的基序号分组正确接收且窗口内存在尚未收到的分组,则基序号需要更新为窗口中最近未收到的分组序号
    • 若发送窗口的基序号分组被确认且窗口内全部分组都收到,则基序号需要向后移动N个位置

窗口尺寸问题

  • 发送窗口尺寸过小
    • 可发送的分组太少了,导致带宽利用率低
    • 窗口很快就满了,发送方不得不等待接收方的确认,导致时延变长
  • 接收窗口尺寸过大
    • 如果出现大量乱序分组,需要更多的内存来缓存
    • 如果序号空间不足,可能会导致序号重叠的问题

序号重叠

  • 无法区分刚到的分组是重传还是新分组
  • 接收窗口的长度必须小于或等于序号长度的一半
  • 对于下图,发送方视角是不一样的,但是接收方视角是一模一样的

6. TCP

6.1 TCP性质

  1. 面向连接的(connection-oriented):在发送数据之前,通信的两端需要建立一个连接,实际上这种连接是逻辑上的,不依赖于物理连接
  2. 全双工服务(full-duplex service):允许通信的两端同时进行数据发送和接收
  3. 点对点(point-to-point):每个连接由两个唯一的IP地址和端口号组成,不存在第三个对等的角色
  4. 最大报文段长度(Maximum Segment Size,MSS):定义了单个TCP报文段的最大数据负载大小(不包括TCP头部)
  5. 缓存(Buffer):通过设置发送缓存和接收缓存来实现可靠的数据传输和流量控制

6.2 TCP报文段结构

字段长度/bit作用
源端口号(Resource Port)16标识数据传输的起始端点
目的端口号(Destination Port)16标识数据传输的终止端点
序号(Sequence Number)32标识TCP数据流中的字节顺序
确认号(Acknowledgement Number)32标识下一个期望接收的字节序号
首部长度(Header Length)4指示TCP头部长度,以32位字为单位
保留区域(Reserved)3保留,可能会在未来的协议扩展中有新的用途
减少拥塞窗口(CWR)1发送方用于确认它已经响应了网络拥塞,调整了其拥塞窗口
显式拥塞通知(ECN-Echo)1接收方用于通知发送方网络发生了拥塞
标志(Flags)6控制位,标识特殊功能(具体在下面)
接收窗口(Window)16指定接收方的缓存区域大小
检验和(Checksum)16检验报文在传输过程中是否出错
紧急数据指针(Urgent Pointer)16指示紧急数据在数据流中的位置
选项(Options)可变用于传输协议的扩展功能
数据(Data)可变实际传输的应用数据部分

序号和确认号

  • 序号:TCP把数据看作是无结构的、有顺序的字节流,因此报文段的序号是该报文段首字节的字节流编号
  • 确认号:TCP是全双工的,因此客户接收到服务器的报文段也是有序号的,确认号就是当前主机期望接收到的下一个字节的序号,即确认报文的序号

如图,考虑以下情况,主机A是客户,主机B是服务器。主机A键入的每个字符会显示在主机B上,如果主机B确实显示了,则会“回显”(echo back)到主机A上。主要经历以下三步:

  1. 主机A先发送一个TCP报文x,序号42,确认号79,数据C
  2. 主机B返回一个TCP报文y,序号79,确认号43,数据C
  3. 主机A再返回一个TCP报文z,序号43,确认号80,无数据

综上,可以得出几个事实:

  • x的确认号就是y的序号!y的确认号就是z的序号!
  • 服务器关于客户的确认被“捎带”(piggybacked)在y中
  • 即使z没有数据,但由于遵循TCP协议,还是需要一个序号
  • z的确认号是80,因为客户已经接收到字节流序号为79之前的数据,所以接下来期望接收到序号为80的数据
  • 两端的起始序号可以是随机的,没有特殊关系

标志(按顺序)

  • URG(Urgent):紧急指示标志,表示有紧急数据需要优先处理
  • ACK(Acknowledgement):确认标志,用于确认收到的数据
  • PSH(Push):推送标志,表示接收方应尽快将数据推送到应用程序,而不是等待缓冲区满
  • RST(Reset):重置标志,用于重新初始化连接
  • SYN(Synchronize):同步标志,用于连接的建立阶段
  • FIN(Finish):结束标志,用于连接的终止阶段

6.3 RTT和超时

采样往返时间(SampleRTT):从某报文段被发出到对该报文段的确认被收到之间的时间量

均值往返时间(EstimatedRTT):由于路由器的拥塞和端系统负载的变化,SampleRTT会随之波动,因此需要对SampleRTT取平均,其中α通常取0.125

EstimatedRTT=(1α)EstimatedRTT+αSampleRTTEstimatedRTT = (1-\alpha) \cdot EstimatedRTT + \alpha \cdot SampleRTT

偏差往返时间(DevRTT):估算SampleRTT偏离EstimatedRTT的程序,即SampleRTT的波动程度,通常取β为0.25

DevRTT=(1β)DevRTT+βSampleRTTEstimatedRTTDevRTT = (1-\beta) \cdot DevRTT + \beta \cdot |SampleRTT-EstimatedRTT|

超时间隔(TimeoutInterval)太小导致不必要的重传,太大导致时延变大,因此超时间隔应该是期望RTT加上一定余量,且SampleRTT波动越大,余量越大

TimeoutInterval=EstimatedRTT+4DevRTTsssTimeoutInterval = EstimatedRTT + 4 \cdot DevRTTsss

6.4 差错控制

6.4.1 回顾可靠数据传输原理

TCP发送方的简化

  • 事件1:应用层调用
    • 生成具有序号nextseqnum的报文段
    • 如果定时器没有运行就启动定时器
    • 向IP传递报文段
    • 更新nextseqnum=nextseqnum+length(data)
  • 事件2:定时器超时
    • 重传具有最小序号但还未被确认的报文段
    • 重启定时器
  • 事件3:收到ACK,且序号为y
    • 如果y大于send_base,根据累积确认规则可以更新send_base=y
    • 如果还存在尚未确认的报文段,则启动定时器

6.4.2 重传情况

2次重传情况:第一个报文段的ACK超时需要重传,第二个报文段的ACK超时也需要重传

1次重传情况:第二个报文段的ACK在第二次超时间隔前到达,则第二个报文不会被重传

0次重传情况:第一个报文段的ACK丢失了,但是第二个报文段的ACK在超时间隔前到达,则两个报文都不需要重传

6.4.3 超时间隔加倍

超时间隔加倍:每次TCP重传时都会将下一次的超时间隔设置为先前的两倍,而不是利用公式计算

原因:定时器过期可能是由于网络拥塞等原因导致的时延增长,而不是简单的分组丢失,如果此时重复传分组,则会导致拥塞更加严重,因此这种情况下应该逐渐增加超时间隔

6.4.4 快速重传

超时触发重传的局限性:如果超时间隔过长,会导致发送方延迟重传丢失的分组,从而增加了端到端时延

TCP接收方生成ACK的策略

冗余ACK(duplicate):由于TCP协议中,接收方不发送显式的否定确认NAK,而是通过发送冗余ACK来指示报文段丢失

快速重传(fast retransmit):规定如果TCP发送方接收到相同数据的3个冗余ACK,则说明该位置的报文段已经丢失,发送方就会在定时器过期之前立即重传丢失的报文段

6.5 流量控制

接收窗口(receive window):发送方维护的一个变量,用于指示接收方的接收缓存还有多少可用空间

流量控制:消除发送方发送过多分组导致的接收方缓存溢出

  • RcvBuffer:接收缓存的大小
  • LastByteRead:从接收缓存交付的最后一个字节的编号
  • LastByteRcvd:从网络中得到的最后一个字节的编号
  • LastByteSent:从发送缓存发送的的最后一个字节的编号
  • LastByteAcked:得到确认的最后一个字节的编号
  • rwnd:接收窗口

满足以下公式:

LastByteRcvdLastByteReadRcvBufferrwnd=RcvBuffer(LastByteRcvdLastByteRead)LastByteSentLastByteAckedrwndLastByteRcvd-LastByteRead \leq RcvBuffer\\ \,\\ rwnd = RcvBuffer - (LastByteRcvd-LastByteRead)\\ \,\\ LastByteSent-LastByteAcked \leq rwnd

6.6 连接控制

6.6.1 建立连接:三次握手

三次握手流程

  1. 客户端TCP向服务器端TCP发送SYN报文段
  • 首部中的SYN标志位被置为1,ACK标志位被置为0
  • 客户会随机地选择一个初始序号(client_isn)
  • 确认号字段被置为0
  1. 服务器端TCP接收到SYN报文段,为TCP连接分配缓存和变量,并向客户端TCP发送SYNACK报文段
  • 首部中的SYN标志位被置为1,ACK标志位被置为1
  • 服务器会随机选一个初始序号(server_isn)
  • 确认号字段被置为client_isn+1
  1. 客户端TCP接收到SYNACK报文段,为TCP连接分配缓存和变量,并向服务器端TCP发送ACK报文段
  • 首部中的SYN标志位被置为0,ACK标志位被置为1
  • 序号字段被置为client_isn+1
  • 确认号字段被置为server_isn+1

为什么需要三次握手

  • 全双工通信
    • 第一次握手确认客户端请求建立连接
    • 第二次握手确认服务器同意建立连接,并且服务器可以接收到客户数据
    • 第三次握手确认客户可以接收到服务器的数据
  • 可靠性:三次握手确保双方都能正确接收和处理对方的序列号和确认号,从而实现可靠数据传输

SYN洪泛攻击:一种常见的DOS攻击,目标是耗尽服务器的资源,从而使其无法处理合法请求

  • 过程:攻击者发送大量的TCP SYN报文段但不完成第三次握手,造成服务器不断为这些半开连接分配资源但从不使用,导致服务器的连接资源被消耗殆尽
  • 解决:服务器响应时,生成并回送一个包含了客户端的请求信息和服务器的状态的SYN cookie,而不是直接在内存中保存半开连接的状态;之后服务器收到ACK时,通过解密和验证SYN cookie,确定是否需要建立连接

6.6.2 结束连接:四次挥手

四次握手流程(假设客户主动结束连接)

  1. 客户会向服务器发送一个FIN报文段,首部的FIN标志位被置为1
  2. 服务器收到FIN报文段之后,回送一个ACK报文段
  3. 服务器会向客户发送一个FIN报文段,首部的FIN标志位被置为1
  4. 客户收到FIN报文段之后,回送一个ACK报文段

TCP连接的双方都可以终止连接,当连接结束时,主机的资源(缓存和变量)都会被释放

客户TCP状态变迁

  1. CLOSED → SYN_SENT:客户端从关闭状态转变为发送SYN报文请求建立连接的状态。
  2. SYN_SENT → ESTABLISHED:客户端在接收到服务器的SYN-ACK响应后,发送ACK,连接状态变为ESTABLISHED,表示连接成功建立。
  3. ESTABLISHED → FIN_WAIT_1:在连接中,客户端发送FIN报文段请求关闭连接,状态变为FIN_WAIT_1。
  4. FIN_WAIT_1 → FIN_WAIT_2:客户端收到服务器对FIN的ACK响应后,状态变为FIN_WAIT_2,表示服务器也已准备好关闭连接。
  5. FIN_WAIT_2 → TIME_WAIT:客户端收到服务器发送的FIN报文段,回送ACK后,状态变为TIME_WAIT,等待可能的重传
  6. TIME_WAIT → CLOSED:在TIME_WAIT状态下,客户端等待预设的时长后,连接完全关闭,状态变为CLOSED。

6.6.3 终止连接

RST报文段:首部的RST标志位设置为1,用于立即终止一个异常的TCP连接

虽然是英文是reset,但不是重新建立一个连接,而是强行终止一个连接

适用情况

  • 异常连接:服务器接收到一个无效的或不期望的报文,例如客户端试图访问一个没有在服务器上监听的端口,或者在连接过程中发送了错误格式的报文
  • 拒绝连接:服务器收到非法的连接请求,例如客户的连接请求未被授权,该端口不允许被访问,或需要额外的安全检查
  • 重置连接:一个已经建立的连接变得无效或异常,例如由于网络故障或应用程序错误,连接需要被强制关闭

7. 拥塞控制

7.1 拥塞原因与代价

7.1.1 无限大缓存的单跳路由

原因:吞吐量受两条连接之间的共享链路容量限制

代价:当分组的到达速率接近链路容量时,分组会经历巨大的排队时延

  • a:理想情况,分组在路由器没有排队
  • b:考虑排队时延

7.1.2 有限大缓存的单跳路由

供给载荷λ^'^(offered load):运输层发送的报文段既有初始数据又有重传数据

原因:有限缓存溢出导致分组丢失,发送方需要重传分组

代价:发送方必要的重传以补偿因为缓存溢出而丢弃的分组消耗链路带宽;发送方不必要的重传引起路由器转发不必要的分组副本浪费链路带宽

  • a:理想情况,发送方完全知道路由器缓存是否空闲,不会发生重传
  • b:理想情况,当且仅当发送方确认一定发生分组丢失才重传
  • c:发送方只要认为可能发生分组丢失就重传

7.1.3 有限大缓存的多跳路由

原因:多路流量为有限缓存空间而竞争

代价:上游路由器转发该分组但下游路由器丢弃该分组而浪费的传输容量

  • 其他路由器重载会导致当前路由器吞吐量为0

7.2 TCP拥塞控制

7.2.1 原理

拥塞窗口(congestion window,cwnd):发送方TCP维护的一个变量,用于指示不会引起网络拥塞的可发送数据量

  • 发送速率满足:LastByteSentLastByteAckedmin{cwnd,rwnd}LastByteSent-LastByteAcked \leq min\{cwnd,rwnd\}
  • 假设接收窗口足够大,则发送速率=cwnd/RTT,通过调整窗口大小就可以控制发送速率

丢包事件:是判断拥塞的依据,虽然不一定是真的丢包了,但是发送方为了可靠数据传输需要按照丢包来处理

  • 定时器超时
  • 收到3个冗余ACK

指导性原则

  • 何时减速:一个丢失的报文段意味着拥塞,应当降低TCP发送方的速率
  • 何时加速:当先前报文段的确认报文正确到达时,应当增加TCP发送方的速率
  • 如何变速:发送方不断执行带宽探测,并根据探测结果调整数据发送速率,自适应网络的实际条件

TCP吞吐量的宏观描述

  • 加性增、乘性减(Additive-Increase,Multiplicative-Decrease,AIMD)方式:发送速率增大是通过逐渐增加一个MSS,发送速率减小是通过逐渐减半,引发了“锯齿”行为
  • 假设最大窗口长度是W,则窗口长度一直在W和W/2之间变化,则平均吞吐量=0.75×WRTT\frac{0.75 \times W}{RTT}

因为指数增长速度很快,全局情况下,处于指数增长的时间很短,而且大多数情况下都处于拥塞避免的状态

7.2.2 算法

  1. 慢启动(slow-start)
  • 指数增长:设置cwnd的初始值是1个MSS,并且每次收到确认报文段cwnd就增加1个MSS,因此每过一个RTT,发送速率就翻番
  • 慢启动阈值(slow-start threshold,ssthresh):当检测到拥塞时,发送方记录一个变量ssthresh=cwnd/2ssthresh=cwnd/2,并令cwnd=1×MSScwnd=1 \times MSS,当发送速率cwnd再次增长到ssthresh时,结束慢启动并转移到拥塞避免

因为先前发生过拥塞,如果到了ssthresh还翻番,则很有可能再次导致拥塞,所以设置先前拥塞的cwnd的二分之一为慢启动阈值

  1. 拥塞避免(congestion avoidance)
  • 线性增长:不再将cwnd翻番,而是每次RTT只将cwnd的值增加一个MSS
  • 超时:回归慢启动
  • 冗余ACK:进入快速恢复

冗余ACK很大概率不是因为网络拥塞,而是因为某个分组丢失,因此不需要回到慢启动

  1. 快速恢复(fast recovery):令ssthresh=cwnd/2ssthresh = cwnd / 2cwnd=ssthresh+3×MSScwnd = ssthresh + 3 \times MSS,然后回归拥塞避免

为什么要加上3个MSS:如果不是网络拥塞导致的丢包,说明此时路由器理应具备多传输3个负载数据的报文的能力,但是却拿来传输冗余ACK了,所以应该加上3个MSS保持网络传输的高效

7.2.3 CUBIC

加性增的局限性:过于谨慎,恢复得太慢,无法快速找到新的平衡点

立方规则

  • W~max~:检测到拥塞时的cwnd值
  • K:预计再次到达W~max~的时间点
  • CUBIC:根据当前时间t和K之间的距离的立方为函数来增加cwnd
  • t<K:当t远离K的时候想要快速恢复,cwnd增长较快;当t接近K的时候变得谨慎,cwnd增长较慢
  • t>K:拥塞状况已经改变,t接近K的时候变的谨慎,cwnd增长较慢;当t远离k的时候想要快速找到新的平衡点,cwnd增长较快

7.3 其它拥塞控制

7.3.1 ECN

明确拥塞通告(Explicit Congestion Notification,ECN):路由器通过在分组头部标记ECN标志位,通知当前路由器存在拥塞

流程

  1. 路由器检测到拥塞并设置分组的ECN标志位
  2. 接收方在ACK报文中将ECE比特置位,告知发送方网络中有拥塞
  3. 发送方将拥塞窗口减半,以响应拥塞
  4. 发送方在随后的报文段中设置CWR比特,通知接收方它已经调整了拥塞窗口

7.3.2 基于时延

基于时延的拥塞控制:通过监测和响应网络延迟来调整数据传输比例,主要关注时延的变化而不是关注丢包

核心目标:通过动态调整发送速率来保持网络管道的最佳负载状态,从而避免拥塞和数据包丢失

流程

  1. 设置合适的延迟阈值RTT~max~
  2. 周期性测量分组的RTT
  3. 如果检测到RTT大于或接近RTT~max~,则认为网络出现了拥塞,降低发送速率
  4. 如果检测到RTT小于RTT~max~,则认为网络没有出现拥塞,增加发送速率

7.4 公平性

公平:每条连接都得到相同份额的链路带宽

两条共享同一条传输速率为R的瓶颈链路的情况

  • 全带宽:两条连接的吞吐量之和为R
  • 平等带宽:两条连接的吞吐量相同
  • 理想情况:既是全带宽也是平等带宽

演变过程

  1. 初始时刻位于A点,由于共同消耗的链路带宽小于R,两条连接都以相同的速率线性增长到B点
  2. 在B点时,由于共同消耗的链路带宽小于R,两条连接的吞吐量同时减半到C点
  3. 在C点时,由于共同消耗的链路带宽小于R,两条连接都以相同的速率线性增长到D点
  4. 以此类推,两条连接的带宽最终会收敛到平等带宽共享曲线和全带宽利用曲线的交叉点附近

影响公平性的因素

  • UDP:UDP不具备拥塞控制机制,不会意识到网络的拥塞,UDP流量会挤压TCP流量
  • 并行TCP:单个程序创建并行TCP会争夺可用带宽,导致每个连接都获得较小的带宽份额,影响整体吞吐量

8. QUIC

快速UDP互联网连接(Quick UDP Internet Connections,QUIC):是建立在UDP运输层协议,但结合了TCP服务模式的应用层协议

特征

  • 面向连接:使用连接ID来标识连接,通过一次握手建立QUIC连接状态
  • 安全:在应用层集成了加密机制,减少了传统TLS和TCP握手中的延迟
  • :允许在一个QUIC连接中管理多个独立的数据流,每个流可以独立进行数据传输和流量控制
  • 可靠:提供了类似于TCP的可靠数据传输机制,包括重传丢失的数据包和确保数据的顺序
  • 拥塞控制:实现了先进的拥塞控制算法,用于动态调整数据传输速率,以应对网络拥塞情况