网络原理之TCP协议(万字详解!!!)

目录

前言

TCP协议段格式

TCP协议相关特性

1.确认应答

2.超时重传

3.连接管理(三次握手、四次挥手)

三次握手(建立TCP连接)

四次挥手(断开连接)

4.滑动窗口

5.流量控制

6.拥塞控制

7.延迟应答

8.捎带应答

9.基于字节流

10.异常情况的处理

小结


前言

在前面,我们已经讲解了有关UDP协议的相关知识,但是在传输层,还有另一个重要的传输协议--TCP协议。UDP协议虽然保证了数据传输的速度,但是每次传输数据最多只能64kb,并且其传输不具备可靠性,在数据丢失后并不会重新传输。

这是就得提到TCP协议了,TCP协议是一个有连接的、可靠的、面向字节流、全双工的传输通信协议。虽然在传输速度上,TCP可能比不过UDP,但是TCP能够传输更大的数据以及在传输数据时具有可靠性,从而在平时我们更多的是使用TCP。

TCP协议段格式

  • 源/目的端口号:表示数据从哪个进程来,到哪个进程去;
  • 32位序号/32位确认序号:后面为大家细讲
  • 4位首部长度 :表示TCP头部信息的长度,单位是4字节(32比特位),所以TCP头部信息最长为15*4=60字节~
  • 保留位(6位) :在需要新增属性/某个属性的长度不够用时,就可以把保留位拿出来用。而UDP报文长度最大只有两个字节的长度,TCP这里就增加了可扩展性
  • 6个标志位:后面和大家讲
  • 16位窗口大小:后面细讲
  • 16位校验和:发送端填充,CRC校验,接收端校验不通过,则认为数据有问题,此处校验和不光包括TCP首部,也包括TCP数据部分。
  • 16位紧急指针:标识哪部分数据是紧急数据。
  • 40字节头部选项:表示可选项,属于报头部分,选项范围是0~40字节。

TCP报头长度最长为60字节,最短为20字节(选项为0字节),当4位首部长度为0时,说明报头长度为20字节,也就是选项长度为0字节;当4位首部长度表示15时,说明报头长度为60字节,也就是选项的长度为40字节。

接下来我们来了解TCP的四个特性。

TCP协议相关特性

  • 有连接:TCP是面向连接的,在传输数据之前,需要先建立连接;当传输完数据后,还需要释放连接。
  • 可靠传输:TCP协议通过确认应答、超时重传、滑动窗口等机制来保证了TCP中数据的可靠传输。确认应答机制保证了发送的数据段能够被接收方正确接收,而超时重传则能够在未接收到ack太长时间重新发送未被确认的数据段。滑动窗口机制则是通过一次发送多条数据,来提供数据的传输效率。
  • 面向字节流:TCP协议是以字节为单位来进行传输数据的,这意味在传输过程中,数据不会被分割成固定长度的数据段,而是根据应用程序的需求来组织和传输。
  • 全双工:TCP协议支持全双工通信,即发送方和接收方可以同时进行数据传输。这使得数据传输更加高效,但是也需要注意处理好流量控制和拥塞控制等问题。

那么TCP协议中,主要使用哪些机制来保证数据的可靠传输呢?

1.确认应答

确认应答 (Acknowledgment,简称ack )是TCP协议中保证数据的可靠传输的重要机制 之一。在TCP通信中,当接收方接收到发送方发送的数据段后,会向发送方发送一个确认应答序号(ack),来通知发送方已经接收到数据段。而接收方发送的确认应答序号,表示希望接收到下一个字节的序号。

确认应答的工作过程:

  1. 发送数据 :当TCP连接的发送方发送数据时,会为每个字节的数据都进行分配序号,即序列号。(注意:序列的分配只是针对TCP载荷部分来进行编号的,而TCP报头部分并不参与分配)
  2. 接收数据 :当接收方接收到发送方发送的数据后,会向发送方发送一个确认序号(ack),一般这个确认序号是下一次期望接受的数据段的序号。

那么发送在发送数据的时候,有没有可能会出现"后发先至"的情况呢?

其实也是有的,有可能是因为网络延迟、网络拥塞和数据包重传等问题,从而导致后发先至 。但是TCP会针对接收方收到数据进行重新排序,确保应用程序读取到的数据一定是和发送方发的数据顺序一致的

