7 传输层
传输层服务和协议
- 传输层协议为运行在不同Host上的进程提供了一种逻辑通信机制
- 端系统运行传输层协议
- 发送方:将应用递交的消息分成一个或多个的Segment,并向下传给网络层。
- 接收方:将接收到的segment组装成消息,并向上交给应用层。
- 传输层可以为应用提供多种协议
- Internet上的TCP
- Internet上的UDP
传输层vs.网络层
- 网络层: 提供主机之间的逻辑通信机制
- 传输层: 提供应用进程之间的逻辑通信机制
- 位于网络层之上
- 依赖于网络层服务
- 对网络层服务进行(可能的)增强
Internet 传输层协议
- 可靠、按序的交付服务(TCP)
- 拥塞控制
- 流量控制
- 连接建立
- 不可靠的交付服务(UDP)
- 基于“尽力而为(Best-effort)”的网络层,没有做(可靠性方面的)扩展
- 两种服务均不保证
- 延迟
- 带宽
多路复用/分用
如果某层的一个协议对应直接上层的多个协议/实体,则需要复用/分用
- 接收端进行多路分用:
- 传输层依据头部信息将收到的Segment交给正确的Socket, 即不同的应用进程
- 发送端进行多路复用:
- 从多个Socket接收数据,为每块数据封装上头部信息,生成Segment,交给网络层
分用如何工作?
- 主机接收到IP数据报(datagram)
- 每个数据报携带源IP地址、目的IP地址。
- 每个数据报携带一个传输层的段(Segment) 。
- 每个段携带源端口号和目的端口号
- 主机收到Segment之后,传输层协议提取IP地址和端口号信息,将Segment导向相应的Socket
- TCP做更多处理
无连接分用
- 利用端口号创建Socket
DatagramSocket mySocket1 =new DatagramSocket (99111); DatagramSocket mySocket2 =new DatagramSocket (99222);
- UDP的Socket用二元组标识
- (目的IP地址,目的端口号)
- 主机收到UDP段后,
- 检查段中的目的端口号
- 将UDP段导向绑定在该端口号的Socket
- 来自不同源IP地址和/或源端口号的IPР数据包被导向同一个Socket
面向连接的分用
- TCP的Socket用四元组标识
- 源IP地址
- 源端口号
- 目的IP地址
- 目的端口号
- 接收端利用所有的四个值将Segment导向合适的Socket
- 服务器可能同时支持多个TCP Socket
- 每个Socket用自己的四元组标识
- Web服务器为每个客户端开不同的Socket
面向连接的分用: 多线程Web服务器
一个进程可能创建多个线程
UDP: User Datagram Protocol [RFC 768]
- 基于Internet IP协议
- 复用/分用
- 简单的错误校验
- “Best effort”服务,UDP段可能
- 丢失
- 非按序到达
- 无连接
- UDP发送方和接收方之间不需要握手
- 每个UDP段的处理独立于其他段
Q:UDP为什么存在?
- 无需建立连接(减少延迟)
- 实现简单: 无需维护连接状态
- 头部开销少
- 没有拥塞控制: 应用可更好地控制发送时间和速率
用途:
- 常用于流媒体应用
- 容忍丢失
- 速率敏感
- UDP还用于
- DNS
- SNMP
- 在UDP上实现可靠数据传输?
- 在应用层增加可靠性机制
- 应用特定的错误恢复机制
UDP校验和(checksum)
目的: 检测UDP段在传输中是否发生错误(如位翻转)
- 发送方
- 将段的内容视为16-bit整数
- 校验和计算:计算所有整数的和,进位加在和的后面,将得到的值按位求反,得到校验和
- 发送方将校验和放入校验和字段
- 接收方
- 计算所收到段的校验和
- 将其与校验和字段进行对比
- 不相等:检测出错误
- 相等:没有检测出错误(但可能有错误)
可靠数据传输原理
什么是可靠?
- 不错、不丢、不乱
可靠数据传输协议
- 可靠数据传输对应用层、传输层、链路层都很重要
- 网络Top-10问题
- 信道的不可靠特性决定了可靠数据传输协议(rdt)的复杂性
- 渐进地设计可靠数据传输协议的发送方和接收方
- 只考虑单向数据传输
- 但控制信息双向流动
- 利用状态机(Finite State Machine, FSM)刻画传输协议
- Rdt 1.0: 可靠信道上的可靠数据传输
- 底层信道完全可靠
- 不会发生错误(bit error)
- 不会丢弃分组
- 发送方和接收方的FSM独立
- Rdt 2.0: 产生位错误的信道
- 底层信道可能翻转分组中的位(bit)
- 利用校验和检测位错误
- 如何从错误中恢复?
- 确认机制(Acknowledgements, ACK):接收方显式地告知发送方分组已正确接收
- NAK: 接收方显式地告知发送方分组有错误
- 发送方收到NAK后,重传分组
- 基于这种重传机制的 rdt 协议称为 ARQ (Automatic Repeat reQuest) 协议Rdt 2.0 中引入的新机制
- 差错检测
- 接收方反馈控制消息: ACK/NAK
- 重传
- Rdt 2.0 有什么缺陷?
- 如果ACK/NAK消息发生错误/被破坏(corrupted)会怎么样?
- 为ACK/NAK增加校验和,检错并纠错
- 发送方收到被破坏ACK/NAK时不知道接收方发生了什么,添加额外的控制消息
- 如果ACK/NAK坏掉,发送方重传
- 不能简单的重传: 产生重复分组
- 如何解决重复分组问题?
- 序列号(Sequence number): 发送方给每个分组增加序列号
- 接收方丢弃重复分组
Rdt 2.1 vs. Rdt 2.0
发送方:
- 为每个分组增加了序列号
- 两个序列号(0,1)就够用,为什么?
- 需校验ACK/NAK消息是否发生错误
- 状态数量翻倍
- 状态必须“记住”“当前”的分组序列号
接收方:
- 需判断分组是否是重复
- 当前所处状态提供了期望收到分组的序列号
- 注意:接收方无法知道ACK/NAK是否被发送方正确收到
- Rdt 2.2:无NAK消息协议
- 与 rdt2.1 功能相同,但是只使用 ACK
- 如何实现?
- 接收方通过 ACK 告知最后一个被正确接收的分组
- 在 ACK 消息中显式地加入被确认分组的序列号
- 发送方收到重复 ACK,之后,采取与收到 NAK 消息相同的动作
- 重传当前分组
- Rdt 3.0
- 如果信道既可能发生错误,也可能丢失分组,怎么办?
- “校验和+序列号+ACK+重传”够用吗?
- 方法: 发送方等待合理时间
- 如果没收到ACK,重传
- 如果分组或ACK只是延迟而不是丢了?
- 重传会产生重复,序列号机制能够处理
- 接收方需在ACK中显式告知所确认的分组
- 需要定时器
Q:Rdt 3.0 的停-等协议导致其性能很差,网络带宽利用率很低!
A:流水线机制与滑动窗口协议
- 流水线机制
- 允许发送方在收到ACK之前连续发送多个分组
- 更大的序列号范围
- 发送方和/或接收方需要更大的存储空间以缓存分组
- 滑动窗口协议: Sliding-window protocol
- 窗口
- 允许使用的序列号范围
- 窗口尺寸为N: 最多有N个等待确认的消息
- 滑动窗口
- 随着协议的运行,窗口在序列号空间内向前滑动
- 滑动窗口协议: GBN, SR
- Go-Back-N(GBN)协议: 发送方
- 分组头部包含 k-bit 序列号
- 窗口尺寸为 N,最多允许 N 个分组未确认
- ACK(n): 确认到序列号n(包含n)的分组均已被正确接收
- 可能收到重复ACK
- 为空中的分组设置计时器(timer)
- 超时Timeout(n)事件: 重传序列号大于等于n,还未收到ACK的所有分组
- ACK 机制: 发送拥有最高序列号的、已被正确接收的分组的 ACK
- 可能产生重复 ACK
- 只需要记住唯一的 expectedseqnum
- 乱序到达的分组:
- 直接丢弃 → 接收方没有缓存
- 重新确认序列号最大的、按序到达的分组
- Selective Repeat协议
- GBN有什么缺陷?
- 接收方对每个分组单独进行确认
- 设置缓存机制,缓存乱序到达的分组
- 发送方只重传那些没收到ACK的分组
- 为每个分组设置定时器
- 发送方窗口
- N个连续的序列号
- 限制已发送且未确认的分组
问题: 序列号空间大小与窗口尺寸需满足什么关系?
- Ns+NR<=2^k
TCP概述:RFCS-793,1122,1323,2018,2581
- 点对点
- 一个发送方,一个接收方
- 可靠的、按序的字节流流水线机制
- TCP拥塞控制和流量控制机制设置窗口尺寸
- 发送方/接收方缓存
- 全双工(full-duplex)
- 同一连接中能够传输双向数据流
- 面向连接
- 通信双方在发送数据之前必须建立连接。
- 连接状态只在连接的两端中维护,在沿途节点中并不维护状态。
- TCP连接包括:两台主机上的缓存、连接状态变量、socket等
- 流量控制机制
TCP段结构
TCP:序列号和ACK
- 序列号;
- 序列号指的是segment中第一个字节的编号,而不是segment的编号
- 建立TCP连接时,双方随机选择序列号
- ACKs:
- 希望接收到的下一个字节的序列号
- 累计确认:该序列号之前的所有字节均已被正确接收到
- Q: 接收方如何处理乱序到达的Segment?
- A: TCP 规范中没有规定,由TCP的实现者做出决策
TCP可靠数据传输概述
- TCP在IP层提供的不可靠服务基础上实现可靠数据传输服务
- 流水线机制
- 累积确认
- TCP使用单一重传定时器
- 触发重传的事件
- 超时
- 收到重复ACK
- 渐进式
- 暂不考虑重复ACK
- 暂不考虑流量控制
- 暂不考虑拥塞控制
TCP RTT和超时
问题: 如何设置定时器的超时时间?
- 大于RTT
- 但是RTT是变化的
- 过短:
- 不必要的重传
- 过长:
- 对段丢失时间反应慢
问题: 如何估计RTT?
- SampleRTT: 测量从段发出去到收到ACK的时间
- 忽略重传
- SampleRTT变化
- 测量多个SampleRTT,求平均值,形成RTT的估计值。EstimatedRTT
定时器超时时间的设置:
- EstimatedRTT+“安全边界”
- EstimatedRTT变化大→较大的边界
测量RTT的变化值: SampleRTT与EstimatedRTT的差值
- DevRTT =(1-beta)*DevRTT+ beta* |SampleRTT-EstimatedRTT|
(typically,β = 0.25)
定时器超时时间的设置:
TimeoutInterval = EstimatedRTT + 4* DevRTT
TCP发送方事件
- 从应用层收到数据
- 创建Segment
- 序列号是Segment第一个字节的编号
- 开启计时器
- 设置超时时间:TimeOutInterval
- 超时
- 重传引起超时的Segment
- 重启定时器
- 收到ACK
- 如果确认此前未确认的Segment
- 更新SendBase
- 如果窗口中还有未被确认的分组,重新启动定时器
- 如果确认此前未确认的Segment
快速重传机制
- TCP 的实现中,如果发生超时,超时时间间隔将重新设置,即将超时时间间隔加倍,导致其很大
- 重发丢失的分组之前要等待很长时间
- 通过重复ACK检测分组丢失
- Sender 会背靠背地发送多个分组
- 如果某个分组丢失,可能会引发多个重复的 ACK
- 如果 sender 收到对同一数据的 3 个 ACK,则假定该数据之后的段已经丢失
- 快速重传: 在定时器超时之前即进行重传
TCP流量控制
- 接收方为TCP连接分配buffer
- 上层应用可能处理buffer中数据的速度较慢
- flow control: 发送方不会传输的太多、太快以至于淹没接收方( buffer溢出)
- 速度匹配机制
(假定TCP receiver丢弃乱序的segments)
- Buffer中的可用空间(spareroom)
=Rcvwindow
=RcVBuffer- [LastByteRcvd - LastByteRead] - Receiver通过在Segment的头部字段将 Rcvwindow 告诉Sender
- Sender限制自己已经发送的但还未收到ACK的数据不超过接收方的空闲Rcvwindow尺寸
- Receiver告知SenderRcvwindow=o,会出现什么情况?
- sender 仍能发送较小的段,以便于 sender 保持发送状态,避免死锁!
TCP连接管理
- TCP sender和receiver在传输数据前需要建立连接
- 初始化TCP变量
- Seq. #
- Buffer和流量控制信息
- Client:
连接发起者 socket clientsocket = new socket ( “hostname” , “port number”) ; - Server: 等待客户连接请求 socket connectionsocket =welcomeSocket.accept() ;
TCP连接管理: 建立
TCP连接管理:关闭
Closing a connection:
client closes socket: clientsocket.close () ;
- Step 1: client 向server 发送 TCP FIN 控制segment
- Step 2: server 收到FIN, 回复ACK.关闭连接,发送 FIN.
- Step 3: client 收到FIN, 回复ACK.
- 进入“等待” - 如果收到 FIN,会重新发送 ACK
- Step 4:server收到ACK. 连接关闭.
拥塞控制
拥塞(Congestion)
- 非正式定义:“太多发送主机发送了太多数据或者发送速度太快,以至于网络无法处理”
- 表现:
- 分组丢失(路由器缓存溢出)
- 分组延迟过大(在路由器缓存中排队)
- 当分组被drop时,任何用于该分组的“上游”传输能力全都被浪费掉
- 拥塞控制vs.流量控制
- A top-10 problem.
拥塞控制的方法
- 端到端拥塞控制:
- 网络层不需要显式的提供支持
- 端系统通过观察loss,delay等网络行为判断是否发生拥塞
- TCP采取这种方法
- 网络辅助的拥塞控制:
- 路由器向发送方显式地反馈网络拥塞信息
- 简单的拥塞指示(1bit): SNA, DECbit, TCP/IP ECN, ATM)
- 指示发送方应该采取何种速率
TCP拥塞控制的基本原理
- Sender限制发送速率
- LastByteSent - LastByteAcked <= Congwin
- rate ~=Conwin/RTT Bytes/sec
- CongWin:
- 动态调整以改变发送速率
- 反映所感知到的网络拥塞
- 问题:如何感知网络拥塞?
- Loss事件=timeout或3个重复ACK
- 发生loss事件后,发送方降低速率
- 如何合理地调整发送速率?
- 加性增—乘性减:AIMD
- 慢启动: SS
加性增一乘性减: AIMD
- 原理: 逐渐增加发送速率,谨慎探测可用带宽,直到发生loss
- 方法:AIMD
- Additive lncrease: 每个RTT将CongWin增大一个MSS——拥塞避免
- Multiplicative Decrease: 发生loss后将CongWin减半
TCP慢启动: SS
- TCP连接建立时, CongWin=1
- 例:MSS=500 byte,RTT=200msec
- 初始速率 = 20k bps
- 可用带宽可能远远高于初始速率:
- 希望快速增长
- 原理: 当连接开始时,指数性增长
- 指数性增长
- 每个RTT将CongWin翻倍
- 收到每个ACK进行操作
- 初始速率很慢,但是快速攀升
Q: 何时应该指数性增长切换为线性增长(拥塞避免)?
A: 当CongWin达到Loss事件前值的1/2时.
实现方法:
- 变量 Threshold
- Loss事件发生时,Threshold被设为Loss事件前CongWin值的1/2。
Loss事件的处理
- 3个重复ACKs:
- Congwin 切到一半然后线性增长
- Timeout事件:
- Congwin直接设为1个MSS,然后指数增长,达到threshold后,再线性增长
- 3个重复ACKs表示网络还能够传输一些 segments
- timeout 事件表明拥塞更为严重
TCP throughput: 吞吐率
- 给定拥塞窗口大小和RTT,TCP的平均吞吐率是多少?
- 忽略掉Slow start
- 假定发生超时时CongWin的大小为W,吞吐率是W/RTT
- 超时后,CongWin=W/2,吞吐率是W/2RTT
- 平均吞吐率为: 0.75W/RTT
TCP的公平性
- 仅使用 TCP 是公平的(拥塞控制)
- 多媒体应用通常不使用TCP,以免被拥塞控制机制限制速率
- 使用UDP:以恒定速率发送,能够容忍丢失
- 产生了不公平
公平性与并发TCP连接:
- 某些应用会打开多个并发连接
- Web浏览器
- 产生公平性问题
- 例子: 链路速率为R,已有9个连接
- 新来的应用请求1个TCP,获得R/10的速率
- 新来的应用请求11个TCP,获得R/2的速率