TCP流量控制机制通过动态调整窗口大小来控制发送端的操作,确保路由器/接收端消息不会溢出。

交互式TCP连接

交互式TCP连接指该连接需要在客户端和服务器之间传输用户输入信息,如按键操作、短消息、操作杆或鼠标操作。对于这些操作,如果用较小的报文段来承载信息,则传输协议需要耗费很高的代价;反之采用较大的报文段则会引入更大的延时,对延迟敏感类应用造成负面影响。因此需要权衡相关因素来找到适合的方法。

以ssh(安全外壳)应用为例,对一个ssh连接,每个交互按键通常都会生成一个单独的数据报(每个按键独立传输)。另外,ssh会在远程系统调用shell,对客户端的输入字符做出回显。客户端的每个输入字符都会生成4个TCP数据段:客户端的交互击键的确认、服务器端对击键的确认、服务器端生成的回显、客户端对该回显的确认。通常服务器端对击键的确认和服务器端生成的回显可以合并成一个信息返回。

(a)是产生4个报文段的过程,(b)是合并击键确认和回显报文后的过程。

一下是针对远程服务器键入date命令并回车的TCP连接信息图:

两张图是一样的操作,1键入d、2确认d并回显d、3对回显d的确认,依次类推至15完成date的输入并键入回车键,16~17是对日期的回显及确认,18~19是换行后对命令提示符的返回和对该数据的确认。

延时确认和Nagle算法

在许多情况下,TCP并不对每个到来的数据包都返回ACK,利用TCP的累积ACK字段可能实现这个功能。累积确认可以允许TCP延迟一段时间发送ACK,以便将ACK和相同方向上需要传的数据结合发送。主机需求RFC[RFC1122]指出,TCP实现ACK延迟的时延应小于500ms,实践中最大取200ms。

采用延时ACK的方法会减少ACK传输数目从而可以一定程度地减轻网络负载。该方法通常用于(大)批量数据传输,但在小数据包传输中,如交互式应用,则需要采取Nagle算法。

Nagle算法[RFC0896]要求,当一个TCP连接中有在传数据(即那些已发送但还未经确认的数据),小的报文段(长度小于SMSS)就不能被发送,直到所有的在传数据都收到ACK。并且在收到ACK后,TCP需要收集这些小数据,将其整合到一个报文段中发送。这种方法迫使TCP遵循停等(stop-and-wait)规程:只有等接收到所有在传数据的ACK后才能继续发送。该算法的精妙之处在于它实现了自时钟控制:ACK返回越快,数据传输越快。在相对高延迟的广域网中,更需要减少微型报的数目,该算法使得单位时间内发送的报文段数目更少,也就是说RTT控制着发包速率。

以下是Nagle算法(以ssh操作为例)禁用和启用的传输过程区别比较:

禁用Nagle:

启用Nagle:

以上两种情况的传输整理成交换过程图如下:

左边是禁用Nagle算法的传输过程,右边是启用Nagle算法后的传输过程。前者19个包,传输持续了0.58s;后者11个包,传输持续了0.8s。

启用Nagle算法后的请求和响应包随时间分布呈一定的规律性,观察每组请求/响应的传输时刻 -- 0.0、0.19、0.38、0.57,遵循一定的模式:每两个间隔为190ms,恰为连接的RTT。每发送一组请求和响应包需要等到一个RTT,这就加长了整个传输过程。Nagle算法做出了一种折中:传输的包数目更少而长度更大,但同时传输时延也更长。

流量控制和窗口管理

回想一下之前在12章介绍TCP数据结构的时候,窗口大小字段(Win,16位,以字节为单位)用于通告一个接收方的窗口大小以达到流量控制。

结合上文的数据传输列表图,可以看到发送方和接收方都带有Win字段,且都有值。发送方请求数据包携带的Win有8320、4220,表示发送且未被确认的数据大小(即发送方窗口大小);接收方响应数据包携带的Win有10800、32900,表示接收方为即将到来的新数据预留的内存存储空间(接收端窗口大小)。

TCP连接的每一端都可以收发数据,连接的收发数据量是通过一组窗口结构来维护的。每个TCP活动连接的两端都维护一个发送窗口结构和接收窗口结构。

如下是TCP发送窗口结构图:

TCP发送端滑动窗口结构记录了已确认、在传以及还未传的数据的序列号。提供窗口的大小是由接收端返回的ACK中的窗口大小字段控制的。

由接收端通告的窗口称为提供窗口,包含4~9字节;接收端已成功确认包括第3个字节在内的之前数据,并通告了一个6字节大小的窗口。窗口大小字段相对ACK号有一个自己的偏移量,发送端计算其可用窗口,即它可以立即发送的数据量,可用窗口计算值为提供窗口大小减去在传且未确认的数据值。变量SND.UNA和SND.WND分别记录窗口左边界和提供窗口值,SND.NXT记录下次发送的数据序列号,可用窗口值为SND.UNA+SND.WND-SND.NXT。