我们可以来举个例子:

假设我和几个同事和领导约了一个饭局,那么当我们先到达饭店之后,我们就需要等领导的到来,才能进行开始我们的饭局。这里的我和几个同事就相当于先到的数据,而领导则是需要先到的数据;而饭局就相当于进程B。

那么我们前面说的序列号和确认应答号到底有什么用呢?

序列号的作用

  • 保证了数据的顺序:TCP将传输的数据分割为TCP段,并为每个段分配一份序号,这些序号就能在接收方重新组合成原始的数据流,保证数据的顺序。
  • 保证了数据的完整性:如果数据在传输的过程中出现了丢失或者损坏的情况,接收端就可以根据序列号来判断哪些数据需要重传,从而保证数据的完整性。

确认应答号的作用

  • **告知发送端下一次期望接收的数据段的序号:**当接收端收到一个TCP数据段后,会向发送端发送一个确认应答序号,其中的确认应答序号表示下一次期望接收到的数据段的开头序号,这有助于数据的重复传输。
  • 实现可靠的数据传输:只有当发送端接收到确认应答序号,才会继续发送后序的数据段。如果发送端在规定的时间内没有接收到确认应答序号,就会重传未接收到确认应答序号的数据段,从而保证数据的可靠传输。

当主机A给主机B发送TCP数据报(TCP报头+TCP载荷),TCP报头中记录的是这次传输的载荷数据中第一个字节的序号,而其他的字节号则需要根据字啊和中数据长度依次推出。这里主机A给主机B发送1~1000的数据,那么B在读取完A发送的数据后,就会返回一个确认应答序号(1001)【一般读取的数据中末尾字节的序号+1】。那么下次主机A就需要发送从1001开始的数据报。

对于应答报文来说(就是接收端返回的数据报)确认序号就会按照收到的数据的最后一个字节序号+1的方式来填写,同时,在六个标志位中,第二位标志位ACK会设置为1,表明该报文是一个应答报文。若ACK标志位为0,说明该报文为普通报文。

注意**:应答报文默认情况下不带数据,除非某些特殊情况。**

2.超时重传

在网络通信的过程中,其实并不能保证数据能够一帆风顺的传输到,有可能会出现"丢包"的情况,什么是丢包呢?

丢包是指在网络传输过程中,数据包因为某些原因没能够正确地到达目的地,从而丢失的现象。丢包可能是网络问题、硬件故障、软件错误或者配置不当等原因造成的。在网络通信中,丢包是一个常见的问题,理解和解决丢包问题对于保证网络的稳定性和可靠性至关重要。丢包是网络性能中最核心的指标之一。

发生丢包,完全是随机的,不可预测的~

这里我们列举两种原因:

1.当数据传输过程中,发生了bit翻转,收到这个数据的接收方/中间的路由器,计算校验和,发生校验和对不上了,那么就需要将这个数据包给丢弃掉。

2.当数据在传输某个节点(路由器/交换机)时,由于该节点正处于高峰期,在单位时间内需要转发的包超过了N,此时该节点就接受不过来了,那么可能数据到这个节点时,该节点就会直接将数据给丢弃掉。

以下是可能出现丢包的两种情况:

  • 发送方发送的过程中丢包
  • 接收方发送确认序号给发送方时丢包

那么在发生丢包问题之后,TCP是如何处理的呢

TCP能做的是就是:在感知到数据是否丢包后,如果丢包了,就重新再发一次数据包

注意:超时重传的时间设定是动态变化的!!!

如果在重发一次在规定的时间t1内没有接收到确认应答序号,那么就会再次重发,第三次重传,超时时间为t2,t2>t1,且每重传一次,超时时间的间隔就会变大,从而重传的次数就会降低~

如果重传的次数越多,等待的时间就会越长,但也不代表这能够无限等待重传。当重传几次后,等待时间就会拉到一定的长度,就会认为数据再怎么传也是没有用,此时就会放弃该TCP连接,准确的来说是触发了TCP的重传连接操作。TCP首先会尝试进行"复位/重置 连接",发送一个特殊的数据包"复位报文",如果网络恢复了,复位报文就会重置连接,使得通信可以继续进行,但如果网络出现了严重的问题,复位报文也没有获取到回应,那么TCP就会单方面放弃连接。

