TCP如何保证可靠传输

TCP协议保证数据传输可靠性的方式主要有:

  • 校验和
  • 确认应答+序列号
  • 超时重传
  • 流量控制
  • 拥塞控制

校验和
发送的数据包的二进制相加然后取反,目的是检测数据在传输过程中的任何变化。如果收到段的检验和有差错,TCP将丢弃这个报文段和不确认收到此报文段。

确认应答+序列号
TCP给发送的每一个包(报文段)进行编号,接收方对数据包进行排序,把有序数据传送给应用层。

超时重传
当TCP发出一个段后,它启动一个定时器,等待目的端确认收到这个报文段。如果不能及时收到一个确认,将重发这个报文段

流量控制
TCP连接的每一方都有固定大小的缓冲空间,TCP的接收端只允许发送端发送接收端缓冲区能接纳的数据。当接收方来不及处理发送方的数据,能提示发送方降低发送的速率,防止包丢失。TCP使用的流量控制协议是可变大小的滑动窗口协议。

接收方有接收窗口(即时窗口),随ACK报文发送

拥塞控制
当网络拥塞时,减少数据的发送
发送方有拥塞窗口,发送数据前比对接收方发过来的接收窗口,取小
慢启动、拥塞避免、快恢复、快重传

应用数据被分割成TCP认为最适合发送的数据块(报文段)。
TCP的接收端会丢弃重复的数据

1. 校验和

计算方式:在数据传输的过程中,将发送的报文段都当做一个16位的整数。将这些整数加起来。并且前面的进位不能丢弃,补在后面,最后取反,得到校验和。

发送方:在发送数据之前计算检验和,并进行校验和的填充
接收方:收到数据后,对数据以同样的方式进行计算,求出校验和,与发送方的进行比对
TCP如何保证可靠传输_第1张图片
注意:如果接收方比对校验和与发送方不一致,那么数据一定传输有误。但是如果接收方比对校验和与发送方一致,数据不一定传输成功。

2. 确认应答与序列号

序列号:TCP传输时将每个字节的数据都进行了编号,这就是序列号。 (TCP传输字节流)

确认应答:TCP传输的过程中,每次接收方收到数据后,都会对传输方进行确认应答。也就是发送ACK报文。这个ACK报文当中带有对应的确认序列号,告诉发送方,接收到了哪些数据,下一次的数据从哪里发。
TCP如何保证可靠传输_第2张图片
序列号的作用不仅仅是应答的作用,有了序列号能够将接收到的数据根据序列号排序,并且去掉重复序列号的数据。这也是TCP传输可靠性的保证之一。

3. 超时重传

在进行TCP传输时,由于确认应答与序列号机制,也就是说发送方发送一部分数据后,都会等待接收方发送的ACK报文,并解析ACK报文,判断数据是否传输成功。如果发送方发送完数据后,迟迟没有等到接收方的ACK报文,这该怎么办呢?而没有收到ACK报文的原因可能是什么呢?

首先,发送方没有接收到响应的ACK报文原因可能有两点:
a、数据在传输过程中由于网络原因等直接全体丢包,接收方没有接收到
b、接收方接收到了响应的数据,但是发送的ACK报文响应却由于网络原因丢包了

TCP在解决这个问题的时候引入了一个新的机制,叫做超时重传机制。简单理解就是发送方在发送完数据后等待一个时间,时间到达没有接收到ACK报文,那么对刚才发送的数据进行重新发送。如果是刚才第一个原因,接收方收到二次重发的数据后,便进行ACK应答。如果是第二个原因,接收方发现接收的数据已存在(判断存在的根据就是序列号,所以上面说序列号还有去除重复数据的作用),那么直接丢弃,仍旧发送ACK应答。

那么发送方发送完毕后等待的时间是多少呢?如果这个等待的时间过长,那么会影响TCP传输的整体效率,如果等待时间过短,又会导致频繁的发送重复的包。如何权衡?

由于TCP传输时保证能够在任何环境下都有一个高性能的通信,因此这个最大超时时间(也就是等待的时间)是动态计算的

超时以500ms(0.5秒)为一个单位进行控制,每次判定超时重发的超时时间都是500ms的整数倍。重发一次后,仍未响应,那么等待 2 * 500ms 的时间后,再次重传。等待4 * 500ms的时间继续重传。以一个指数的形式增长。累计到一定的重传次数,TCP就认为网络或者对端出现异常,强制关闭连接。

