计算机网络之Ch3运输层

3 运输层

3.1 概述和运输层服务

  • 运输层协议为运行在不同主机上的应用进程之间提供了逻辑通信.
  • 运输层协议在端系统中实现.
  • 运输层分组称为报文段. 运输层将从发送应用程序进程接收到的报文转换成报文段, 然后在发送端系统中, 运输层将这些报文段传递给网络层, 网络层将其封装成数据报并向目的地发送; 在接收端, 网络层数据报中提取运输层报文段, 并上交给运输层, 运输层处理接收到的报文段, 使报文段中的数据为接受应用进程所使用.
  • 因特网有两种运输层协议: TCPUDP.
    • UDP: 不可靠, 无连接
    • TCP: 可靠, 面向连接
  • IP的服务模型是尽力而为交付, 所以称为不可靠服务. TCP和UDP的最基本的责任是, 将两个端系统间IP的交付服务扩展为运行在端系统上的两个进程之间的交付服务, 即运输层的多路复用和多路分解.
  • TCP和UDP都会进行差错检查.
  • TCP通过流量控制, 序号, 确认和定时器提供可靠数据传输, 还提供拥塞控制.

3.2 多路复用和多路分解

  • 一个进程有一个或多个套接字, 相当于从网络向进程传递数据和从进程向网络传递数据的门户.
  • 将运输层报文段中的数据交付到正确的套接字的工作称为多路分解
  • 源主机从不同套接字中手机数据块, 并为每个数据块封装上首部信息从而生成报文段, 将报文段传递到网络层, 所有这些工作称为多路复用
  • 套接字有唯一标识符, 每个报文段有特殊字段指示该报文段所要交付到的套接字, 即源端口号字段目的端口号字段
  • 端口号是一个16比特的字段, 0-1023范围的端口号称为周知端口号, 是保留给周知应用层协议使用的.
  • UDP套接字是由一个二元组(目的IP地址, 目的端口号)全面标识的, 不同源IP/端口号但相同目的IP和端口号的UDP报文段会通过相同的目的套接字被定向到相同目的进程
  • TCP套接字是由一个四元组(源IP地址, 源端口号, 目的IP地址, 目的端口号)标识的, 不同源IP/端口号的TCP报文段会定向到不同的套接字, 除非是包含初始创建连接请求的报文段(目的端口号12000)
  • 对于web服务器, 一个进程对应多个套接字

3.3 无连接运输: UDP

  • UDP只是做了运输协议能够做的最少工作, 即复用/分解和少量的差错检测
  • DNS使用UDP协议
  • UDP相比TCP的优点:
    • 关于发送什么数据以及何时发送的应用层控制更为精细
    • 无须连接建立
    • 无连接状态
    • 分组首部开销小

3.3.1 UDP报文段结构

  • 首部4个字段:
    • 源端口号
    • 目的端口号
    • 长度
    • 检验和

3.3.2 UDP检验和

  • 对报文段中的所有16比特字求和(有溢出的加法需要回卷, 即最高位的记为加到最低位), 再取反码(01对换).

  • 需要检验和的原因: 不保证所有链路提供差错检验, 所以必须在端到端基础上在运输层提供差错检验.

  • UDP提供差错检测但不提供差错恢复

3.4 可靠数据传输原理

3.4.1 构造可靠数据传输协议

  1. rdt 2.0
  • 因为信道可能有比特差错, 所以需要使用肯定确认ACK否定确认NAK
  • 自动重传请求(ARQ)协议: 差错检测, 接收方反馈, 重传
  • 发送放在确认接收方正确接受之前需要等待, 因此称为停等协议
  1. rdt 2.1
  • 考虑到ACK和NAK分组受损的可能, 在分组中加入了序号. 一旦发送方接收到对同一个分组的2次ACK(冗余ACK), 就知道接收方没有正确接受到该分组后的分组.
  1. rdt3.0
  • 假设可能丢包, 发送方选择一个时间值, 以判定可能发生了丢包
  • 如果在这个时间内没有收到ACK, 则重传
  • 因此需要一个倒计时计数器

3.4.2 流水线可靠数据传输协议

  • 停等太慢, 所以改为流水线
  • 流水线需要:
    • 增加序号范围
    • 协议的发送方和接收方两端缓存多个分组
    • 所需序号范围和对缓冲的要求取决于数据传输协议如何处理丢失, 损坏以及延时过大的分组. 解决流水线的差错恢复有两种基本方法: 回退N步选择重传

