前面一节咱们介绍完了TCP协议,这部分,将要介绍,TCP是如何实现可靠传输的。

TCP的可靠传输

1.滑动窗口

上一节我们介绍TCP报文段头部的时候说得到,"窗口"这个部分,"窗口"的内容就是发送/接收的数据的字节总量(窗口是以字节为单位)。

发送方A有发送窗口,接收方B有接收窗口。头部的"窗口"要配合"确认号"才能确定,要发送哪些数据和具体接收哪些数据。

现假定A收到了B发来的确认报文段中,其中窗口是20(字节),而确认号是31(这表明B期望收到的下一个序号是31,而序号31之前的数据已经收到了),A就构造出自己的发送窗口,如下图:

我们现在来讨论一下A的发送窗口:

在没有收到B确认的情况下,A可以连续把窗口内的数据都发送出去。凡是发送过的数据,会暂时保留一份副本,以便超时重传时使用。

发送窗口的位置由窗口前沿和窗口后沿的位置共同决定。

后沿: 上图中后沿的后面部分(左边)表示已经发送并且收到了确认。这些数据不在保留副本。

后沿变化的情况有两种:不动(没有收到新的确认号(>31))和前移(收到了新的确认号)。后沿是不会向后移动的,因为不能撤销掉已收到的确认。

前沿: 上图中前沿的前面部分(右边)表示不允许发送的数据。

前沿变化情况有三种:

向前移动: 收到新的确认后(>31)的通常情况

不动: 没有收到确认;收到了新的确认号(35),但窗口值变小了变成了15,那前沿的位置还是在50那。

收缩:窗口值变得更小。如上面新的确认号是35,但窗口值变成了10,那前沿的位置就到了45的位置。不过TCP的标准强烈不赞成这样做。

后沿前沿的移动,正好就把窗口滑动的特点展示出来了。

下面我们再通过举例子,再详细介绍滑动窗口的特点

假定A发送了窗口中序号为31~41的数据(黑色小方框表示),表示已发送但未收到确认。而42~50号的数据是允许发送但尚未发送的。如下图。

上图中,要描述发送窗口的状态需要三个指针:P1,P2,P3。而小于P1的是已经发到并且收到确认的部分,大于P3是不允许发送的部分。

P3 - P1 = A的发送窗口(通知窗口)

P2 - P1 = 已发送但尚未收到确认的字节数。

P3 - P2 = 允许发送但尚未发送的字节数(可用窗口或有效窗口)

再说一下B的接收窗口。先看下图。

B接收窗口内的序号(31~50)是允许接收的。在上图中,32,33的被接收到了,而31的数据没有收到(也许丢失,也许滞留在网络中)。而B只能将按顺序收到的数据中,将最高号作为确认号发出。由于31号还没有收到,所以B发送的确认报文仍然是31,而不是32或33。

现在假设B收到了31号数据,并把31~33的数据交给应用程序,然后B会删除这些数据。接着把接收窗口向前移动3个序号,同时给A发送确认号34,窗口只20。

此时A、B窗口移动后的图如下:

A收到了收到了B发送的34号确认号,20的窗口值,所以P1移至34,P3移至53。但P2指针没有动。

而我们可以看到B还接收到了37、38、40。但这些数据没有按序到达,所以只能先暂存在接收窗口中。

然后,A再继续发送完序号42~53的数据后,指针P2向前移动和P3重合。如下图:

窗口内的序号已经用完。但还没有收到B的确认。如果过了一段时间A还没有收到确认,就只能超时重传这部分数据,直到B收到确认位置。

如果A收到确认号落在发送窗口内,那么A可以使窗口向前滑动,发送新的数据。

2. 发送/接收缓存

发送方的应用进程把字节流写入TCP的发送缓存;接收方的应用进程从TCP的接收缓存中读取字节流。下图画出了TCP缓存和窗口之间的关系:

我们先来看(a)图:

可以看到数据量大小是发送缓存>应用程序>发送窗口。

发送缓存用来暂时存放:

1: 应用程序传给TCP准备发送的数据。

2: TCP已经发送但未收到确认的数据。

应用程序必须控制写入缓存的速率,不能太快,否则发送缓存就是会没有存放数据的空间。(I/Obuffer)

再看一下(b)图:

