TCP原理之:TCP数据传输

TCP(Transmission Control Protocol)全称为传输控制协议, 它工作在网络七层模型中的第四层-传输层, 是一种面向连接的可靠的数据传递协议。 对于IP和UDP协议, 它们会在接收到数据后根据数据的校验值来对数据的有效性进行判断, 对于无效的数据会直接丢弃, 而不会去纠正。 相比于UDP协议, TCP协议显得更“安全”, 它在数据失效时会进行“重传”以确保数据的正确性。本文主要讨论TCP数据传输过程中所涉及到的一些基本知识,包括报文的格式,TCP连接建立和TCP数据包发送过程的分析,并对滑动窗口和SACK做了相关介绍。

1 TCP头部和封装

传输队列中的TCP数据包常被称为”分组“,因此下文用”分组“来代指TCP数据包。

在发送TCP报文时, 需要对TCP报文进行层层的封装, 由于最终的报文是以以太网帧的形式发送出去的, 因此TCP报文要经历TCP数据报→IP数据报→以太网帧的封装。 TCP在IP数据报中的封装如下图所示。 TCP报文的头部不带选项的话为20个字节,带选项可达60字节。

TCP头部的结构如下图所示, 每个TCP头部都包含了16位的源端口和目的端口。 TCP头部中的端口号和IP头部中的IP地址唯一地标示了一个连接, IP地址和端口的组合也被称为端点(endpoint)套接字(socket)

头部长度字段指出了TCP头部的长度,它是以32位为单位的(即4字节),其最小值为5,代表头部长度为4*5=20字节。

SEQ(序列号):该字段代表着该数据报中所携带在数据中的第一个字节在数据流中的位置, 它是一个32位的无符号数, 在到达232−12^{32}-1232−1后再循环到0。
ACK(确认号):字段代表着接收方期望接收的下一个序列号。
SYN(同步位):该标志位用于TCP连接的建立, 详见下文中的TCP连接。
FIN(结束位):发送方已经结束报文的发送。
RST(重置位):重置连接。

2 TCP三次握手连接

TCP的连接通常分为三个阶段:启动, 数据传输和退出。 TCP连接的建立分为三个阶段, 需要发送三个报文, 因此该连接方式也被称为三次握手协议,步骤如下:

  1. 首先客户端(这里将其简称为:C)向服务端(这里将其简称为:S)发送一个TCP报文, 该报文的SYN位被设为1, 序列号SEQ被设置为一个初始值, 这里将其记为ISN©。 这个报文被称为段1。 此时C的状态为SYN_SEND, 即同步位已发送状态。
  2. S在接收到该报文后, 通过报文的SYN位得知这是一个段1的报文, 因此它需要发送一个报文作为响应, 该报文被称为段2。 段2的SYN位被设置为1, ACK字段被设置为ISN(c)+1, SEQ字段被设置为一个初始值, 这里将其记为ISN(s)。此时S的状态由一开始的LISTEN(监听)变为SYN_REV(同步位已收到)
  3. C在接收到段2后首先校验该报文的SYN及ACK段是否正确, 如果正确则向S发送一个响应报文, 该报文被称为段3。 段3的ACK=ISN(s)+1, SYN被设置为0。 段3在发送后, C端的状态变为ESTABLISHED(已连接), S段在收到该报文后状态也变为ESTABLISHED(已连接)。至此, 三次握手完成, 连接建立。

3 TCP数据的发送

TCP分组: TCP是可靠传输协议,通过超时与重传机制,来保证收到的数据是完整的。因为TCP是可靠传输协议,如果要传输的数据大于 1480 - 20(tcp头部) =1460Byte时,在ip层被分片,而ip层分片会导致,如果其中的某一个分片丢失,因为tcp层不知道哪个ip数据片丢失,所以就需要重传整个数据段,这样就造成了很大空间和时间资源的浪费,为了解决这个问题,就有了tcp分组和MSS(最长报文大小)概念,利用tcp三次握手建立链接的过程,交互各自的MTU,然后用小的那个MTU-20-20 , 得到MSS,这样就避免在ip层被分片。

在TCP连接建立后, TCP便进入了数据传输的状态, 数据发送的步骤如下图所示,具体为:

  1. 在发送数据时, 发送方对分组数据进行封装, 这里将该分组称为F(x)。 设置F(x)分组的SEQ为其携带的数据的首字节的序号,这里假设其为100; ACK设置为发送方上一次接收到的分组的SEQ+数据长度+1,这里假设其为1。
  2. 接收方接收到该分组后, 会向发送方发送一个确认报文, 这里将其称为F(x+1)。 该报文的ACK字段为F(x).SEQ+LEN, 即希望接收的下一个分组的序列号,这里为100+60=160(注意该分组的最后一个字节的序列号为159)。