3.4.3 回退N步

  • 定义基序号为最早未确认分组的序号, 下一个序号为最小的未使用序号, 则将序号范围分割为4段:

    • $[0, base-1]$: 已经发送并确认
    • $[base, nextseqnum-1]$: 已经发送但是未确认
    • $[nextseqnum, base+N-1]$: 要被立即发送的分组
    • $\ge base+N$: 不能使用, 直到前面的未确认分组被确认
  • $N$: 窗口长度(为了流量限制)

  • 回退N步(GBN)协议也被称为滑动窗口协议
  • 设接受方上次交付给上层 数据是序号为$n-1$的分组,
    • 如果正确接收到序号为$n$的分组, 为其发送一个ACK, 并交付给上层
    • 其他所有情况, 丢失(失序)分组, 为$n-1$分组重新发送ACK

3.4.4 选择重传

  • 选择重传(SR)协议通过让发送方只重传那些它怀疑在接收方出错(丢失或受损)的分组而避免了不必要的重传.
  • SR确认方接受一个正确接收的分组而不管其是否按序
  • 发送方:

    • 从上层收到数据, 检查下一个可用于该分组的序号. 如果在窗口内就发送, 否则缓存.
    • 超时: 每个分组有一个自己的逻辑计时器
    • 收到ACK: 如果序号在窗口内, 标记为已接受; 否则, 窗口基序号前移到具有最小序号的未确认分组处. 如果移动后窗口内有未发送分组, 则发送这些分组.
  • 接收方:

    • 序号在$[rcv_base, rcv_base + N - 1]$(窗口内)被正确接收, 则返回ACK
    • 序号在$[rcv_base-N,rcv_base-1]$, 则是接收方已经确认过的分组, 一定要发送ACK.
      • 这种情况是因为之前接收方发的ACK没有到达发送放, 如果此时不发ACK, 会使发送方的窗口一直无法前进
    • 其他情况忽略
  • 如果序号范围有限, 发送发和接收方的窗口不一致会产生严重后果, 无法区分新分组和重发的分组

3.5 面向连接的运输: TCP

  • TCP连接是逻辑连接
  • TCP连接提供的全双工服务: 一台主机的进程A和另一台主机的进程B存在一条TCP连接, 那么应用层数据可以
    A到B也可以B到A. TCP连接也是点对点的.
  • TCP连接建立: 三次握手
  • 最大报文段长度MSS: 指在报文段里应用层数据的最大长度, 不含TCP首部. 根据最大传输单元MTU设置.
  • TCP连接的组成: 一个台主机上的缓存, 变量和进程连接的套接字和另一个台主机上的缓存, 变量和进程连接的套接字.

3.5.1 TCP报文段结构

TCP报文结构

  • TCP首部一般20字节, 比UDP多12字节

  • 源端口号和目的端口号

  • 序号
    • 是该报文段首字节的字节流编号
  • 确认号
    • 主机A填充进报文段的确认号是主机A从主机B收到的下一报文段的首字节的序号
    • TCP只确认该流中至第一个丢失字节为止的字节, 称为累计确认
  • 校验号
  • 接收窗口字段(rwnd)
    • 用于流量控制, 指示接收方愿意接受的字节数量
  • 首部长度字段
    • 以32比特的字为单位
  • 选项字段
  • 标志字段
    • ACK: 指示确认字段中的值是有效的
    • RST, SYN, FIN用于连接的建立和拆除
    • CWR, ECE: 明确拥塞通告
    • PSH: 指示接收方应立即将数据交给上层
    • URG:报文段里存在着被发送端的上层实体设为 “紧急” 的数据

3.5.3 往返时间的估计

  • 往返时间RTT: 一个报文段发出到它被确认的时间

  • $\text{EstimatedRTT}=(1-\alpha)\cdot \text{EstimatedRTT}+\alpha\cdot \text{SampleRTT}$

    • 如果RTT序号越小越新, 则

    • 一般$\alpha=0.125$,

    • 称为指数加权移动平均EWMA

  • 估计偏离程度: $\text{DevRTT}=(1-\beta)\cdot\text{DevRTT}+\beta\cdot|\text{SampleRTT}-\text{EstimatedRTT}|$

  • 一般$\beta=0.25$

  • TCP超时间隔: $\text{TimeoutInterval}=\text{EstimateRTT}+4\cdot\text{DevRTT}$

    • 推荐初始值为$1 s$, 超时则翻倍
    • 只要收到(非重传)报文段并更新了$\text{EstimatedRTT}$, 就用上述公式重新计算$\text{TimeoutInterval}$

