3.1 概述和运输层服务

运输层的功能
为运行在不同主机上的应用进程提供逻辑通信

(对比之下,网络层协议实现的是主机之间的逻辑通信)

3.1.1 运输层和网络层的关系

关于命名:下面将TCP和UDP的数据统称为报文段(实际上,UDP的数据有时也被成为数据段)

3.2.2 因特网运输层概述

IP服务模型:尽力而为交付服务 => 不可靠
把主机通信扩展为进程通信被称为运输层的多路复用多路分解

TCP:传输控制协议
UDP:用户数据报协议

TCP能提供的服务:

  • 1、进程间的数据交互(多路复用/多路分解)
  • 2、差错校验/差错检测(检查报文是否完整)
  • 3、可靠的数据传输(reliable data transfer)
  • 4、拥塞控制(congestion control)

1、2点是所有的运输层协议要实现的功能(实际上,UDP只实现了这两个功能)

多个应用层报文通过TCP复用可以成为一个TCP报文(socket,etc)
各类运输层报文组合成IP数据报并在网络层传输

3.2 多路复用与多路分解

多路分解: 将运输层报文段中的数据交付到正确套接字的工作
多路复用: 从多个套接字收集数据块,为每个数据块添加首部信息生成报文段,然后将报文段发送到网络层的过程

1、无连接的多路复用与多路分解

UDP的套接字(注意,不是UDP报文!报文里可以包含源主机的信息)是一个二元组,包含目的IP和目的端口

具有相同的二元组(目的IP,目的端口)的UDP报文段会被分发到同一个套接字

2、面向连接的多路复用与多路分解

TCP套接字由一个四元组(源IP地址,源端口,目的IP地址,目的端口)组成
接收方根据这个四元组定位这个报文要分发到的套接字

服务器主机支持多个并发的TCP套接字
Web服务器为每一个客户连接产生不同的套接字;非持久HTTP会对每一个请求都建立不同的套接字

3、无连接运输:UDP

[RFC 768]
发送方:

  • 获取数据
  • 加上源IP和目的端口号,以及差错校验信息
  • 传给UDP Socket

UDP数据报中的首部有源端口、目的端口、长度(包括报文的总长度)和校验和(checksum)

接收方:

  • #TBS

UDP相对于TCP的优势

  • 应用层有更大的掌控权
  • 无需建立连接
  • 无连接状态
  • 分组首部开销小
3.3.2 UDP检验和

发送方

  • 计算校验和
    • 报文看作若干个16比特的字组成的序列
    • 所有字段相加得到和
      • 回卷:最高位有进位的时候,舍弃该进位,最低位加1
  • 把相加得到的和取反成为校验和写入检验和字段中
    接收方
  • 计算接收到的报文段的
  • 与检验和相加
    • 有0:有错误
    • 没有0:可能有错误

校验和取反的目的就是方便检验?

首部如何校验?
在计算首部的校验和时,校验和位置用16个0来占位计算
接收方计算过程校验和首部及校验字段用的就是发送方计算好的校验和,这个地方的不同不会影响判断(?为什么) #TBS

出现了奇数字节的数据包?
最后一个字节后面加上8个0来计算

出现了奇数位的数据报文?
我们认为这种情况不可能出现,从应用层获取的数据一定是按字节的

3.4 可靠数据传输原理(重要)

运输层要解决网络层信道不可靠的问题,为应用层提供可靠的服务
一些接口函数:

1
2
3
4
5
6
/*发送方*/
rdt_send()//由上层调用(应用层->传输层),将**数据**送给接受方的上层
udt_send()//被rdt调用(传输层->网络层),将**分组**通过不可靠通道传输给接收方
/*接收方*/
rdt_rcv()//**分组**到达接受方的时候调用
deliver_data()//由rdt调用,将**数据**交付上层

r和u指reliable和unreliable

3.4.1 构造可靠数据传输协议

不丢失、不乱序、不出错

有限状态机(FSM)
用来描述发送方和接收方

1、rdt1.0
假设:底层不出错、不丢失、发送单个分组

序号 角色 状态 事件 状态转移
1 发送方 等待来自上层的调用 接收到调用 接收并封装数据;发送分组;转移到1
2 接收方 等待来自下层的调用 接收到调用 对分组进行解封装,提取出应用层数据;向上层发送数据;转移到2

