第19章 TCP:数据流
所属:TCP/IP详解 卷2:实现 来源:TCP/IP详解 卷2:实现
本章描述TCP数据流的处理,包括序号、确认、滑动窗口、流量控制和数据的收发等。
19.1 引言
TCP数据流的特点
TCP数据流的特点:
- 面向字节流
- 没有边界
- 应用看到的是连续的字节流
- 可靠、有序
- 流量控制
TCP是面向字节流的协议。
本章讨论的内容
本章讨论的内容:
- 序号和确认号
- 滑动窗口
- 流量控制
- 数据的发送
- 数据的接收
- 紧急数据
本章讨论TCP数据流的处理。
19.2 序号和确认号
序号
序号(Sequence Number):
- 本报文段数据的第一个字节的序号
- 32位
- 循环使用
- 建立连接时初始序号(ISN)随机
为什么需要序号:
- 标识每个字节
- 保证顺序
- 去重
- 确认
序号是字节流的编号。
确认号
确认号(Acknowledgment Number):
- 期望收到的下一个字节的序号
- 表示这个序号之前的都收到了
- 32位
- 累积确认
累积确认:
- 确认到某个序号
- 之前的都收到了
- 中间没有丢的
确认号是累积的。
序号空间
序号空间:
- 32位,约40亿
- 循环使用
- 最大段生命期(MSL)内不会回绕
- 高速网络可能有问题(PAWS解决)
序号是循环的。
19.3 滑动窗口
什么是滑动窗口
滑动窗口(Sliding Window):
- 流量控制的机制
- 接收方通告窗口大小
- 发送方最多发窗口大小的数据
- 窗口随着确认滑动
滑动窗口实现流量控制。
发送方的窗口
发送方的窗口结构:
已确认 已发送未确认 可发送 不可发送
<---------><---------------><-----------><--------------->
^ ^ ^
| | |
SND.UNA SND.NXT SND.UNA + SND.WND
三个部分:
- 已确认:已经收到ACK的
- 已发送未确认:发了还没收到ACK
- 可发送:窗口内还没发的
- 不可发送:窗口外的
发送方的窗口分为几个部分。
接收方的窗口
接收方的窗口结构:
已接收已确认 可接收 不可接收
<-------------><-----------><------------->
^ ^
| |
RCV.NXT RCV.NXT + RCV.WND
两个部分:
- 已接收已确认:已经收到并确认了
- 可接收:窗口内的,可以接收
- 不可接收:窗口外的,丢弃
接收方的窗口决定能收多少。
窗口的滑动
窗口的滑动:
- 收到新的数据,确认号增加
- 窗口向右滑动
- 可以接收更多的数据
- 发送方收到ACK,窗口也向右滑
窗口随着数据的收发滑动。
19.4 流量控制
什么是流量控制
流量控制(Flow Control):
- 防止发送方发得太快
- 接收方处理不过来
- 接收方通告窗口大小
- 发送方不能超过窗口
流量控制防止接收方被淹没。
窗口大小的通告
窗口大小的通告:
- 每个ACK都带窗口大小
- 接收方根据自己的缓冲区调整
- 缓冲区大,窗口大
- 缓冲区小,窗口小
窗口大小的单位:
- 字节
- 16位字段,最大65535
- 窗口扩大选项可以更大
窗口大小在TCP首部中通告。
零窗口
零窗口(Zero Window):
- 接收方缓冲区满了
- 通告窗口为0
- 发送方不能再发数据
- 等窗口恢复
零窗口的问题:
- 死锁风险
- 窗口恢复的ACK丢了
- 两边都等
- 坚持定时器解决
零窗口可能导致死锁,坚持定时器解决。
糊涂窗口综合征
糊涂窗口综合征(Silly Window Syndrome, SWS):
- 窗口很小就发送
- 每个段只有很少的数据
- 效率很低
- 40字节首部 + 1字节数据
- 开销太大
怎么避免:
- 接收方:窗口小就不通告
- 发送方:数据少就不发
- 等窗口大一点或数据多一点
避免小窗口,提高效率。
19.5 数据的发送
发送缓冲区
发送缓冲区:
- 应用写入的数据
- 还没发送的
- 发了没确认的
- 都在缓冲区里
发送缓冲区的大小:
- 可以设置
- 影响最大窗口
- 不能超过对端的窗口
发送缓冲区缓存待发送和未确认的数据。
什么时候发送
TCP发送数据的时机:
-
MSS满了
- 数据够一个最大段
- 立即发送
-
应用推送
- 应用设置PSH
- 立即发送
-
Nagle算法超时
- 等了一会
- 还是不够一个MSS
- 发送
-
需要确认
- 收到ACK,窗口打开
- 可以发新数据
不是应用写了就立即发。
Nagle算法
Nagle算法:
- 防止大量小报文
- 只有一个未确认的小报文
- 等ACK回来再发下一个
- 或者等数据够一个MSS
为什么需要:
- 交互式应用
- 每次发1字节
- 40字节首部 + 1字节数据
- 效率太低
Nagle算法减少小报文。
延迟ACK
延迟ACK(Delayed ACK):
- 不收到数据就立即发ACK
- 等一会
- 可能有数据要发,捎带ACK
- 减少报文数量
延迟多久:
- 通常200ms
- 最多等2个段
- 超时就发
延迟ACK减少ACK数量。
19.6 数据的接收
接收缓冲区
接收缓冲区:
- 收到的数据
- 还没被应用读取的
- 按序号排序
- 可能有间隙(乱序)
接收缓冲区的大小:
- 可以设置
- 决定窗口大小
- 窗口 = 缓冲区 - 已接收未读取
接收缓冲区缓存收到的数据。
乱序数据的处理
乱序数据的处理:
- 收到的段不是按顺序的
- 先缓存起来
- 等前面的到了再一起交给应用
- 重复的ACK(确认最后一个有序的)
为什么要缓存乱序:
- 网络可能乱序
- 不一定是丢了
- 缓存起来可以避免重传
- 提高效率
乱序数据先缓存,不丢弃。
数据的上交
数据交给应用:
- 按顺序
- 连续的数据
- 应用用read等读取
- 从缓冲区复制到应用空间
什么时候上交:
- 数据连续
- 应用在读
- 或者有推送标志
按顺序交给应用。
19.7 紧急数据
什么是紧急数据
紧急数据(Urgent Data):
- 也叫带外数据(Out-of-Band, OOB)
- 优先级高
- 不需要等前面的数据
- 可以提前通知接收方
怎么表示:
- URG标志
- 紧急指针
- 指向紧急数据的最后一个字节
紧急数据优先级高。
紧急数据的处理
发送方:
- 设置URG标志
- 设置紧急指针
- 可以在窗口外发送
- 立即发送
接收方:
- 收到URG标志
- 通知应用有紧急数据
- 应用可以读取
- 正常数据还是按顺序
紧急数据只是通知,数据还是按顺序。
紧急数据的问题
紧急数据的问题:
- 实现不一致
- 有的实现是1字节,有的是多字节
- 有的是通知,有的是真正带外
- 应用要小心使用
紧急数据的实现有差异。
19.8 小结
序号和确认
-
序号
- 每个字节的编号
- 32位循环
- 初始序号随机
-
确认号
- 期望的下一个序号
- 累积确认
- 之前的都收到了
滑动窗口
-
发送方窗口
- 已确认
- 已发送未确认
- 可发送
- 不可发送
-
接收方窗口
- 已接收已确认
- 可接收
- 不可接收
-
窗口滑动
- 随着确认滑动
- 流量控制
流量控制
-
窗口通告
- 每个ACK带窗口
- 接收方控制
-
零窗口
- 缓冲区满了
- 不能发了
- 坚持定时器防止死锁
-
糊涂窗口综合征
- 避免小窗口
- 提高效率
数据发送
-
发送缓冲区
- 缓存待发送和未确认的
-
发送时机
- MSS满了
- 应用推送
- Nagle超时
- 收到ACK
-
Nagle算法
- 防止小报文
- 提高效率
-
延迟ACK
- 减少ACK数量
- 捎带确认
数据接收
-
接收缓冲区
- 缓存收到的数据
-
乱序处理
- 先缓存
- 等前面的到了
-
数据上交
- 按顺序
- 应用读取
紧急数据
-
作用
- 高优先级
- 提前通知
-
处理
- URG标志
- 紧急指针
- 通知应用
-
问题
- 实现不一致
- 小心使用
关键概念
-
字节流
- 面向字节
- 没有边界
-
滑动窗口
- 流量控制
- 窗口滑动
-
序号和确认
- 字节编号
- 累积确认
-
Nagle算法
- 减少小报文
-
延迟ACK
- 减少ACK数量
-
紧急数据
- 带外数据
- 高优先级