接收缓存用来暂时存放:

1: 按序到达的、但尚未被接收应用程序读取的数据

2: 未按序到达的数据

如果收到的分组被检测出有差错,则要丢弃。如果应用程序来不及读取收到的数据,接收缓存最终就会被填满,使接收窗口减小到0。反之,如果应用程序能够及时从接收缓存中读取收到的数据,接收窗口就可以增大,但不能超过接收缓存的大小。(b)中还指出了,下一个期望收到的字节号,这个字节号也就是接收方给发送方的报文段的首部中的确认号。

经过讨论,可以强调3点:

1. A的发送窗口是根据B的接收窗口来设置的。

2. TCP对不按序到达的数据会先临时存放在接收窗口中,等到缺少字节收到后,再上交付到应用程序。

3. TCP要求接收方必须有积累确认的功能,这样可以减小传输开销。接收方可以在合适的时候发送确认,也可以在自己有数据要发送时把确认信息顺便捎带上。

3. 超时重传选择

3.1超时重传时间设置

如果超时时间设置的太短,就会引起报文段不必要的重传,增大网络负荷;如果设置的太长,又增加了网络的空闲时间,降低了传输效率。所以超时重传时间设置应该是动态变化的。

RTT(报文段的往返时间): 一个报文段发出的时间,到收到确认的时间。

RTTs(加权平均往返时间或平滑往返时间): 根据RTT计算出来的,超时重传时间。

新的RTTs = (1-α)*(旧的RTTs) + α*(新的RTT样本)。新的RTT即动态变化的重传时间。

RFC2988推荐的α值为1/8。用这种方法得出的加权平均往返时间RTTs就比测量出的RTT值更加平滑。

3.2超时计时器的设置

RTO(超时计时器设置的超时重传时间),应该略大于RTTs。RTO就是真正用来计算超时重传的时间设置。

RTO=RTTs + 4 * RTTd

RTTd是RTT的偏差的加权平均值。当第一次测量时,RTTd值为测量到的RTT值的一半,往后的测量中,RTTd的计算公式如下:

新的RTTd = (1-β) * (旧的RTTd) + β * |RTTs - 新的RTT样本|

β的推荐值是1/4。

3.3Karn算法

重传后,如何判定此确认报文段是对先发送的报文段的确认,还是对后来重传报文段的确认?重传报文段和原来的报文段完全一样,因此源主机在收到确认后无法作出正确的判断,而RTTs的值对正确的判断影响很大。

Karn提出了一个算法:在计算RTTs时,只要报文段重传了,就不采用重传报文段的往返时间样本(即上面公式中新的RTT)。这样得出的RTTs和RTO就比较准确。

但有个问题,就是超时重传时间无法更新。于是修正后的Karn算法是:报文段每重传一次,就把超时重传时间RTO增加大一些。典型做法是取新的重传时间为2倍的旧的重传时间。当不发生重传时,才根据上面RTO的公式计算超时重传时间。

选择确认SACK

在第一章介绍报文段头部选项的时候有介绍SACK的作用:只传送缺少的数据,不重传已经正确到达接收方的数据。

然而SACK文档没有指明发送方应当怎样响应SACK。因此大多数的实现所有未被确认的数据块。