假设网络丢包率为10%(这是一个很大的数字了),重传两次都失败的概率为1%,这个概率是非常小的,如果两次重传都不能接收到ACK的话,说明可能是网络或者硬件设备出现了问题。

那么这里可能就会有个问题,如果是在接收方接收到数据报后发送ACK的时候出现了丢包现象,那么这里TCP发送方在等待一段时间后,没有接收到ACK重新发送一份数据报文,那么岂不是会出现冲突?其实是会的,不过TCP在这里给出了相应的措施来解决这种情况:

TCP接收放在操作系统内核中存在一段内存空间,叫做"接收缓冲区",对于上一次接收到的数据,会先放入接收缓冲区中,当再一次接收到数据时,TCP会将这个后至的数据和接收缓冲区中的数据进行比较,如果发现后到的数据已经在接受缓冲区中存在,TCP就会将这个后来的数据给丢弃掉。

小结

接收缓存区不仅可以对数据进行重排序,也可以解决接收到重复数据的问题,从而确保应用程序接收和读取到的数据顺序是和发送方一致的。

确认应答和超时重传相互补充,共同构建了TCP的"可靠传输机制"!!!

"三次握手 四次挥手"只是决定了TCP具有建立连接和断开连接的特性,核心作用是确认应答+超时重传!!!

3.连接管理(三次握手、四次挥手)

三次握手(建立TCP连接)

对于三次握手、四次挥手,我们在平常可能会经常听到,那么这到底是什么意思呢?接下来我们就来认识认识~

三次握手就相当于通信双方打招呼,告诉对方我要和你建立连接。发送方首先会给接收方发送一个SYN(Synchronize Sequence Numbers,同步报文段),告诉对方我要和你建立连接,当接收方接收到SYN同步报文段后,接收方会给发送方发生一个ACK应答数据包,同时也发送一个SYN给发送方,告诉对方我要和你建立连接,当发生方接收到接收方发送的ACK应答数据包和SYN后,就会给返回一个ACK数据包给接收方。

那么可能就会有人问:这里不是三次握手吗?怎么发送了四次数据包呢?其实这里接收方在给发送方发送ACK数据包和SYN时,会打包成一次发送过去。

那么在接收到数据包之后,如何区分该数据包时SYN还是ACK数据包呢?其实在前面我已经讲过了,如果是ACK数据包的话,会在ACK标志位中记录该包为ACK包,若是SYN包的,就在SYN标志位中记录该包是SYN数据包。

三次握手除了解决连接问题,那么还可以解决哪些问题呢?

  • 确认当前网络是否畅通(进行可靠传输的前提)
  • 让发送方和接收方确认自己的发送和接受能力是正常的
  • 针对一些重要的参数进行协商

当通信双方进行三次握手建立连接的时候,互相会发送数据包,以此来判断双方是否能进行通信,如果数据包到达不了对方,并在重传几次之后依旧不能达到对方,说明双方不能进行正常的通信。

当通信双方要建立连接的时候,发送方会向接收方发送一个SYN同步数据段 ,如果接受方能够接收到SYN数据段,说明发生方的发送能力和接收方的接受能力没有问题 。接下来接收方就会给发送方发送ACK数据包和SYN数据段,如果发送方接收到ACK数据包和SYN数据包,说明发送方就会认为自己的接受能力和接收方的发送能力没有问题 。但由于此时接收方不清楚自己的发送能力是否正常,所以发送方在接收到ACK数据包和SYN数据包后还需要给接收方发送一个ACK数据包当接收方接收到这个ACK数据包后,就会认为自己的发送能力没有问题

TCP进行通信的时候,起始数据的序号是通过三次握手来协商确定的(换而言之,TCP序号并不是从1开始的),在每次建立连接的时候,TCP的起始序号都不同,而且故意差别很大。

那么TCP可不可以是两次握手?

其实是不可以的,如果是两次握手,那么通信双方就无法完成针对各自发送能力和接收能力的验证~看上面我举的例子就可以看出来,如果是两次握手的话,朋友就不能确定自己的麦克风是否正常。

那么可以不可以是四次握手呢?

其实是可以的,但是没有必要,四次握手的话,就是将接收方返回的ACK和SYN拆分为两次,但是没有必要做~