在上述传输中, 若F(x+1)无法顺利到达或者延迟到达发送方, 则会导致F(x)的重发。 这种情况可能导致接收方收到多个F(x)的副本,接收方通过SEQ来判断是否接收过该分组, 并丢弃重复的分组。

延迟确认:

接收端在收到数据包后不立马进行ACK数据包的发送,而是等待一定的时间,并统一对收到的多个数据包进行ACK,这种方法称为延迟确认。延迟确认能够减少数据包的发送,节约资源。当接收队列中存在失序分组时,延迟确认将不起作用:收到任意数据包时都将立马进行ACK。

4 分组窗口和滑动窗口

在上述的TCP分组发送过程中, 上一个分组发送成功并得到确认后, 下一个分组才能发送, 而这中间的“等待”会造成效率的降低。 如果同时允许多个分组进入网络又会引发一系列的问题。 为解决这一些列的问题, 滑动窗口的概念被提出。

建立TCP的两端都维护着一个发送窗口结构和接收窗口结构。发送窗口指的是将发送的TCP分组按照其序列号顺序放置到一个窗口中,窗口左边为已确认的分组,右边为待发送的分组,窗口里为已发送但还未确认的分组,如下图所示。图中,2、3为已确认的分组,10、11为待发送的分组,4-9为窗口中的已发送待确认的分组。

此时当我们收到4号分组的ACK,10号分组会被发送从而进入窗口,4号分组得到确认从而退出窗口,这个过程仿佛窗口往右滑动了一段,因此称为滑动窗口

接收窗口的实现逻辑与发送窗口类似,这里不再赘述。

窗口中的分组数量称为窗口大小,可以简单理解为图中窗口的宽度,其实就是窗口中的所有分组所携带的数据大小。窗口过大会导致占用较大的缓存,浪费内存;窗口过小将影响传输的效率,因此窗口大小要设置在一个合理的范围内。TCP通讯双方通过TCP报文中的16位的窗口大小字段来设置对方发送窗口的大小,16位导致窗口最大只能设置为65535,为了提高效率,增大窗口大小,可以中TCP头部的选项中设置窗口缩放字段(16位),最终的窗口大小为窗口大小*窗口缩放

在分组传输过程中,窗口的大小是动态变化的,接收方会通过窗口通告发送分组告知发送方采用多大的窗口大小(简称为:目标窗口大小)。当目标窗口大小小于当前窗口大小时,发送窗口的右边界不动,左边界右移,实现窗口的“缩小”;当目标窗口大小大于当前窗口大小时,发送窗口的左边界不动,右边界右移,实现窗口的“放大”。

在数据发送过程中,当接收方跟不上发送方的速度时,需要告知发送方慢下来,这称为流量控制。使用滑动窗口能够很好的进行流量控制:接收方通过通告较小的窗口大小来降低发送方的发送速度。当接收方忙碌时,可以将目标窗口大小设置为0,从而使发送方停止发送。此时发送方将定期向接收方发送探测报文(keep-alive)来查看接收方窗口的状态,一旦查询到目标窗口大小为非零值,将继续进行分组的发送。

5 超时与重传

当发送方发送的分组长时间得不到确认,收不到该分组的ACK时,就认为该分组已经超时,需要对其进行重新发送。目前常用的重传方法有定时器重传和超时重传。

定时器重传

定时器重传指的是为窗口中每个已发送的报文设置一个超时定时器,一旦定时器超时后还没有收到该报文的ACK,则进行重传。重传超时RTO的设置一般为动态设置,根据RTT的值来确定(过程比较复杂)。

快速重传

快速重传指的是在接收端接收到非预期的报文(未按照预期顺序到达的报文),ACK不进行延迟,而是立马返回以请求缺失的报文。发送端在接收到该ACK后,若其请求的报文未超时,则不立马进行重传,而是记录请求该报文的重复的ACK的数量,当到达一定的阀值(一般为3)后则进行重传。

Nagle算法

Nagle算法是为了应对小报文在数据发送过程中产生数据浪费,如发送1字节的数据会发送64字节的报文,造成63字节的浪费。其基本思想是:在收到前一个报文的ACK前,不发送下一个报文,而是将这段时间内添加到发送队列中的小报文重组为一个报文(不超过1500字节),然后在收到前一个报文的ACK后将其发送出去。这种机制会导致报文传输的一定延迟,对于实时性要求比较大的场景不适用,可通过内核的TCP_NODELAY选项进行禁用。