随着传输的进行,当接收到返回的数据ACK时,滑动窗口也随之右移,窗口两端的相对运动使得窗口增大或减小,可用三个术语来描述窗口左右边界的运动:

  1. 关闭,窗口左边界右移。当已发送数据得到ACK确认时,窗口会减小。

  2. 打开,窗口右边界右移,使得可发送数据量增大。当已确认数据得到处理,接收端可用缓存变大,窗口也随之变大。

  3. 收缩,窗口右边界左移。主机需求[RFC1122]并不支持这一做法,但TCP必须能处理这一问题。

窗口左边界不能左移,因为它控制的是已确认的ACK号,具有累积性,不可返回。当得到的ACK号增大而窗口大小保持不变时,称为窗口向前滑动;随着ACK号增大窗口却减小,则左右边界距离减小,当左右边界相等时,称为零窗口。零窗口时,发送端不能再发送新数据,且TCP发送端开始探测对方窗口以伺机增大提供窗口。

接收端也维护一个窗口结构,记录了已接收并确认的数据,以及它能够接收的最大序列号,该窗口可以保证其接收数据的正确性,特别是接收端希望避免存储重复的已接收和确认的数据以及避免存储不应接收的数据。

接收窗口结构如下:

接收窗口相对发送窗口较简单。对接收端来说,到达序列号小于左窗口边界(RCV.NXT)被认为是重复数据而丢弃;超过右边界(RCV.WND+RCV.NXT)的则超出处理范围,也被丢弃;接收窗口(RCV.WND)内会接收并保存。接收窗口只有在接收到数据序列号等于左边界时,才会发生窗口前移。

当通告窗口值变为0时,可以有效的阻止发送端继续发送,直到窗口大小恢复为非零值。当接收端重新获得可用空间时,会给发送端传输一个窗口更新告知其可继续发送数据。窗口更新通常不包含数据,为纯ACK,且不能保证其传输的可靠性。如果一个包含窗口更新的ACK丢失,通信双方就会一直处于等待状态:接收方等待接收数据(已将窗口设为非零值),发送方等待收到窗口更新告知其可继续发送。

针对这个问题,TCP有以下处理方式:发送端会采用一个持续计时器间歇性地查询接收端,看其窗口是否已增长。持续计时器会触发窗口探测的传输,强制要求接收端返回ACK(其中包含了窗口大小字段)。主机需求[RFC1122]建议在一个RTO之后发送第一个窗口探测,随后以指数时间间隔发送。窗口探测包含一个自己的数据,采用TCP可靠传输,因此可以避免由窗口更新丢失导致的死锁。

基于窗口的流量控制机制,尤其是不使用大小固定的报文段的情况下可能会出现称为糊涂窗口综合征(SWS)的缺陷。当出现该问题时,交换数据段大小不是全长而是一些较小的数据段[RFC0813],由于每个报文段中有用的数据相对于头部信息的比例较小,因此耗费的资源也更多,相对应的传输效率也更低。

TCP连接的两端都可能导致SWS的出现:接收端的通告窗口较小、发送端发送的数据段较小。要避免SWS问题,必须在发送端或接收端实现相关规则:

  1. 对于接收端来说,不应通告小的窗口值。[RFC1122]描述的接收算法中,在窗口增至一个全长的报文段(接收端MSS)或者接收端缓存空间的一半(取两者中较小值)之前,不能通告比当前窗口(可能为0)更大的窗口值。可能有两种情况会用到该规则:当应用程序处理接收到的数据后使得可用缓存增大;TCP接收端需要强制返回对窗口探测的响应。

  2. 对于发送端来说,不应发送小的报文段,而且需要由Nagle算法控制何时发送。为避免SWS问题,只有指示满足以下条件之一时才能传输报文段:

    a) 全长(发送MSS字节)的报文段可以发送。

    b) 数据段长度 ≥ 接收端通告过的最大窗口值的一半的,可以发送。

    c) 满足一下任一条件的都可以发送:(i)某一ACK不是目前期盼的(即没有未经确认的在传数据);(ii)该连接禁用Nagle算法。

在Windows(Vista/7及以后版本)和linux中,支持接收窗口自动调优。自动调优连接的在传输数据值需要不断被估算,通告窗口值不能小于这个值。这种方法使得TCP达到其最大可用吞吐量,而不必提前在发送端或接收端设置过大的缓存。该功能可在系统中通过命令进行配置。

参考:

《TCP IP 详解卷1:协议》

RFC官方文档