4. 连接管理

连接管理就是三次握手与四次挥手的过程,在前面详细讲过这个过程,这里不再赘述。保证可靠的连接,是保证可靠性的前提。

4.1 流量控制

接收端在接收到数据后,对其进行处理。如果发送端的发送速度太快,导致接收端的结束缓冲区很快的填充满了。此时如果发送端仍旧发送数据,那么接下来发送的数据都会丢包,继而导致丢包的一系列连锁反应,超时重传呀什么的。而TCP根据接收端对数据的处理能力,决定发送端的发送速度,这个机制就是流量控制

在TCP协议的报头信息当中,有一个16位字段的窗口大小。在介绍这个窗口大小时我们知道,窗口大小的内容实际上是接收端接收数据缓冲区的剩余大小。这个数字越大,证明接收端接收缓冲区的剩余空间越大,网络的吞吐量越大。接收端会在确认应答发送ACK报文时,将自己的即时窗口大小填入,并跟随ACK报文一起发送过去。而发送方根据ACK报文里的窗口大小的值的改变进而改变自己的发送速度。如果接收到窗口大小的值为0,那么发送方将停止发送数据。并定期的向接收端发送窗口探测数据段,让接收端把窗口大小告诉发送端。
TCP如何保证可靠传输_第3张图片

16位的窗口大小最大能表示65535个字节(64K),但是TCP的窗口大小最大并不是64K。在TCP首部中40个字节的选项中还包含了一个窗口扩大因子M,实际的窗口大小就是16为窗口字段的值左移M位。每移一位,扩大两倍。

4.2 拥塞控制

TCP传输的过程中,发送端开始发送数据的时候,如果刚开始就发送大量的数据,那么就可能造成一些问题。网络可能在开始的时候就很拥堵,如果给网络中在扔出大量数据,那么这个拥堵就会加剧。拥堵的加剧就会产生大量的丢包,就对大量的超时重传,严重影响传输。

所以TCP引入了慢开始的机制,在开始发送数据时,先发送少量的数据探路。探清当前的网络状态如何,再决定多大的速度进行传输。这时候就引入一个叫做拥塞窗口的概念。发送刚开始定义拥塞窗口为 1,每次收到ACK应答,拥塞窗口加 1。在发送数据之前,首先将拥塞窗口与接收端反馈的窗口大小比对,取较小的值作为实际发送的窗口。

慢开始算法 和 拥塞避免算法

发送方维护一个发送窗口,发送窗口的大小取决于网络的拥塞情况和接收窗口的大小,发送窗口是动态变化的。
发送方还维护一个慢开始门限
拥塞窗口 < 慢开始门限:使用慢开始算法
拥塞窗口 > 慢开始门限:使用拥塞避免算法
拥塞窗口 = 慢开始门限:使用慢开始算法或拥塞避免算法

算法的具体过程:

  1. 通信开始时,发送方的发送窗口设为1,并发送第一个分组M1;
  2. 接收方收到M1后,返回确认应答,此时发送方发送窗口扩大两倍,并发送M2、M3;(即,发送方每次收到确认应答后,都将发送窗口设为当前值的两倍)
  3. 若拥塞窗口>慢开始门限,则使用拥塞避免算法,每次收到确认应答后都将发送窗口+1;
  4. 若发送方出现了超时重传,则表明网络出现拥塞,此时:
    a)慢开始门限设为当前发送窗口的一半;
    b)发送窗口设为1;
    c)启用拥塞避免算法;

发送超时重传时,发送窗口有可能已经超过了慢开始门限,也有可能还没超过;此时不管何种情况,都一律启用拥塞避免算法,并执行上述三步操作!

慢开始算法的作用:慢开始算法将发送窗口从小扩大,而且按指数级扩大,从而避免一开始就往网络中注入过多的分组从而导致拥塞;它将窗口慢慢扩大的过程其实也在探测网络拥塞情况的过程,当发现出现拥塞时,及时降低发送速度,从而减缓网络拥塞。慢开始算法不是增长很慢,只是初始值比较小。
拥塞避免算法的作用:拥塞避免算法使发送窗口以线性方式增长,而非指数级增长,从而使网络更加不容易发生拥塞。