针对TCP协议来说,必须是三次握手,需要验证通信双方的发送接收能力是否正常!!!

但其他协议不一定是三次

在客户端启动之前,服务端是保证优先于客户端开启的,服务端和客户端一开始都是处理CLOSED状态,当服务端启动之后就处于LISTEN状态(监听),等待接收客户端的请求,当客户端启动之后,客户端向服务端发送请求,客户端的状态就由LISTEN -> SYN_SENT,当服务端接收到客户端发来的请求之后,就会从LISTEN ->SYN-RCVD,给客户端发送一个ACK+SYN的数据包;当客户端接收端这个ACK数据包之后,就会从SYS_SENT ->ESTABLISTEND状态,同时给服务端发送一个ACK;当服务端接收到ACK之后,就会从SYN_RCVD ->ESTABLISTEND状态,当客户端和服务器都处于ESTABLISTEND状态,说明双方已经建立成功连接。

四次挥手(断开连接)

当已经建立连接的双方需要断开的时候,也是需要类似三次握手的过程,在这个过程中,通信双方会给对方发送"FIN"和"ACK"这样具有特殊标志位的数据包。与建立连接时发送的SYN不同,断开连接需要用FIN数据包。

当主机A想要和主机B断开连接的时候,就会给主机B发送一个FIN数据包 . 当主机B接收到这个FIN数据包的时候,就会给发送方返回一个ACK确认应答数据包,并且还会返回一个FIN数据包;当主机A接收到ACK和FIN数据包后,就会给主机B返回一个ACK数据包,当主机B接收到这个ACK数据包的时候,主机A和B就会断开连接~

那么,这里主机B返回ACK和FIN数据包的时候,能不能和三次挥手那样呢?将ACK数据包和FIN数据包合一起一次性发送呢?

其实是不可以的,ACK数据包和SYN数据包都是由操作系统内核来操作发送的,而FIN的触发则是由应用程序通过调用close/进程退出来触发的~由于FIN数据包是由用户态操作发送的,中间需要经过怎么样的逻辑,我们是不知道的,所以什么时候调用到close方法我们也是不知道的。

这里四次挥手断开连接和三次握手建立连接还有地方不同,这里当主机A收到主机B发来的ACK数据包的时候不跟建立连接的时候主机A发完ACK后就立即建立连接,四次挥手当主机A收到主机B传来的ACK和FIN数据包的时候不会立刻和主机B断开连接,而是会等待一段时间后再和主机B断开连接。

为什么主机A在接收到ACK和FIN包后,还需要等待一段时间之后再断开连接呢?

如果主机A在接收到主机B传来的ACK和FIN数据包后,在给主机B发送ACK数据包的时候就断开连接,那么当主机A发送的ACK丢了咋办?此时如果主机B在等待一段时间后没有接受到主机A发送的ACK数据包,那么就会触发超时重传机制,给主机A发送ACK和FIN数据包,但是此时由于主机A已经和主机B断开了连接,主机A再也接收不到主机B发送的数据包,也就不会返回ACK数据包。当主机B重传几次后发现主机A依旧没有回应,此时主机B就会主动放弃这个TCP连接。准确的来说是触发了TCP的重传连接操作。TCP首先会尝试进行"复位/重置 连接",发送一个特殊的数据包"复位报文",这个复位报文的RST标志位为1,如果这个复位报文也没有获取到回应,那么TCP就会单方面放弃连接。

想一下,为什么这里的TIME_WAIT为什么是2MSL?

  • MSL是TCP报文的最大生存时间,因此TIME_WAIT持续2MSL的话,就能够保证两个传输方向上的尚未被接收或迟到的报文段都已经消失(否则服务器立即重启,可能会收到来自上一个进程的迟到的数据,但是这种数据很可能是错误的);
  • 痛死也是在理论上保证最后一个报文可靠到达(假设最后一个ACK丢失,那么服务器机会重发一个FIN,这时虽然客户端的进程不在了,但是TCP连接还在,仍然可能重发LAST_ACK);

TCP状态转移图