3.5.4 可靠数据传输

  • 快速重传:

    • 冗余ACK: 再次确认某个报文段的ACK
    • TCP发送方收到对相同数据的3个冗余ACK, 说明跟在后面的数据报已经丢失, 就进行快速重传
  • 使用回退N步和选择重传的混合体: 选择确认, 即有选择地确认收到地报文,而非采用累计确认.

  • 发送端事件处理大致如下:

    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
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    /*假设发送方不受TCP流量控制和拥塞控制,每一个数据小于MSS.*/
    NextSeqNum = InitialSeqNum
    SendBase = InitialSeqNum

    loop(永远){
    switch(event):

    case 从上层应用接收数据:
    生成序号为NextSeqNum的报文;
    if(定时器没有启动){
    启动定时器;
    }
    向IP传递报文;
    NextSeqNum += 数据字节数;
    break;

    case 超时:
    重传SendBase对应的TCP报文; /*和GBN不同之处*/
    TimeoutInterval *= 2;
    重启定时器; /*每次重传一个报文后都会重新启动定时器*/
    break;

    case 接收ACK, AN = y:
    if(y > SendBase){
    SendBase = y; /*采取累计确认*/
    重新计算TimeoutInterval;
    if(仍有发送且未确认报文){
    重启定时器;
    }
    }
    else{/*实际上此时y == SendBase*/
    y的冗余数量 += 1;
    if(y的冗余数量 == 3){
    /*快速重传*/
    立即重传序号为y对应的报文;
    }
    }
    break;
    }
  • 接收端产生ACK的情况在RFC 5681中建议:

    | 事件 | 动作 |
    | :—————————————————————————————- | :—————————————————————————————- |
    | 具有所期望序号的按序报文段到达 且在此之前的报文段都已经被确认 即本报文段是当前状态下第一个接收但未被确认的报文 | Delayed ACK,延迟ACK发送 (根据本地的计时器设定, 如果在短时间内还有可以接收的报文段,那么可以减少ACK的数量) |
    | 具有所期望序号的按需报文段到达 且当前有一个报文段等待ACK传输, 即此时处于事件一的状态 | 立马发送ACK,确认这两个报文 |
    | 比所期望序号大的失序报文段到达, 即接收产生了空隙 | 立马发送一个冗余ACK |
    | 收到能够填补间隙的报文段 | 如果该报文段序号是间隙的低端, 则马上发送ACK |

3.5.5 流量控制

  • TCP为它的应用程序提供了流量控制服务以消除发送方使接收方缓存溢出的可能
  • TCP也有可能因为IP网络的拥塞而被遏制, 这种形式的发送方的控制称为拥塞控制

  • TCP通过让发送方维护一个称为接收窗口(rwnd)的变量来提供流量控制, 确保

    $\text{LastByteRecd}-\text{LastByteRead}\le \text{RcvBuffer}$, 因此

    $\text{rwnd}=\text{RcvBuffer}-[\text{LastByteRecd}-\text{LastByteRead}]$

  • 主机B把当前的$\text{rwnd}$值放入它给主机A的报文段接收窗口字段中, 主机A保证

    $\text{LastByteSent}-\text{LastByteAcked}\le \text{rwnd}$

    • 如果主机B的$\text{rwnd}=0$, 主机A依然发送1字节数据的报文段, 被确认后就知道主机B的缓存清空后的新$\text{rwnd}$值

3.5.6 TCP连接管理(必考)

连接建立

三次握手:

三次握手

  1. 客户端发送不含应用层数据的特殊TCP报文段, 其中SYN比特置1. 同时客户端随机选择一个初始序号($\text{client_isn}$)
  2. 服务器收到SYN报文段后, 服务器为该TCP连接分配YCP缓存和变量, 并向客户端发送允许连接的SYNACK报文段(也不含应用层数据). SYN比特置为1, 确认号ACK为$\text{client_isn}+1$, 服务器选择自己的初始序号$\text{server_isn}$.
  3. 收到SYNACK报文段后, 客户也要为该连接分配缓存和变量. 客户主机向服务器发送另一个报文段, 对服务器的允许连接的报文段进行确认, 确认号ACK为$\text{server_isn}+1$, SYN比特置为0, 序列号为$\text{client_isn}+1$.
  • 快速连接: 发送SYN的时候就带数据, 第三次握手不光是ACK还是FIN.
  • 客户端time wait: 保证ACK已经被收到; 防止影响后续连接
  • 服务端没有time wait

例子:

  • 客户端: SYN 1056
  • 服务端: SYN 2078, ACK 1057
  • 客户端: SN 1057, ACK 2079
  • 连接已经建立
  • 客户端: SN 1057(并没有+1)