AIMD算法(加法增大乘法减小算法)
慢开始算法 和 拥塞避免算法 还有个名称叫做加法增大乘法减小算法。
加法增加:指的是拥塞避免算法,使得发送窗口以线性的方式增长;
乘法减小:指的是不管当前正使用慢开始算法还是拥塞避免算法,只要发生拥塞时,慢开始门限将会变成当前窗口的一半。

快重传算法 和 快恢复算法

上述慢开始算法和拥塞避免算法能保证网络出现拥塞时进行相应的处理,而快重传和快恢复是一种拥塞预防的方式,此时网络可能尚未出现拥塞,但已经有拥塞的征兆,因此得作出一些预防措施

快重传原理:因为TCP具有累计确认的能力,因此接收者收到一个分组的时候不会立即发出应答,可能需要等待收到多个分组之后再同一发出累计确认。但快重传算法就要求,接收者如果接收到一个乱序的分组的话,就必须立即发出前一个正确分组的确认应答,这样能让发送者尽早地知道有一个分组可能丢失。

快恢复原理:当发送者收到同一个分组的三个确认应答后,就基本可以判断这个分组已经丢失了;这时候无需等待超时,直接执行乘法减小加法增大(AIMD,将慢开始门限降为当前拥塞窗口的一半,然后直接采用拥塞避免算法,从慢开始门限开始):

  1. 将慢开始门限减半;
  2. 将发送窗口减半(不设为1);
  3. 使用拥塞避免算 ;
    TCP如何保证可靠传输_第4张图片
    更多拥塞控制讲解以及滑动窗口讲解请参考:https://blog.csdn.net/jinjiniao1/article/details/90698643#334__151

TCP为什么引入接受缓存这个数据结构?
如果没有接受缓存的话,或者说只有一个缓存的话,为了保证接受的数据是按顺序传输的,所以如果位于x序号之后的序号分组先到达目的主机的运输层的话必然丢弃,这样的话将在重传上花费很大的开销,所以一般如果有过大的序号达到接收端,那么会按照序号缓存起来等待之前的序号分许到达,然后一并交付到应用进程

TCP是以段为单位进行数据包的发送的。
(1)在建立TCP连接的同时,也可以确定发送数据包的单位,称之为“最大消息长度”:MSS。最理想的情况是,最大消息长度MSS正好是IP层中不被分片处理的最大数据长度。
(2)TCP在传送大量数据的时候,是以“段=MSS的大小”将数据进行分割发送的,进行重发时也是以MSS为单位的。
(3)最大消息长度——MSS是在三次握手的时候,在两端主机之间被计算得出的。两端主机在发出“建立TCP连接请求的SYN包”时,会在SYN包的TCP首部中写入MSS选项,告诉对方自己所能够适应的MSS的大小,然后发送端主机会在两者之间选择一个较小的MSS值投入使用。

TCP 粘包/拆包的原因及解决方法

TCP是以流的方式来处理数据,一个完整的包可能会被TCP拆分成多个包进行发送,也可能把小的封装成一个大的数据包发送。

TCP粘包/分包的原因:

  • 应用程序写入的字节大小大于套接字发送缓冲区的大小,会发生拆包现象,而应用程序写入数据小于套接字缓冲区大小,网卡将应用多次写入的数据发送到网络上,这将会发生粘包现象;
  • 进行MSS大小的TCP分段,当TCP报文长度-TCP头部长度>MSS的时候将发生拆包
  • 以太网帧的payload(净荷)大于MTU(1500字节)进行ip分片。

解决方法

  • 消息定长:FixedLengthFrameDecoder类
  • 包尾增加特殊字符分割:行分隔符类:LineBasedFrameDecoder或自定义分隔符类 :DelimiterBasedFrameDecoder
  • 将消息分为消息头和消息体:LengthFieldBasedFrameDecoder类。分为有头部的拆包与粘包、长度字段在前且有头部的拆包与粘包、多扩展头部的拆包与粘包。

5. 字节序号与报文段序号

接下来的内容是学习后续内容的基础,必须先讲清楚。为了方便你回忆 TCP 首部,这里再次把这个图贴出来,以便对照。
TCP如何保证可靠传输_第5张图片

5.1 序号

5.1.1 序号存在的意义
首先得弄清楚为什么要有序号。