2、rdt2.0
假设:不丢失,不乱序,分组可能受损

处理机制:差错校验

接收方要通知发送方分组是否受损
ACK(分组未出错),NAK(分组出错)
(acknowledged)

发送方得知分组受损后:出错重传

有限状态机
停等(stop and wait)协议

序号 角色 状态 事件 状态转移
1 发送方 等待来自上层的调用 接收到调用 接收并封装数据;发送分组,转移到2
2 发送方 等待ACK或NAK 等待接收方反馈的packet 接收packet并检查类型【1】是NAK:重传数据,转移到2; 【2】是ACK:转移到1
3 接收方 等待来自下层的调用 接收到调用 接收并校验数据;【1】数据有问题:发送NAK【2】数据没有问题:提取数据,发送数据,发送ACK

如果传输不成功,那么发送方不会传输新的数据

如何实现重传?
在发送方设置缓冲区,储存已发出但未收到反馈的报文
关于缓冲区大小:接收方和发送方各一个报文的大小就行了

致命缺陷:无限重传
ACK/NAK损坏了怎么办?
解决的可能思路:

  • 接着按照2.0等待
  • 增加足够的检查和比特,使得接收方在校验数据的同时还能尝试修复数据
  • 收到出错的反馈时,直接重新发送当前数据分组(rdt 2.1)

fix:rdt2.1版本
为数据加入编号来进行区分
ACK,NAK有checksum

有限状态机
发送方:

序号 状态 事件 状态转移
1 等待来自上层的调用0 接收到调用 接收并封装数据(增加序号位);发送分组,转移到2
2 等待ACK或NAK0 等待接收方反馈的packet 接收packet并检查类型【1】是NAK或包损坏:重传数据,转移到2;【2】是ACK:转移到3
3 等待来自上层的调用1 接收到调用 接收并封装数据(增加序号位);发送分组,转移到4
4 等待ACK或NAK1 等待接收方反馈的packet 接收packet并检查类型【1】是NAK或包损坏:重传数据,转移到4;【2】是ACK:转移到1

#TBS 似乎不是很全面,后面再翻书

接收方

序号 状态 事件 状态转移
1 等待来自下层的调用0 接收到调用 接收并校验数据;【1】数据有问题:发送NAK【2】数据没有问题且序号为0:处理数据,发送NAK;【3】数据没有问题且序号为1:发送NAK(因为这个packet应该被0号状态处理过了)
2 等待来自下层的调用1 接收到调用 和上面类似
基本思想大概是0号状态只处理序号为0的数据,1号收到

rdt 2.2
只使用ACK,对最后一个正确收到的分组的发送ACK(必须明确指出分组的序号)
发送方收到重复的ACK按照NAK来处理
是NAK就重传报文(大概是吧,回头再查查)

3、rdt3.0

假设:信道不但出错,而且丢包

判定丢包的方法:等待(timeout)

rdt3.0最大的特点是启用了超时重传

发送方
为每一个(分组,ACK)对设置了计时器(start_timer/stop_timer)

接收方:
对每一个收到的packet都发送ACK

几种情况的处理(书本P139)

  • 无丢包
  • 分组丢失
  • ACK丢失
  • 过早超时

性能问题:因为是一个停等协议,物理资源的利用率受限制

发送方利用率$U_{sender}$很低,大部分的时间不是在传输数据,而是在等待接收方的回复

如何提高性能?
流水线技术
发送方连续发送多个分组而无需等待确认
为了避免数据乱序,状态机的序号必须增加
发送方应该尽力缓存已经发送但是未被确认的分组(可能要重传)

具体做法:

  • 分组首部用k-bit字段表示序号
  • 序列为: 已被确认-发送,还未确认-可用,还未发送-不可用
  • 发送,还未确认-可用,还未发送可以看作一个滑动窗口

出现丢包,如何重传?

  • Go-Back-N(GBN) 将丢失的包后面的包全部重传
  • Selective Resend(SR) 以发送方的缓存为代价

3.4.3 回退N步

对接收方,如果接收分组的顺序不对的话,那从第一个顺序不对的分组开始丢弃packet,并发送最后一个正确接收的分组的ACK包