TCP/IP 笔记 - TCP数据流和窗口管理的更多相关文章

  1. TCP/IP 笔记 - TCP连接管理

    TCP是一种面向连接的单播协议,在发送数据之前,通信双方必须在彼此建立一条连接:这与UDP的无连接不同,UDP无需通信双方发送数据之前建立连接.所有TCP需要处理多种TCP状态时需要面对的问题,比如连 ...

  2. TCP/IP笔记——TCP特点、首部格式、滑动窗口

    这次总结一下TCP相关的知识. TCP主要特点 面向连接:在通信前必须建立连接(只是逻辑上存在,而不是物理连接) 只能有两个端点:即只能一对一通信(所以通常p2p是用UDP实现的) 提供可靠交付服务: ...

  3. TCP/IP 笔记 - TCP拥塞控制

    拥塞控制是TCP通信的每一方需要执行的一系列行为,这些行为有特定算法规定,用于防止网络因为大规模的通信负载而瘫痪.其基本方法是当有理由认为网络即将进入拥塞状态(或已由于拥塞而出现路由丢包情况)时减缓T ...

  4. TCP/IP 笔记 - TCP保活机制

    TCP协议中不存在轮询机制,这意味着加入启动一个客户端进程,与服务器建立连接后,然后离开几小时.几天.甚至几个月,连接依然会保持着.理论上,中间路由器可以崩溃和重启,数据线可以断开再连接,只要连接两端 ...

  5. TCP/IP笔记(一)网络基础知识

    计算机与网络发展 计算机自诞生伊始,经历了一系列演变与发展.大型通用机计算机.超级计算机.小型机.个人电脑.工作站.便携式电以及现如今的智能手机终端都是这一过程的产物.它们性能逐年增强,价格却逐年下降 ...

  6. TCP/IP笔记 一.综述

    1. TCP/IP分层 TCP/IP 是四层的体系结构:应用层.运输层.网际层和网络接口层,如下图: OSI协议是国际标准的网络协议,但是由于OSI的实用性等问题造成OSI没有流行起来.目前国际上广泛 ...

  7. TCP/IP、TCP、UDP、Socket知识汇总

    带你了解TCP/IP,UDP,Socket之间关系 https://blog.csdn.net/chaoshenzhaoxichao/article/details/79785318 主要知识点: T ...

  8. 【TCP/IP】TCP详解笔记

    目录 前言 17. TCP 传输控制协议 17.1 引言 17.2 TCP 服务 17.3 TCP的首部 18. TCP连接的建立与终止 18.1 引言 18.2 连接的建立与终止 18.2.1 建立 ...

  9. TCP/IP 笔记 - 安全

    安全:可扩展身份认证协议.IP安全协议.传输层安全.DNS安全.域名密钥识别邮件 任何由用户或以用户账号执行却违背了用户本身意愿的软件被称为恶意软件 网络安全是一门十分广泛及有深度的学识,而本书旨在了 ...

随机推荐

  1. TJOI2010中位数

    中位数 上面是题目链接. 这一题比较水. 思路非常显然. 用mid查询时,只要返回中间值就行了. 主要就是add操作. 我们肯定不能插在末尾,然后用系统快排,这样只有30分. 那么正确的操作应该是二分 ...

  2. Jmeter—关联【学习截图】

  3. 2019.03.09 bzoj4999: This Problem Is Too Simple!(树链剖分+线段树动态开点)

    传送门 题意:给一颗树,每个节点有个初始值,要求支持将i节点的值改为x或询问i节点到j节点的路径上有多少个值为x的节点. 思路: 考虑对每种颜色动态开点,然后用树剖+线段树维护就完了. 代码: #in ...

  4. Spring Boot的应用启动器

    Spring Boot应用启动器基本的一共有44种,具体如下: 1)spring-boot-starter 这是Spring Boot的核心启动器,包含了自动配置.日志和YAML. 2)spring- ...

  5. sjms-2 创建型模式

    设计模式分类 创建型模式(5种):工厂方法模式.抽象工厂模式.创建者模式.原型模式.单例模式结构型模式(7种):适配器模式.桥模式.组合模式.装饰模式.外观模式.享元模式.代理模式行为型模式(11种) ...

  6. PageHelper分页+前台BootStrap_pagination样式/BootStrap_table样式

    一.PagerHelper分页+前台BootStrap_pagination样式: 效果: 1.引入pageHelper插件:2种方式    pageHelper所需jar包:pagehelper-5 ...

  7. JavaScript -DOM 编程艺术 2nd 完

    今日看完了这本书,做完了最后一个综合性例子.说实话收获良多,终于明白前端-h5 具体做什么 越学习越无知,这个看来真是一个真理. 后期计划: 1.CSS + DIV 布局深入了解,重点实战 2.Jav ...

  8. Chrome扩展插件流程

    一.浏览器插件基础步骤: 1.文件最基础的配置 : 一个manifest文件.一个或多个html文件.可选的一个或多个javascript文件.可选的任何需要的其他文件,例如图片:在开发应用(扩展)时 ...

  9. 学习Python第四天

    关于剩下的数据类型:字符串 字符串是有序的,不可变的(不可变的意思是指将变量a重新赋值后不会覆盖原来的值,而是在内存中开辟了一块新的内存地址,存储变量的值) 字符串的各种方法: 1,将字符串中的大写变 ...

  10. linux系统下安装redis以及java调用redis

    关系型数据库:MySQL  Oracle 非关系型数据库:Redis 去掉主外键等关系数据库的关系性特性 1)安装redis编译的c环境,yum install gcc-c++ 2)将redis-2. ...