在 APUE 基础中,我们通过 TCP 协议将数据发送给对方,就比如 helloworld,这一串字节流,假设被拆分成了三个 TCP 报文段,第一个报文段携带了 hel,第二个报文段携带了 lowo,第三个报文段携带了 rld,这三个报文段不一定是按照顺序送到对端的,那么对端收到这三个段是如何确定他们的顺序的呢?此时序号的意义就体现在这里。

5.1.2 序号
序号占用 4 字节,即 32 位。它的范围是 [0 , 232−1] [ 0 , 232−1],也就是说一共有 4 294 967 296 个序号。TCP 协议中的序号,指的是报文段序号

字节序号
TCP 连接中,为传送的字节流(数据)中的每一个字节按顺序编号。也就是说,在一次 TCP 连接建立的开始,到 TCP 连接的断开,你要传输的所有数据的每一个字节都要编号。这个序号称为字节序号

初始序号 ISN
当新连接建立的时候,第一个字节数据的序号称为 ISN(Initial Sequence Number),即初始序号。ISN 一开始并不一定就是 1。在 RFC (规定网络协议的文档)中规定,ISN 的分配是根据时间来的。当操作系统初始化的时候,有一个全局变量假设为 g_number 被初始化为 1(或 0),然后每隔 4us 加 1. 当 g_number 达到最大值的时候又绕回到 0.当新连接建立时,就把 g_number 的值赋值给 ISN.

在 BSD 系统中,这段代码实现时并未遵守协议,它将 g_number 初始化为 1,每 8us 加 1,也就是说,每隔 1 秒增加 125000,约 9.5 小时后 g_number 又绕回到了 0.

初始序号是非常非常重要的概念,它告诉对端,第一个报文段是谁!而三次握手的目的,就是为了确认初始序号

报文段序号
如果一个 TCP 报文段的序号为 301,它携带了 100 字节的数据,就表示这 100 个字节的数据的字节序号范围是 [301, 400],该报文段携带的第一个字节序号是 301,最后一个字节序号是 400.
TCP如何保证可靠传输_第6张图片上图中,报文段序号是 2379453244,它携带了 6 字节的数据 hello\0,这 6 字节的数据字节序号就是从 h->2379453244,e->2379453245 一直到最后一个空字符 \0->2379453249.

注意:序号字段只有在下面两种情况的任意一种才有意义:
数据字段至少包含一个字节
这是一个 SYN 段,或者是 FIN 段,或者是 RST 段

5.2 确认号

每传送一个 TCP 段,都要等待对方回复一个确认。不过这种方式效率太低,在 TCP 协议中,一般采用累积确认的方式,即每传送多个连续 TCP 段,可以只对最后一个 TCP 段进行确认。

对方通过回复一个确认号,来表示确认已经接收到了哪个 TCP 段。比如发送方发送了一个报文段序号为 301 的 TCP 段,这个段携带了 100 字节数据,则接收方应当回复的确认号是 401,它表示接收方已经收到了字节序号为 [0, 400] 的数据,现在期望你发送字节序号为 401 以及以后的数据。

只有当 ACK 标志位被置位的时候,确认号这个字段才有效。

一次完整的 TCP 连接到释放的过程
TCP如何保证可靠传输_第7张图片
为了能够清晰的看到客户端与服务器的交互过程,这里将它画成了下面的时序图。

TCP如何保证可靠传输_第8张图片

tcp如何保证传输的可靠性:

  • 合理分片:将数据分割成最适合tcp发送的数据块
  • 超时重传:tcp发送端发送数据后会启动一个计时器,当计时器超过某个时间没有收到接收端的确认就,重新发送数据。
  • 确认:tcp接收端接收到数据后发送确认给发送端。
  • 校验:tcp接收到数据检验发现数据有误,丢弃报文段,不给出相应,发送端会超时重传
  • 失序重排:tcp是用ip数据报传送数据的,ip数据报到达会失序,因此数据到达也会失序。Tcp会对失序的数据重新排列。
  • 重复丢弃:对收到的重复数据丢弃掉。
  • 流量控制:当接收端来不及处理发送端发送的数据,能提示发送端降低发送的速率,防止包丢失。
  • 拥塞控制:当网络拥塞时,减少数据的发送。

序号部分参考:https://blog.csdn.net/q1007729991/article/details/69261780

你可能感兴趣的:(计算机网络,TCP如何保证可靠传输)