6 SACK

TCP的ACK是一种累积的确认方法,因此在进行ACK时只能发送已连续到达的序列号最大的分组的ACK。下图为在网络丢包或者分组乱序到达时的接收方的接收窗口情况,红色报文代表已到达报文。很明显,此时4、7、8、9报文很可能已经发生了网络丢包的情况,但在进行申请重传时只能发送3号报文的ACK以申请4号报文的重传,无法申请7、8、9报文的重传。这种串行的重传机制降低了重传的效率,在网络丢包严重的情况下将十分影响数据的传输。

为了解决这一问题,SACK(Selective Acknowledgment,选择确认)被提出。在TCP连接时可以通过报文选项SACK-Permitted Option来设置是否启用SACK。SACK与普通的ACK报文相同,只不过在选项中设置了额外的SACK确认信息,其格式如下图所示。kind=5代表着这个选项是SACK确认选项,length代表选项的长度;块1代表着最新到达的分组所在数据块,左边界为该块最左边数据的序列号,右边界为最右边数据的序列号+1;块2为第二新的数据块。假设上图中分组到达的顺序为05、10、06,则块1为05、06分组,块2为10分组。块1的左边界为05分组的序列号,右边界为06分组的ACK。由于TCP报头长度的限制(选项最大长度为40字节),SACK中最多只能包含三个确认块(时间戳等信息也要占用选项空间)。从上述分析我们可以看出SACK是可以重复确认的,即多次确认同一个数据块,这从一定程度上提高了网络传输的容错性。收到SACK后,发送方把对应的分组状态设置为SACKed,在进行超时时不会重发。

发送方在接收到SACK后,可以推断出空缺的分组,并在合适的时机对其进行重发。从上图中我们可以看出07、08、09号分组为空缺分组。空缺也可以理解为被SACKed最右边的分组左边的所有未被SACKed的分组。SACK重发流程如下图所示。

案例分析

下面将以案例的形式来讲解一下SACK数据具体的发送与接收流程(案例来自RFC2018)。假设发送窗口左边界(最左边分组的序列号)为5000,窗口中有8个分组,每个分组长度为500字节。

情景1:8个分组中的前4个分组顺利到达,此时没有产生失序分组,接收方会返回正常的不带SACK选项的ACK数据包,该数据包的ACK为7000(5000+4*500)。

情景2:1号分组丢失,后面7个分组顺利到达。因为后面七个分组都是失序分组,因此都会触发ACK数据包的立刻发送。假设后面七个数据包是按照正常顺序到达的,则接收方返回的ACK数据包如下:

触发的分组 ACK 块1左边界 块1右边界 块2左边界 块2右边界 块3左边界 块3右边界
5500 5000 5500 6000
6000 5000 5500 6500
6500 5000 5500 7000
7000 5000 5500 7500
7500 5000 5500 8000
8000 5000 5500 8500
8500 5000 5500 9000

情景3:第2、4、6、8个分组丢失,第1、3、5、7分组正常到达。由于1号分组属于正常到达的分组,因此会返回正常的ACK。而3号及以后的分组属于失序分组,会触发ACK的立刻返回并携带SACK信息。

触发的分组 ACK 块1左边界 块1右边界 块2左边界 块2右边界 块3左边界 块3右边界
5000 5500
6000 5500 6000 6500
7000 5500 7000 7500 6000 6500
8000 5500 8000 8500 7000 7500 6000 6500

假设此时4号分组到达了(可能是因为网络延迟而导致其晚到达,也可能触发了重发),此时的ACK信息如下:

触发的分组 ACK 块1左边界 块1右边界 块2左边界 块2右边界 块3左边界 块3右边界
6500 5500 6000 7500 8000 8500

再假设此时2号分组也到达了,则ACK信息如下:

触发的分组 ACK 块1左边界 块1右边界 块2左边界 块2右边界 块3左边界 块3右边界
5500 7500 8000 8500

食言:

当接收端丢弃被SACK的分组时,食言的情况就发生了。当发送端检测到食言情况时,将取消所有分组SACKed的标识,并进行重发。因为SACK只是一种建议项,所以不能完全根据SACK来判断是否接收到数据包,ACK是唯一判断的依据。