TCP相关状态:

  • CLOSED:TCP连接的初始状态,没有进行任何连接活动,当一个连接关闭后就会进行此状态。
  • LISTEN:服务器处于监听状态,等待接收客户端的连接请求。
  • SYN_SENT:客户端向服务端发送了一个SYN同步报文,请求建立连接,客户端发送完SYN就进入此状态。
  • SYN_RCVD:服务端接收到客户端的SYN报文后,给客户端发送SYN+ACK报文后进入此状态。
  • ESTABLISHED:双方成功建立连接,可以传输数据;当客户端接收到服务器的ACK+SYN报文后,给服务端发送一个ACK后就进入此状态。
  • FIN_WAIT_1:客户端给服务端发送了FIN(终止)报文,请求关闭连接,客户端发送FIN后进入此状态。
  • FIN_WAIT_2:客户端接收到了服务端发送的ACK报文,等待服务端发送的FIN报文,此时客户端就会进入此状态。
  • CLOSE_WAIT:服务器接收到客户端的FIN报文,并给客户端发送ACK报文后进入此状态。
  • LAST_ACK:服务端给客户端发送了FIN报文,并等待客户端的ACK报文,此时服务端进入此状态。
  • CLOSING:客户端和服务器几乎同时发送FIN报文,进入此状态,比较少见。
  • TIME_WAIT:客户端收到了服务端发送的FIN报文,并且给服务端发送ACK报文,等待2MSL后关闭连接。当客户端发送ACK报文后进入此状态。(2MSL是为了确保最后一个报文能够被服务端接收到~)。

4.滑动窗口

提到滑动窗口,可能大家都比较熟悉,有类算法题就是利用了滑动窗口的思想来解决的,算法中的滑动窗口就是借鉴了TCP的滑动窗口。

由于TCP是一个可靠传输的通信协议,所以在传输的时候效率就比较低,为了解决TCP传输效率慢的问题,TCP中采用了几个机制来优化TCP传输慢的问题。

对于这种一发一送的方式,明显效率很多,但是如果我们一次发送多条数据,就可以大大提高传输效率了(其实就是将多个段的等待时间重叠在一起)。

滑动窗口就是指发送方在发送数据的时候会指定一个窗口大小的数据,在这个窗口内的数据不用非要等上一个发送的数据接收到ACK数据包后才继续发送数据,而是当这个数据发送完之后,继续发送下一个数据包,直到这个窗口中所有的数据包都发送了之后才会进入等待TIME_WAIT,等待接收方发送的ACK数据包,这样比一发一收的效率要高上不少。

在滑动窗口的时候,其实也是会出现丢包的问题的,分为两种情况:

  • 接收方返回ACK数据包时丢包
  • 发送方发送的数据包丢失

对于这种情况,那么接收方有必要重传ACK数据包吗?

其实是没有必要的,不需要做任何处理 ,由于我们这里是采用了滑动窗口,那么肯定是批量发送数据,批量发送ACK,对于多个ACK,不可能全部丢包。确认序号表示的是期望下次接受到的数据的第一个序号,可以理解为就是在确认序号之前的数据都已经被接收到。我们看上图,这里虽然1001序号丢了,但是2001到达了发送方,发送方接收到2001,说明2001之前的数据接收方都已经接收到了,说明后一个ACK能够涵盖前一个ACK的意义。

我们可以看到1001-2000的数据包在传送的过程中丢包了,那么接收方就不会返回带有2001确认序号的ACK数据包,当接收2001~3000的数据包的时候,正常来说主机B需要返回一个带有3001确认序号的ACK数据包,但是这里由于前面主机B没有接收到1001~2000之间序列号的数据包,而我们知道在读取数据时是需要与主机A发送的数据顺序一致,所以在后面没有接收到1001~2000间序列号的数据包时,会一直向主机A返回带有1001确认序号的ACK数据包。当发送方发现收到了三次1001确认序号的时候就会意识到是不是以1001序号为起始序号的数据包丢失了,此时发送方就会重发这个丢失的数据包。这种能够快速识别出哪个数据丢包,并且针对性的重传,其他顺序到达的数据都无需重传,这种重传叫做"快速重传",可以认为是**"滑动窗口"下搭配的超时重传~**

当发送方重发这个10001~2000的数据包并且能够被接收方成功接收到,那么接收方下次返回的就不会一个1001确认序号的ACK数据包,而是会返回一个带有正确的确认序号的ACK数据包。看上图,由于在20001~7000的数据包已经在前面发过,并且被接收方成功接收,所以下次索要的数据包就是7001序号开头的数据包。

