先补充一个知识:
1. 停止等待协议: 是 tcp 保证传输可靠的重要途径,"停止等待" 就是指发送完一个分组就停止发送, 等待对方确认之后, 才能继续发送下一个分组
停止等待协议的优点是简单, 缺点就是信道的利用率太低, 一次只发送一个消息, 信道大部分时间都是空闲的.
2: 超时重传有一下三种情况:
1) 分组丢失: 发送方发出来了, 接收方没有收到
2) 确认丢失: 接收方收到了, 也发送了确认分组, 但是确认分组丢失了
3) 确认延时: 确认分组没有丢失, 由于传输太慢, 发送方在规定时间内没有收到接收方发的确认分组.
3. 下面两个协议就是解决信道效率太低和增大吞吐量, 以及流量控制:
1)连续 ARQ 协议: 它是指发送方维护着一个窗口, 这个窗口中不止有一个分组, 而是有好几个分组. 窗口的大小是由接收方返回的 win 值决定的. 所以窗口大小 是动态变化的. 只要在窗口中的分组都可以被发送, 这就使得 TCP 一次不是只发送一个分组了. 从而大大提高了信道利用率. 并且它采用累积确认的方式, 对于按序到达的最后一个分组进行确认.
2)滑动窗口协议: 因为窗口不断往前走. 该协议允许发送方在停止并等待确认前发送多个数据分组. 不需要每发送一个就分组就停下来等待确认. 所以可以加速数据的传输, 还可以控制流量.
3)累积确认: 如果发送方发送了 5 个分组, 接收端只收到了 1 2 4 5 , 没有收到 3, 那么我的确认信息会是说明我期望下一个收到的组是第三个, 此时发送方会将 3 4 5 都重发一遍.
20.1 引言
本章我们将介绍 TCP 所使用的被称为滑动窗口协议的另一种形式的流量控制方法.
该协议允许发送方在停止并等待确认前可以连续发送多个分组.
20.2 正常数据流
经受时延的确认: 假设 A -> B,B 要对 A 的数据进行确认, 当有一个分组来的时候, B 不立即确认, 而是启动一个定时器(RFC 规定要小于 500ms, 很多都是 200ms), 等待 200ms, 如果在这期间 A 又来了其他的数据就可以一起确认了 . 或者 A 要发数据给 B 了, 就顺带把之前的也确认了.
比如下面这样, 对 1025 的确认放到了跟 2049 一起.
使用滑动窗口协议, 接收方不必确认每一个收到的分组. 在 TCP 中, ACK 是累积的 - 它们表示连接方已经正确收到了一直到确认号减 1 的所有字节. 比如上面的 2049, 就表示我收到了 2048 个字节.
20.3 滑动窗口协议
比如上面的例子, 应该是右边发送数据给左边. 左边进行确认. 窗口往右移动.
接收方通告的窗口称为提出的窗口(offered window): 上面的 4 - 9
说明接收方已经收到了 3 字节的数据, 且通告窗口大小为 6.
当接收方确认数据后, 这个滑动窗口不断的向右移动. 下面用三个术语来描述窗口左右边沿的运动:
1) 称窗口左边沿向右边沿靠近为窗口合拢. 这种现象发生在数据被发送和确认时.
2) 当窗口右边沿向右移动时将允许发送更多的数据, 我们称之为窗口张开. 这种现象发生在另一端的接收进程读取已经确认的数据, 并释放了 TCP 的接收缓存时.(就是接收方读取了缓冲区里面的数据的时候)
3)当右边沿向左移动, 称为窗口收缩.
如果左边沿到达右边沿, 则称其为一个零窗口. 此时发送方不能发送任何数据.
20.4 窗口大小
由接收方提供的窗口的大小通常可以由接收进程控制, 这将影响 TCP 的性能.
插口 API 允许进程设置发送和接收缓存的大小. 接收缓存的大小是该连接上所能通告的最大窗口大小. 有一些应用程序通过修改插口缓存大小来增加性能.
20.5 PUSH 标志
发送方使用该标志通知接收方将所收到的数据全部提交给接收进程.(这里的数据包括与 PUSH 一起传送的数据, 以及接收方 TCP 已经为接收进程收到的其他数据)
假设一个客户端发送数据给服务器, 设置了 PUSH 标志:
对客户端来说: 客户进程通知 TCP 在向服务器发送一个报文段时不要因等待额外数据而使已提交数据在缓冲中滞留.
对服务器来说: TCP 收到一个设置了 PUSH 标志的报文时, 它需要立即将这些数据递交给服务器进程而不能等待判断是否还会有额外的数据到达.
目前大多数的 API 没有向应用程序提供通知其 TCP 设置 PUSH 的方法, 很多实现程序任务 PUSH 已经过时了. 一个好的 TCP 实现能够自动设置 PUSH 标志.
如果待发送数据将清空发送缓存, 则大多数的源于伯克利的实现能够自动设置 PUSH 标志.
20.6 慢启动
"慢启动"slow start 算法: 通过观察到新分组进入网络的速率应该与另一端返回确认的速率相同而进行工作.
拥塞窗口(congestion window): 当与另一个网络的主机建立 TCP 连接时, 拥塞窗口被初始化为 1 个报文段(即另一端通告的报文段大小). 每收到一个 ack, 拥塞窗口就增加一个报文段(慢启动以报文段大小为单位进行增加).
发送方取拥塞窗口与通告窗口中的最小值作为发送上限.
拥塞窗口是发送方用的流量控制, 而通告窗口则的接收方用的流量控制.
最开始为 1, 确认一个后变成了 2, 就可以发送两个报文段了, 当这两个被确认以后拥塞窗口就变成了 4. 这是一种指数增加的关系.
当到达互联网的极限时, 中间路由器开始丢弃分组, 这就通知发送方它的拥塞窗口开的过大.
20.7 成块数据的吞吐量
通常发送一个分组的时间取决于两个因素:
1) 传播延时: 由光的有限速率, 传输设备的等待时间引起(这个一般是固定的)
2) 媒体速率的发送延时: 媒体每秒可传输的比特数.(取决与 分组的大小.)
假设 A 发数据给 B, 拥塞窗口慢慢增大, 到最后, 发送方和接收方的之间的管道被填满. 此时无论拥塞窗口和通过窗口是多少, 它都不能再容纳更多的数据. 每当接收方在某一时间单位从网络上移去一个报文段, 发送方就再发送一个报文段到网络上. 但是不管有多少报文段填充了这个管道, 返回路径上总是有相同数目的 ack, 这就是连接的理想稳定状态.
20.7.1 带宽时延乘积
如何设置窗口大小呢. 下面是计算公式: 带宽 * RTT , 括号里面是单位.
c a p a c i t y (bit) = b a n d w i d t h (b/s) × ro u n d-trip time (s)
一般称之为带宽时延乘积. 这个值依赖于网络速度和两端的 RTT.
比如: 一条穿越美国 (RTT 约为 60ms) 的 T1 电话线路 (1544000 b/s) 的带宽时延乘积为 11580 字节. 1544000 * 0.06 / 8 就是字节了.
所以增加 RTT 和增加带宽都可以使管道容量增加.
20.7.2 拥塞
发送拥塞的两种情况:
1) 数据到达一个大的管道并向一个较小的管道发送时便会发送拥塞.
2) 当多个输入流到达一个路由器, 而路由器的输出流小于这些输入流的总和时也会发送拥塞
20.8 紧急方式
urgent mode: 它使一端可以告诉另一端有些具有某种方式的 "紧急数据" 已经放置在普通的数据流中. 另一端被通知这个紧急数据已被放置在普通数据流中, 由接收方决定如何处理.
如何发送紧急数据: 设置 TCP 首部中的两个字段来发出紧急数据. URG 置为 1, 并且从一个 16bit 的紧急指针被置为一个正的偏移量, 该偏移量必须与 TCP 首部中的序号字段相加, 以便得出紧急数据的最后一个字节的序号.
TCP 本身对紧急数据知之甚少, 没有办法指明紧急数据从数据流的何处开始. TCP 通过连接传送的唯一信息就是紧急方式已经开始 (URG 置为 1) 和指向紧急数据最后一个字节的指针. 其他的事情留给应用程序去处理.
来源: https://www.cnblogs.com/xcywt/p/8401523.html