TCP学习总结(二)的更多相关文章

  1. 网络编程基础之TCP学习(二)编程案例

    TCP网络编程流程如下: 实现功能:服务器端与客户端成功通讯后返回get! 服务器端程序 #include <netdb.h> #include <sys/socket.h> ...

  2. TCP学习之二:客户端与服务端的连接

    主要参考张子阳大神的博客:http://www.cnblogs.com/JimmyZhang/category/101698.html TcpClient是对Socket的封装 一个TcpClient ...

  3. CentOS 7 学习(二) 配置Nginx反向代理

    CentOS 7 学习(二) 配置Nginx反向代理 Nginx可以通过php-fpm来运行PHP程序,也可以转向apache,让apache调用php程序来运行. 不过对于Nginx来说,其反向代理 ...

  4. 《Spring Cloud》学习(二) 负载均衡!

    第二章 负载均衡 负载均衡是对系统的高可用.网络压力的缓解和处理能力扩容的重要手段之一.Spring Cloud Ribbon是一个基于 HTTP 和 TCP 的客户端负载均衡工具,它基于Netfli ...

  5. Netty 学习(二):服务端与客户端通信

    Netty 学习(二):服务端与客户端通信 作者: Grey 原文地址: 博客园:Netty 学习(二):服务端与客户端通信 CSDN:Netty 学习(二):服务端与客户端通信 说明 Netty 中 ...

  6. crawler4j 学习(二)

    crawler4j 学习(二) 实现控制器类以制定抓取的种子(seed).中间数据存储的文件夹.并发线程的数目: public class Controller { public static voi ...

  7. 从零开始学习jQuery (二) 万能的选择器

    本系列文章导航 从零开始学习jQuery (二) 万能的选择器 一.摘要 本章讲解jQuery最重要的选择器部分的知识. 有了jQuery的选择器我们几乎可以获取页面上任意的一个或一组对象, 可以明显 ...

  8. Android Animation学习(二) ApiDemos解析:基本Animators使用

    Android Animation学习(二) ApiDemos解析:基本Animatiors使用 Animator类提供了创建动画的基本结构,但是一般使用的是它的子类: ValueAnimator.O ...

  9. AspectJ基础学习之二搭建环境(转载)

    AspectJ基础学习之二搭建环境(转载) 一.下载Aspectj以及AJDT 上一章已经列出了他的官方网站,自己上去download吧.AJDT是一个eclipse插件,开发aspectj必装,他可 ...

随机推荐

  1. CDH5.16.1集群新增节点

    如果是全新安装集群的话,可以参考<Ubuntu 16.04上搭建CDH5.16.1集群> 下面是集群新增节点步骤: 1.已经存在一个集群,有两个节点 192.168.100.19 hado ...

  2. HIT2019春软件构造->正则表达式语法

    普通字符:匹配与之相同的字符        字母.数字.汉字.下划线.没有特殊定义的标点符号. 简单的转义字符: \n 换行符 \t 制表符 \\ 代表\本身 \^,\$,\.,\(,\),\{,\} ...

  3. ThinkPHP5.0源码学习之缓存Cache(一)

    一.文件 1.缓存配置文件:thinkphp\convention.php 2.缓存文件:thinkphp\library\think\Cache.php 3.驱动目录:thinkphp\librar ...

  4. python--多线程多进程

    一.进程 对于操作系统来说,一个任务就是一个进程(Process),比如打开一个浏览器就是启动一个浏览器进程,打开一个记事本就启动了一个记事本进程,打开两个记事本就启动了两个记事本进程.进程是很多资源 ...

  5. LaLeX数学公式

    启用数学公式: 需要插入公式时,用 $ 将公式包围.若需要输入多行,则用一对 $$ 包围. 例如: $$ \rho = \sqrt{(\Delta x)^{2}+(\Delta y)^{2}} \\ ...

  6. IDEA配合Flash CS6开发Flash项目--配置篇

    前端篇: 1:下载好IDEA和Flash CS6 2:配置IDEA 打开IDEA,点击创建新项目,选择Flash项目. 一开始没有装Flex SDK的界面会显示红色的none字样,这时需要找到自己下载 ...

  7. Firebug: 已拦截跨源请求:同源策略禁止读取位于XXX的远程资源。(原因:CORS 头缺少 'Access-Control-Allow-

    第一种,就是在被请求的程序中添加HTTP头,即CORS跨域(跨域资源共享,Cross-Origin Resource Sharing) 如: Response.Headers.Add("Ac ...

  8. lumion材质系统室内渲染6.3

    材质系统是对于导入的模型,而不对自带的模型起作用.自带的模型有的能改变属性. 点击“材质”点击墙,出来材质库.为墙体赋予一种材质, 完成后点击保存,就可以保存了.然后给窗户添加玻璃材质. 可以看到墙, ...

  9. Ubuntu系统建立交叉编译环境

    飞凌 FET6818核心板 解压编译器: tar zxvf arm-cortex_a9_eabi-4.7-eglibc-2.18.tar.gz  -C/opt 设置默认编译环境: vi /etc/pr ...

  10. ubuntu 服务restart失败

    在ubuntu使用时碰到的一个问题,修改了nginx服务的配置文件后,重启服务,显示 * Restarting nginx nginx                                  ...