连接关闭

四次挥手

  • 客户端向服务器发送FIN
  • 服务器收到后发ACK确认
  • 服务器发自己的终止报文段FIN
  • 客户对终止报文段发ACK确认
  • 同样的, 客户端需要time wait. 原因: 可能最后A的ACK丢失,导致B重发FIN/ACK,这个2MSL能够保证A能够获得重发的数据报并回应。

例子:

  • 客户端: FIN 7080, ACK 6079
  • 服务器: SN 6079, ACK 7081

  • 服务器:FIN 10679, ACK 7081
  • 客户端: SN 7081, ACK 10680

TCP状态

TCP状态图

  • 如果一台主机接收了具有目的端口80的TCP SYN分组, 但是该主机在端口80不接受连接(即它不在端口80上运行Web服务器), 则该主机向源发送一个特殊重置报文段, 该报文段的RST标记位置为1.
  • 因此探索一台主机上的一个特定端口, nmap将对该端口发送一个TCP SYN报文段
    • 如果收到TCP SYNACK, 则目标主机上一个应用程序使用该TCP端口
    • 如果收到TCP RST, 说明SYN到达了目标主机但是目标主机并没有使用该端口号的应用程序
    • 如果什么都没有收到, 那么可能该SYN报文段被中间的防火墙锁阻挡

补充: 崩溃检测

  • 如果一边崩溃重启, 状态丢失, 连接半开
  • 当任意报文段i到达, 重启的那端发送RST i, 从而另一端关闭连接
  • 或者多次重试后关闭

3.6 拥塞控制原理

3.6.2 拥塞控制方法

  • 端到端拥塞控制: TCP报文段的丢失(通过超时或者三次冗余确认而得知)被认为是网络拥塞的迹象, TCP会相应的地减少其窗口长度, 且可以增加往返时延值.
  • 网络辅助的拥塞控制, 例如拥塞分组, 或者路由器标记或更新从发送方通知该网络拥塞指示

3.7 TCP拥塞控制

  • TCP采用的拥塞控制方法是让每一个发送方根据所感知到的网络拥塞程度来限制其能向连接发送流量的速率.

TCP发送方限制其向连接发送流量速率的方法

  • 发送方跟踪一个额外的变量: 拥塞窗口(cwnd)
  • 在一个发送方中, 未被确认的数据量不会超过$\min\{\text{cwnd}, \text{rwnd}\}$. rwnd为接收窗口(由接受缓存决定)

TCP发送方如何感知在它和目的地之间的路径上出现了拥塞

  • 定义丢包事件: 超时或者三次冗余ACK
  • 当出现过度的拥塞时, 在这条路径上的一个或多个路由器的缓存会溢出, 引起丢包.
  • 因为TCP使用确认来触发增大它的拥塞窗口长度, 所以TCP被称为是自计时的.

TCP拥塞控制算法

  1. 慢启动
  • cwnd初始值为1MSS, 较小.

  • 每当传输的报文段被首次确认, cwnd就翻倍, 指数增长.

  • 停止慢启动的时机:
    • 如果存在一个超时指示的丢包事件, TCP发送方将cwnd设置为1MSS, 且将第二个状态变量ssthresh(慢启动阈值)设为cwnd/2, 即拥塞窗口值的一半.
    • 检测到cwnd的值到达或超过ssthresh后, 结束满启动, 并且TCP转移到拥塞避免状态, 更加谨慎地增加cwnd.
    • 检测到3个冗余ACK, 则执行快速重传并进入快速恢复状态
  1. 拥塞避免
  • 一旦进入拥塞避免状态, cwnd大约为上次拥塞时的值的一半, 每次cwnd的值增加一个MSS.
  • 超时则cwnd设为1MSS, 更新ssthresh为cwnd/2
  • 碰到三个冗余ACK, 将cwnd减半(再根据冗余ACK的数量增加相应个数的MSS), ssthresh记为cwnd/2, 并进入快速恢复状态
  1. 快速恢复
  • 在快速恢复中, 对于引起TCP进入快速恢复状态的缺失报文段, 对收到的每个冗余的ACK, cwnd的值增加1个MSS.
  • 当缺失报文段的最后一个ACK到达时, TCP将降低cwnd后进入拥塞避免状态
  • 超时处理方法同1中
  • TCP Reno: 三次冗余ACK后窗口减半; TCP Tahoe: 三次冗余和超时一样, 窗口置1
  1. TCP拥塞控制: 回顾
  • 加性增, 乘性减(AIMD)模式: 每个RTT内cwnd线性增加1, 出现3个冗余ACK后减半