对于乱序的分组,会直接丢弃

对发送方,维护一个队列和base,nextseqnum

3.4.4 选择重传

对于每个分组都要单独设置定时器

  • 发送方
    维护一个发送窗口
    收到的ACK位于窗口内,将对应分组标记为已接收
    特别地,收到的ACK的序号等于send_base时,滑动窗口移动到send_base位于第一个未确认的分组位置

  • 接收方
    对两个区间内的数据进行处理

1、[rcv_base,rcv_base+N-1]
正确接收则返回对应分组序号的ACK,分组此前没有收到过就缓存分组
特别地,如果收到的分组序号是rcv_base,那么将[rcv_base,第一个未接收到的分组)这个区间的数据交付给上层,然后接收窗口向前移动

2、[rcv_base-N,rcv_base-1]
必须回复ACK(避免发送方无限重传)

注意
窗口长度必须小于或等于序号空间大小的一半(避免新旧分组混淆)
形式化地,$接收方窗口\le 2^{k-1}$,其中k是用来保存窗口序号使用的二进制位数

3.5 面向连接的运输: TCP

3.5.1 TCP连接

面向连接

全双工: 可以双向同时传输数据

点对点连接:两个端系统之间没有第三方

三次握手:用于建立连接,协商参数

可靠的字节流:最大报文段长度MSS(Maxium Segment Size)

MSS与MTU(Maxium Transmission Unit)(链路层最大传输单元)的关系:
$$
TCP首部长度+IP首部长度+MSS = MTU
$$
MSS的典型值为1460(以太网和PPP具有1500MTU)

3.5.2 TCP报文段结构

结构

  • 源端口号、目的端口号
  • 序号(seq):序号字段的值是发送数据(不是整个报文) 的第一个字节在整个报文中的序号
  • 确认号(ACK):期望收到对方回应的报文第一个数据字节的序号
  • 首部长度
  • 保留未用
  • 一堆1bit字段
    • URG:紧急字段
    • ACK:=1时确认号字段有效
    • PSH:推送比特,指定报文是否不缓存而直接交付
    • RST:复位比特,指示重建TCP
    • SYN:同步位SYN=1表示其为握手过程报文
    • FIN:用来释放一个连接
  • 窗口字段:控制对方发送的数据量,单位是字节(确定对方发送窗口的上限)
  • 检验和:包含首部和数据,计算检验和的时候在报文段加上12个字节伪首部
  • 紧急指针:指向本报文段紧急数据最后一个字节的序号(和URG配合使用)
  • 选项字段:长度可变,TCP规定的选项字段只有MSS
  • (选项字段后面还有填充字段)使得整个首部长度是4字节的整数倍
  • 数据

3.5.3 往返时间的估计与超时

应该大于RTT

如何估算RTT?

  • 样本RTT
    用来测量发送方从发送报文到收到确认之间的时间
    忽略重传
    样本RTT可能会有波动,采用多次估算求平均的方法
    $$
    Est_RTT = (1-\alpha )*Est_RTT + \alpha * Samp_RTT
    $$
    一般取 $\alpha$ 为0.125

$$
DevRTT = (1-\beta) * DevRTT + \beta |E_RTT - S_RTT|
$$

$\beta$一般取0.25

$$
TimeoutInterval = \alpha E _RTT + \betaDevRTT
$$
TimeoutInterval也叫RTO
通常$\alpha=1,\beta = 4$

3.5.4 可靠数据传输

TCP数据传输
  • TCP编号采用字节编号,而不是报文段编号
  • TCP采用单一超时定时器(可以减少开销)(与GBN区别)

传输流程 #TBS 书本P157
注意y>sendbase的时候,认为y之前的报文全部处理完成,不用重传(感觉得看下接收方怎么处理数据)

产生TCP ACK的建议
#TBS 书本P160
[RFC 2122]
[RFC 2581]
[RFC 5681]

快速重传

如果收到一个数据的3次重复ACK,则认为该数据后面的数据全部丢失,立即发起重传(即在超时定时器之前启动重传)

超时间隔加倍

每次TCP重传均将下一次超时间隔设置为先前值的两倍(为了尽可能减少重传)
(实际上是一种形式受限的拥塞控制)