那么窗口是不是越大越好呢?其实不是的,如果窗口太大,传输效率就会太快了,导致接收方在短时间内接受不过来,那也会发生丢包的现象,那么我们如果来控制窗口的大小呢?

这里我们就需要用到另一个机制---流量控制

5.流量控制

TCP协议中的流量控制机制是确保数据传输过程中不会导致接收方缓冲区溢出的重要机制。流量控制是根据接收方的接收能力来制约发送方的发送速率,从而避免数据丢失或者拥塞

当接收方接收到发送方发来的数据包时,接收方会将这个数据包放到接收缓冲区中,并且将接受缓冲区的剩余空间大小通过ACK数据包发送给发送方告诉发送方我还可以容纳多大的数据,并且发送方下次发送的数据不能超过可以容纳的数据大小。

这里其实类似于生产者消费者模型,发送方就相当于生产者,接收方就相当于消费者,而接收缓冲区就相当于一个阻塞队列(Blocking Queue)。

在上图中,主机A第一次向B发送一个数据包,主机B在接收到数据包后,会将B缓存区中剩余的空间3000通过ACK数据包发送给主机A。当主机A接收到ACK数据包后,就能知道主机B的接收缓冲区还可以容纳多大的数据,那么A就会将窗口大小设置为3000。

当B接收到主机A发送的数据包后,由于前面的数据还没有处理完,就会将这次主机A传过来的数据包存放到B的接收缓存区中,此时接收缓冲区的大小就为0,B会将此时缓冲区的大小通过ACK数据包发送给主机A;当主机A接收到ACK数据包后,就能知道此时接收缓冲区为0,主机A就进入TIME_WAIT状态,不再传输数据包,而是等待主机B处理完一些数据,等接收缓冲区中还可以容纳数据后,等主机B发过来ACK数据包来通知窗口更新。

如果超过了等待时长,还没有接收到主机B发送的窗口更新通知的ACK数据包,那么主机A就会发送一个窗口探测包,探测包主要是为了让主机B返回一个携带缓冲区大小的ACK数据包,如果返回的ACK数据包显示接收缓冲区还有空间,那么主机A就会以该剩余空间大小作为窗口的大小发送数据报给主机B,若返回的ACK数据包显示接收缓冲区剩余空间为0,那么主机A就继续等待。

主机B剩余的接收缓冲区的大小会记录在TCP报头的 16位窗口大小 中~(只有在ack为1时才能表示剩余的接收缓冲区大小)

16位比特位,那么这个窗口大小能表示最大就是64kb吗?

其实不是的, 在选项中,我们可以设置一个特殊的选项"窗口扩展因子",来表示窗口大小*2的多少次方。

流量控制是通过反馈接收方的接收能力来制约发送方的发送能力,但是在网络传输的过程中,我们不仅要考虑发送方发送能力和接收方的接收能力,而且还需要考虑在传输链路上所有可能经过的节点的接收能力。那么我们就需要使用拥塞机制来处理传输路径中间节点的接收能力。

6.拥塞控制

拥塞控制机制是确保网络资源合理分配的关键技术之一。目的是为了防止网络中的数据传输量远超于网络的处理能力,从而避免或者减少网络拥塞现象

TCP引入了慢启动 机制,先发少量的数据,探探路,摸清当前的网络拥堵状态,再决定多大的速度传输数据。

  • 慢启动 刚开始发送数据,拥塞窗口会很小,用一个很小的速度来发送数据
  • 不丢包,增大窗口大小(指数增大)
  • 增长到一定长度后,达到某个指定的阈值,此时即使没有丢包,也会停止指数增长,变长线性增长
  • 线性增长,也会让发送速度越来越快,达到某个情况下,就会出现丢包
  • 如果出现丢包,那么就会回到一个新的sstresh值,继续进行线性增长,形成动态平衡

拥塞控制是类似一个不断试错的过程,如果此时窗口大小不会发生丢包的现象,那么下次传输数据的窗口就会越来越大;如果此时窗口大小在传输数据的时候发生丢,那么下次传输数据的时候窗口就会缩小。

总的来说:

流量控制是站在接收方的视角来限制发送方的传输速度的;

拥塞控制是站在传输链路的视角来限制发送方的传输速度的。