TCP原理之:TCP数据传输相关推荐

  1. 【Java 网络编程】网络通信原理、TCP、UDP 回显服务

    一.网络发展历史 互联网从何而来? 这要追溯到上个世纪 50 - 60 年代,当时正逢美苏争霸冷战,核武器给战争双方提供了足够的威慑力,想要保全自己,就要保证自己的反制手段是有效的. 如何保证能够反击 ...

  2. 网络原理:TCP/UDP

    目录 一.数据组织格式 1.1 xml 1.2 json 1.3 protobuffer 二.传输层重要协议---UDP协议 2.1 UDP协议端格式 2.2 校验和 三.传输层重要协议---TCP协 ...

  3. 网络原理之TCP/IP协议

    文章目录 1.网络基础 1.1 认识IP地址 1.1.1概念 1.1.2 作用 1.1.3 格式 1.1.4 组成 1.1.5 分类 1.2 子网掩码 1.2.1 格式 1.2.2 作用 1.2.3 ...

  4. 【Javaweb】TCP原理(三次握手四次挥手)

    目录 一.TCP协议 二.TCP原理 1,确认应答机制 2,超时重传机制 3,连接管理机制 4,滑动窗口 5,流量控制 6,拥塞控制 7,延迟应答 8,捎带应答 9,粘包问题 一.TCP协议 TCP, ...

  5. TCP原理,Socket与网络编程入门

    TCP原理,Socket与网络编程入门 开篇 从互联网的诞生以来,网络程序逐渐普及.计算机网络将各个计算机连接到一起使它们可以通信.在现代,网络已成为我们十分重要的一部分.这次不搞些费脑子的东西,就让 ...

  6. 计算机网络原理fin,一个动画看懂网络原理之TCP建立和释放过程

    一个动画看懂网络原理之TCP建立和释放过程 一.TCP的概念 TCP(Transmission Control Protocol 传输控制协议)是一种面向连接的.可靠的.基于字节流的传输层通信协议.T ...

  7. JavaEE:网络原理之TCP/IP

    文章目录 一.网络基础 1.认识 IP 地址 2.子网掩码 3.认识 MAC 地址 4.总结IP地址和MAC地址 二.应用层重点协议 1.DNS 2.NAT 3.NAPT 4.HTTP/HTTPS 三 ...

  8. JaveEE UDP 与 TCP 原理

    这篇博客真的很详细很详细很详细,不打算试试看吗 > .o 文章目录 JaveEE & UDP 与 TCP 原理 1. 应用层协议(自定义组织格式) 2. 传输层UDP协议 2.1 数据报 ...

  9. TCP原理和三次握手和四次挥手过程

    TCP原理和三次握手和四次挥手过程 TCP原理和三次握手和四次挥手过程 TCP是什么?有什么作用? 三次握手 连接建立 详细过程 四次挥手 连接终止 详细过程 参考 TCP原理和三次握手和四次挥手过程 ...

最新文章

  1. 30年前的热门研究,今获经典论文奖,贝叶斯网络之父旧论文「考古」
  2. python使用base64编码解码数据
  3. Nginx 配置实战:负载均衡的实现
  4. mysql5.5数据库权限知识补充
  5. dubbo中log4j检查(开发环境中建议设置为false)
  6. 基于JAVA+SpringMVC+MYSQL的报价管理系统
  7. 根据接口文档中的入参,生成自动化测试用例中的异常测试用例,包含用例描述,用例数据
  8. 重装也无法修复此计算机,遇到Win7系统崩溃无法修复的情况怎么办
  9. java textfield事件_java – 以编程方式触发JTextField中的键事件...
  10. Web服务器基础详解
  11. python3携程多任务_python3之携程yield及greenlet
  12. C# 删除注册表信息
  13. Android用浏览器打开pdf文件
  14. Word页码从任意页开始设置方法
  15. linux rsh通信实现_RSH的网络通信细节
  16. 微信Windows客户端版本无法打开小程序问题的解决
  17. 滚动视差让你不相信“眼见为实”
  18. 视频教程-Ajax从入门到进阶视频课程(通俗易懂)-JavaScript
  19. oracle程序窗口,oracle窗口函数的使用
  20. Padding和Margin不同数量参数表示什么意思

热门文章

  1. spring framework 4 学习之路 1 -- 框架概述
  2. 内网搭建图片网站:软件安装配置 1-3
  3. 基于FileZilla Server文件服务器配置且支持公网解决方案
  4. 苹果cms安装 php映射,苹果cmsV10安装过程中的常见问题
  5. python图像识别 车牌_[图像处理] Python+OpenCV实现车牌区域识别
  6. RESTFul风格的API管理后台,基于Thymeleaf和layui
  7. 【Maven】Maven本地仓库无法下载依赖解决方案
  8. c语言二进制负数的除法,C语言中负数除法与右移取整问题
  9. Mac vim环境配置
  10. ucdos做程序用的c语言吗,C语言求教