TCP流量控制(flow control service)

目的:为了避免接收方缓存溢出
手段:将缓冲区剩余空间的大小填充到报文段首部的窗口字段中,通知发送方

维护一个接收窗口(receive window) 来提供流量控制

通知基于应答,如果缓存区满了发送方就不应该发送数据,那么如何当接收方缓存清空的时候要如何通知发送方呢

发送方通过持续向接收方发送只有一个字节数据的报文段,试探其是不是空;当缓存区清空的时候,接收方会将信息返回给发送方

TCP连接建立

三次握手
发送方向接收方发送SYN,SEQ=x
接收方回复SYN,ACK,SEQ=y,ACK=x+1
发送方回复ACK,SEQ=x+1,ACK=y+1

TCP连接释放

FIN字段表示主机不发送数据,但是仍旧可以接收数据

因此,如果只有客户机发了FIN,那么表示它可以接收数据,但是不会再发送数据,此时属于半连接状态

服务机发送了FIN才会彻底断开连接

3.6 拥塞控制原理

注意拥塞控制不等于流量控制
拥塞控制作用于网络而非发送方

现象

  • 丢包
  • 延时长
3.6.1 拥塞原因与代价
  • 情境一

  • 情境2


    ${\lambda}’_{in}$也称为供给载荷(offered load)

  • 情境3


    当分组被丢弃的时候,该分组曾用到的所有“上游”传输容量就会被浪费(也就是说,R2的分组由R1来的话,丢弃后R1的所有传输都是无用的)

拥塞控制的方法
  • 网络辅助的拥塞控制
    • ◆ 直接网络反馈:路由器以阻塞分组的形式通知发送方“网络拥塞了”
    • ◆ 经由接收方的网络反馈:路由器标识从发送方流向接收方分组中的某个字段以指示拥塞的产生,由接收方通知发送方“网络拥塞了”
  • 端到端拥塞控制
    • ◆ 网络层不为拥塞控制提供任何帮助和支持
    • ◆ 端系统通过对网络行为(丢包或时延增加)的观测判断网络是否发生拥塞
    • ◆ 目前TCP采用该种方法

TCP主要靠发送方自动感知网络拥塞的程度,根据感知的结果控制外发的流量

3.7 TCP拥塞控制

要解决的问题:

  • 如何限制外发速率
    • 维护一个拥塞窗口
    • $LastByteSent-LastByteAcked \le CongWin$
    • rate = CongWin/RTT Bytes/sec

      实际上,应该同时考虑CongWin和RecvWin(同时考虑网络和服务端的缓存),但现在假设服务端的缓存足够大

  • 如何感知速率
    • 超时
    • 3个冗余ACK
  • 如何调节速率

Reno算法

  • 加性增,乘性减
    • 丢包后CongWin减半,没有丢包的时候每个RTT增加一个MSS
  • 慢启动
    • 超时情况
    • 冗余ACK情况
    • 注意区分概念:门限值(ssthresh)CongWin
    • 门限值规定了慢启动与拥塞避免之间的界限
    • 改变发送速率的事件是收到ACK包
    • 收到3个重复的ACK时,门限值Threshold设为拥塞窗口的1/2,而拥塞窗口CongWin设为门限值Threshold+3MSS收到新的ACK,则拥塞窗口 CongWin设为门限值Threshold
    • 超时事件发生时,门限值Threshold设为拥塞窗口的1/2,而拥塞窗口 CongWin设为1个MSS。
    • 对不同事件的反应
    • Q:门限值会增大吗?初始的门限值是怎么设置的?
      • A:门限值会增大,门限值的设置总是在丢包事件之后;当前窗口大小超过门限值之后仍然会增加,当其超过了两倍的门限值,再发生丢包,门限值就会增大

TCP吞吐量的计算
忽略慢启动
丢包事件发生时,窗口大小为W,则吞吐量为$\frac{W}{RTT}$
吞吐量是丢包率的函数

3.7.2 基于时延的TCP拥塞控制

#TBS

3.7.3 公平性分析
TCP

TCP连接之间吞吐量的关系

最后两边的带宽都会无限接近$\frac{R}{2}$

UDP

UDP传输是不公平的,没有拥塞控制
#TBS