7.延迟应答

延迟应答机制是提高网络传输效率的一种策略,延迟应答机制允许接收方不用立即发送应答,而是等一段时间后再发送确认应答(ACK),从而减少在网络中传输的ACK数据包的数量,从而来节约带宽,提高网络传输速率。

简单地来说,主机B在接收到主机A发送的数据包后,一般会立即给发送方返回一个ACK数据包,在ACK数据包中来告诉发送方我的接收缓冲区还能够容纳多大的数据。如果立即返回的话,可能此时接收端处理掉接收缓存区的数据可能比较少,此时若发送ACK数据包给发送方,那么发送方的窗口可能也比较小。但是如果我们让ACK晚一点发送给发送方,那么主机B此时能够处理的数据就越多了,从而让接收缓冲区可容纳空间变大,此时返回ACK数据包给主机A中,那么下次主机A能够发送数据的窗口也就可以更大一些,从而提高传输效率。

通过延迟应答,应用程序能够更多的数据,使得接收缓冲区中的剩余空间更大,从而让发送数据的应用程序能够获取到一个更大的窗口。

当然,也不是所有的数据包都能够延迟应答。

  • 数量限制:每隔N个包就应答一次;
  • 时间限制:超过最大延迟时间就应答一次。

具体的数量和超时时间,依操作系统不同也由于差异;一般N取2,超时时间取200ms;

8.捎带应答

TCP的捎带应答是建立在延迟应答之上的,提高传输效率的机制。当发送端发送请求给接收端后,接收端的内核不会立即返回ACK(确认应答报文)给发送端,而是会等一段时间,等接收端处理完数据并计算出响应,将响应和ACK合并为一个数据包发送给发送端。

原本的数据的传输过程

使用捎带应答传输数据

正常来说,ack和响应是不同的时机的,无法合并,但是由于捎带应答是建立在延迟应答之上的,延迟应答就会让ACK返回的时间往后延迟,这样一来,发送响应的操作就可能可以赶的上,就可以合并为一个数据包发送。

9.基于字节流

在前面,我们已经知道了TCP是基于字节流传输的,既然是基于字节流传输的,那么每次可以传输数据的字节数都是可以变化的。

当发送方给接收方发送多个数据报后,由于另一方可能在处理在处理数据,此时接收到的数据就来不及处理,就会存放到接收缓存区中,但由于TCP是面向字节流传输的,接收方(站着应用层角度)并不能区分出从哪里开始到哪里结束,是一个完整的应用层数据包,这就是所谓的"粘包"问题。

那么我们如何解决"粘包问题"呢?

  • 对于变长的包,可以指定分隔符,我们可以在包和包之间使用明确的分隔符(应用层协议,不与正文冲突即可)
  • 对于变长的包,可以在包头位置,约定一个包总长度的字段,从而知道了包的结束位置
  • 对于定长的包,保证每次都按固定大小读取即可。

思考:对于UDP协议,是否也存在着"粘包问题"呢?

  • 对于UDP来说,如果还没有上层交付数据,UDP的报文长度仍然在。同时,UDP是一个一个把数据交付给应用层,就有很明确的数据边界。
  • 站在应用层的角度,使用UDP的时候,要么收到完整的UDP报文,要么不受,不会出现"半个"的情况。

