前面: (1)讲到TFTP使用了停止等待协议。 数据发送方在发送下一个数据块之前需要等待接收已发送数据的确认 (2) 现在,我们介绍TCP锁使用的被称为滑动窗口协议的另一种形式的流量控制方法。
滑动窗口协议
: 允许发送方在停止并等待确认前可以连续发送多个分组。 由于发送方不必每发一个分组就停下来等待确认,因此该协议可以加速数据的传输
。
一般正常数据流的方式:
- 通常使用的"隔一个报文确认'的策略
- 线路上看到的分组顺序依赖于许多无法操控的因素: 发送方TCP的实现, 接收方TCP的实现,接收进程读取数据(依赖于操作系统的调度)。 对于两个TCP而言,没有一种单一的、正确的方法来交换给定数量的数据。
- 接收方发送ACK(报文段8),但通告其窗口大小为0, 这说明接收方已收到所有数据, 但这些数据都在接收方的TCP缓冲区, 因为有应用程序还没有机会读取这些数据。
- 另一个ACK(9称为:
窗口更新
) 在17.4ms后发送, 标明接收方现在可以接收另外的4096各位字节的数据。 虽然这看起来像一个ACK,,但由于它并不确认任何新数据, 只是用来增加窗口的右边沿, 因此被称为窗口更新
。
滑动窗口
窗口的左右边沿的运动在这个图中, 我们将字节从1到11进行标号。 接收方通告的窗口称为
提出窗口(offerd window)
, 它覆盖了从第4个字节到底9个字节的区域。表明接收方已经确认了包括第3字节在内的数据,并且通告窗口大小为6。 回顾前面17章【tcp通信协议】,我们知道窗口大小是与确认序号相对应的。 发送方计算它的可用窗口,该窗口表明多少数据可以立即被发送。 当接收方确认数据后, 这个滑动窗口不时地向右移动。窗口两个边沿的相对运动增加或减少了窗口的大小。
传输过程中的滑动窗口的动态性三个术语描述它们边沿的移动:
- 成窗口左边沿向右边沿靠近为窗口合拢。 这种现象发生在数据被发送和确认时。
- 当窗口右边沿向右移动时将允许发送更多的数据,我们称之为窗口扩张。 这种现象发生在另一端的接收进程读取已经确认的数据并释放了TCP的接收缓存时。
- 当右边沿向左移动时, 我们称之为窗口收缩。 (Host Requiremenets RFC 强烈建议不要使用这种方式)
因为左边沿受另一端发送的确认序号的控制,因此不可能向左边移动。如果接收到一个指示窗口左边沿向左移动的ACK,则它认为是重复的ACK,并被丢弃
如果左边沿到达右边沿,则称其为一个零窗口, 此时,发送方不能够发送任何数据
TCP 传输过程中的传递时序图
??? 有个问题,为什么发送方的窗口都是4096 ,返回的是3072的大小,为什么发送方的窗口还是4096?????? z这样就有一个问题了? 发送方的窗口大小, 是由谁来进行确定的?是不是发送方自己指定的大小了,也就是发送方表示: 我发送端是可以一时间发送多少个数据的的窗口大小的
- 发送方不必发送一个全窗口大小的数据 【发送方最大的数据受限于MSS, 窗口大小一般是大于MSS的】
- 来自接收方的一个报文段确认数据,并把窗口左边沿向右滑动。 这是因为窗口的大小是相对于确认序号的。
- 窗口大小可以减小,但是窗口的右边沿确不能够向左移动(eg:报文段7到报文段8的变化)
- 接收方在发送一个ACK前不必等待窗口被填满。 我们看到许多实现每收到两个报文段就会发送一个ACK。
窗口大小
由接收方提供的窗口大小通常可以由接收进程控制, 这将影响TCP的性能。
Socket API 允许进程设置发送和接收缓存的大小, 接收缓存的大小是该连接上锁能够通告的最大窗口大小。 有一些应用程序通过修改插口缓存大小来增加性能。
所以: 我们这里就知道, 有关发送方和接收方的窗口大小【缓存大小】是通过socket来进行设置的,当然是有默认值的
PUSH 标志
表示:发送方使用该标志通知接收方将所收到的数据全部提交给接收进程
, 这里的数据是包括与Push一起传送的数据以及接收方TCP已经为接收进程收到的其他数据。
慢启动
- 前面基本上是开始就向玩过发送多个报文段, 知道达到接收方通告的窗口大小为止。当发送方和接收方处于同一个局域网时,这种方法是可以的。【网络处于十分理想的状态下】
- 如果发送方和接收方之间存在多个路由器和速率较慢的链路时, 就有可能出现一些问题。 一些中间路由器必须缓存房租, 并有可能耗尽存储的空间。 —— 证明了这种方式严重降低了了TCP连接的吞吐量。 【网络状态不理想的状态下】
- TCP 采用了慢启动[slow start]”的算法, 降低一开始就发送过多的数据到网络。通过观察到新分组进入网络的速率应该与另一端返回确认的速率相同而进行工作。
- 慢启动为
发送方
的TCP增加了另一个窗口:拥塞窗口(congestion window)记为:cwnd
。 当与另一个网络的主机简历TCP连接时,拥塞窗口被初始化为1个报文段(即:另一端通告的报文段大小)。每收到一个ACK, 拥塞窗口就增加一个报文段(cwnd 以自己为单位,但是慢启动以报文大小为单位进行增加)。发送方取拥塞窗口与通告窗口中的最小值作为发送上限。拥塞窗口是发送方使用的流量控制, 而通告窗口则是接收方使用的流量控制。
- 发送方开始时发送一个报文段,然后等待ACK。 当收到该ACK时,拥塞窗口从1增加为2, 即可以发送2个报文段。 当收到两个报文段的ACK时,拥塞窗口就增加了为4. 这是一个
指数增长
的关系。- 在某些点上可能达到了互联网的容量,
于是中间路由器开始丢弃分组, 发送方检测到丢包, 相当于得到通知:发送方的拥塞窗口开得过大,需要调整。
后面会讨论TCP的超时和重传机制
。
在 2 1 . 6 节中我们将再次讨论慢启动,并介绍怎样采用另一种被称为“拥塞避免”的技术来作为通常的实现。 —— 拥塞避免来实现慢启动? 这个好像。。。
发送一个分组的时间
1 ) 通常发送一分组的时间取决于两个因素: 传播时延和发送时延(带宽)
- 对于一个给定的两个接点之间的通路,传播时延一般是固定的, 而发送时延取决于分组的大小。
- 在速率较慢的情况下发送时延起主要作用, 而在千兆比特速率下传播时延占主要地位。
成块数据的吞吐量
观察 窗口大小、 窗口流量控制、慢启动 对传输成块数据的TCP连接的吞吐量的相互作用。
可以看一下上面的交互过程
现在有一个问题? 怎么样使用它的带宽, 让他连续的发送出去;
带宽时延乘积
—— 窗口应该设置多大的问题
- 为了最大限度的利用链路带宽, 必须确保发送方源源不断的收到接收方发送的ACK, 作为对收到数据的确认和更新window size的大小
- 开始阶段, 通告的window size必须大于等于带宽和往返时延的乘积, 才能确保在收到第一个ACK前, 能够一直发送数据流量。 因为发送第一个数据报文到收到对应的ACK, 时间至少为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 )
拥塞
- 高速带宽到低速带宽的时候
- 带宽汇聚的时候,也会出现拥塞
紧急方式(urgent mode)
- TCP提供了“紧急方式”, 它使一端可以告诉另一端有些具有某些方式的
“紧急数据”已经放置在普通的数据流中
。 另一端被通知这个紧急数据已被放置在普通数据流中, 由接收方决定如何处理。 可以通过设置TCP首部中的两个字段来发出这种从一端到另一端的紧急数据已经被放置在数据流中的通知。 URG比特被置1, 并且一个16bit的紧急指针被置为一个正的偏移量, 该偏移量必须与TCP首部中的序号字段相加, 以便得出紧急数据的最后一个字节的序号
。- TCP 必须通知接收进程,已接收到一个紧急数据指针。 接收进程读取数据流,并被告知如何碰到紧急数据指针。 只要从接收方当前读取位置到紧急数据指针之间有数据存在,就认为应用程序处于“紧急方式”。 在紧急指针通过之后,应用程序边转回到正常方式。
- TCP 本身对紧急数据知之甚少。 没有办法之名紧急数据从数据流的何处开始。
TCP 通过连接传送的唯一信息就是紧急方式已经开始(TCP首部中的额URG比特)和指针紧急数据最后一个字节的指针。 其他的事情留给应用程序去处理。
- 紧急方式有什么作用? 常见的例子:Telnet 和Rlogin , 当交互用户键入中断键时,看到使用紧急方式来完成这个功能的例子。 另外一个例子是FTP, 但及哦啊胡用户防疫一个文件的传输时。
Telnet 和Rlogin从服务器到客户使用紧急方式是因为在这个方向上的数据里很可能要被客户的TCP停止(也即, 它通告了一个大小为0的窗口)。但是如果服务器进程进入了紧急方式, 尽管它不能够发送任何数据,服务器TCP也会立即发送紧急指针和URG标志。 当客户TCP接收到这个通知时就会通知客户进程,于是客户可以从服务器读取输入、打开窗口并使数据流动。
网友评论