10.异常情况的处理

  • 进程崩溃 。当进程异常崩溃的时候/正常结束时,此时操作系统都能够回收释放对应的PCB(进程控制模块),TCP的文件描述符就会自动释放,x相当于调用了close()方法。此时崩溃的进程上的通信方就会触发发送FIN,当对方接收到FIN的时候,就会返回ACK+FIN,然后崩溃的进程上的通信方就会再发送一个ACK,也就是正常的四次挥手。(TCP连接是独立于进程之外的
  • 主机关机(正常流程的关机)。对于这种正常流程的关机,操作系统会先尝试强制结束所有的用户进程,然后再进入关机流程。在强制结束所有进程的时候,也是会进行1的操作。但是这里有一些地方与1情况不同,如果在系统关闭之前通信端接收到ACK+FIN,并返回了ACK,那么就执行了完整的四次挥手;但如果关机主机上的通信端在接受到ACK+FIN之前就关机了,那么这个端就不会返回ACK,而另一端在等一段时间后就会触发超时重传几次都没有接收到ACK的话,就会向该端发送一个"复位/报文段",这个"复位/报文段"的RST标志位设为1,如果这个段也没有收到回应的话,就自动放弃这个连接。
  • 主机掉电。对于这种情况,主机是来不及结束进程,也来不及发送FIN数据包的,主机就直接停机。
  1. 如果是发送方A掉电 ,那么接收方B此时就不知道A什么时候给我发数据,但是由于TCP提供了心跳包这种机制,接收方B会周期性的给发送方发送一个心跳包,如果心跳包收到了回应,说明对端存活着;如果对端没有回应并且发送了几次都没有回应,那么接收方B就会放弃这个连接。
  2. 如果是接收端B掉电,那么发送方A就会等待接收方发送的ACK数据包,如果一段时间之后还没有接收到ACK的话就会触发超时重传,当重传几次后还是没有接收到ACK的话,发送方A就会触发重置连接,给接收方发送一个"复位报文段",如果还是没有接收到回应的话,发送方A就会放弃这个连接。
  • 网线断开。与主机掉电类似,如果发送方在传输数据的时候网络断开,那么就会触发超时重传->重置连接RST->单方面断开;如果是接收方,就会给发送方发送心跳包->重置连接->单方面释放连接。

TCP心跳机制

TCP心跳机制是一种用于检测TCP连接是否正常的机制。通过周期性地发送心跳包来表明发送方仍然存在包的内容一般没有什么特别的规定,都是很小的包,或者只包含包头的一个空包。心跳包主要也就是用于长连接的保活和断线处理。

TCP心跳机制有两种实现方案:

  • 客户端发送心跳包:客户端定时向服务器发送心跳包,以告知服务器自己仍然在线。服务器在收到心跳包后,会回复一个确认包给客户端。如果客户端在一定时间内没有收到服务器的确认包,则认为连接已断,需要采取相应的措施,如重新连接或关闭连接。

  • 服务器发送心跳包:服务器也可以定时向客户端发送心跳包,以检测客户端是否在线。客户端在收到心跳包后,会回复一个确认包给服务器。如果服务器在一定时间内没有收到客户端的确认包,则认为客户端已断线,需要采取相应的措施,如关闭连接或通知客户端重新连接。

小结

以上就是TCP10个机制的内容讲解咯,是不是很复杂呢?

主要是为了保证可靠性,同时又尽可能的提高性能!

可靠性

  • 校验和
  • 序列号
  • 确认应答
  • 超时重发
  • 连接管理
  • 流量控制
  • 拥塞控制

提高性能:

  • 滑动窗口
  • 快速重传
  • 延迟应答
  • 捎带应答

面试题:如何使用UDP实现可靠传输?

  1. 引入序列号,保证数据顺序;
  2. 引入确认应答,确保对端收到了数据
  3. 引入超时重传,如果隔一段时间没有应答,就重发数据。
  4. 还可以引入滑动窗口、流量控制、拥塞控制等...

以上就是本篇所有内容,若有不足,欢迎指正~

相关推荐
zquwei12 分钟前
SpringCloudGateway+Nacos注册与转发Netty+WebSocket
java·网络·分布式·后端·websocket·网络协议·spring
Aimin202226 分钟前
路由器做WPAD、VPN、透明代理中之间一个
网络
群联云防护小杜1 小时前
如何给负载均衡平台做好安全防御
运维·服务器·网络·网络协议·安全·负载均衡
ihengshuai1 小时前
HTTP协议及安全防范
网络协议·安全·http
爱码小白1 小时前
网络编程(王铭东老师)笔记
服务器·网络·笔记
蜜獾云1 小时前
linux firewalld 命令详解
linux·运维·服务器·网络·windows·网络安全·firewalld
柒烨带你飞2 小时前
路由器转发数据报的封装过程
网络·智能路由器
东方隐侠安全团队-千里2 小时前
网安瞭望台第17期:Rockstar 2FA 故障催生 FlowerStorm 钓鱼即服务扩张现象剖析
网络·chrome·web安全
MuLogin_Browser3 小时前
如何保障多个Facebook账号稳定运行:一账号一稳定IP?
服务器·tcp/ip·facebook
神一样的老师3 小时前
面向高精度网络的时间同步安全管理架构
网络·安全·架构