【计算机网络原理】对传输层TCP协议的重点知识的总结

embedded/2024/9/24 0:22:10/

˃͈꒵˂͈꒱ write in front ꒰˃͈꒵˂͈꒱
ʕ̯•͡˔•̯᷅ʔ大家好,我是xiaoxie.希望你看完之后,有不足之处请多多谅解,让我们一起共同进步૮₍❀ᴗ͈ . ᴗ͈ აxiaoxieʕ̯•͡˔•̯᷅ʔ—CSDN博客
本文由xiaoxieʕ̯•͡˔•̯᷅ʔ 原创 CSDN 如需转载还请通知˶⍤⃝˶​
个人主页:xiaoxieʕ̯•͡˔•̯᷅ʔ—CSDN博客

系列专栏:xiaoxie的网络>计算机网络学习系列专栏——CSDN博客●'ᴗ'σσணღ

"探索未来,掌握人工智能"🚀 点击加入我们的AI学习之旅,让技术变得有趣又易懂,点击跳转
我的目标:"团团等我💪( ◡̀_◡́ ҂)" 

( ⸝⸝⸝›ᴥ‹⸝⸝⸝ )欢迎各位→点赞👍 + 收藏⭐️ + 留言📝​+关注(互三必回)!

目录

​编辑​ 一.TCP协议

1.TCP协议的特点

2.TCP协议的格式

1.简单介绍各个字段

3.TCP的十个重要的机制

1.确认应答

2.超时重传

1.传输的数据出现丢包

2.ACK丢包

3.超时重传的超时时间如何设定

 4.TCP的可靠性是如何保证的(面试题)

3.连接管理

1.三次握手

1.三次握手的意义是什么,解决了啥问题

2.TCP为啥要三次握手,两次可以吗,四次可以吗

3.TCP三次握手的状态和Socket api

2.四次挥手

1. 四次挥手是否可以像三次握手一样,合并成三次挥手呢?

2.TCP四次挥手的状态(重要)

 3.TCP状态转换的详细图片

4.滑动窗口

1. 滑动窗口出现丢包的情况:

情况一:数据包抵达了,ACK出现了丢包

情况二:数据包出现了丢包

​编辑

5.流量控制

6.拥塞控制

1.拥塞控制,拥塞窗口, 大小动态变化,具体是咋变的? 是否有规律??

7.延时应答

 8.捎带应答

9.粘包问题

10.异常情况

 1.其中某一个进程崩溃

2.某个主机被关机(正常关机)

3.某个主机电源断电(非正常关机)

4.网线断开

4.如何使用UDP实现可靠性传输(面试题)

​编辑


​ 一.TCP协议

1.TCP协议的特点

1.有连接

在数据传输开始之前,TCP需要建立一个连接,这通常通过三次握手来完成。一旦连接建立,数据就可以在两个端点之间双向传输,直到连接被关闭。

2.可靠传输

TCP通过使用序列号、确认应答、数据重传以及窗口机制等技术,确保数据的可靠传输。如果数据包在传输过程中丢失或损坏,TCP会重新发送丢失或损坏的数据包,直到接收端正确接收到所有数据。

3.面向字节流

TCP不像UDP那样传输数据报,而是将数据视为连续的字节流。这意味着TCP不保留数据包边界,它负责将整个数据流从一个端点传输到另一个端点。

4.全双工

TCP允许通信双方同时发送和接收数据,即数据传输是双向的,并且双方可以独立控制数据的发送和接收速率。

2.TCP协议的格式

这里只是简单的介绍一下 ,每一个字段表示的是什么意思,后续会结合具体的TCP机制和字段一起详细的介绍

1.简单介绍各个字段

  1. 源端口号(Source Port)16位,用于标识发送方的端口号。端口号用于区分同一IP地址上的不同服务或应用程序。

  2. 目的端口号(Destination Port)16位,用于标识接收方的端口号。端口号同样用于区分不同服务或应用程序。

  3. 序列号(Sequence Number)32位,用于标识从发送方发送的数据字节的顺序。序列号用于确保数据的有序传输,并允许接收方检测丢包。

  4. 确认号(Acknowledgment Number)32位,用于期望从接收方接收的下一个字节的序列号。这是发送方期望接收的下一个字节,用于确认已成功接收数据。

  5. 数据偏移(Data Offset)4位,实际上表示头部中数据起始处距离报文段开始处的字节数。因为TCP头部最小长度是20字节,最大可以达到60字节(如果包含所有选项)。这个字段也被称为“头部长度”字段。

  6. 保留(Reserved)6位,目前未使用,必须设置为0。

  7. 标志位(Flags)6位,用于控制TCP的行为,具体包括:

    • URG(紧急指针有效 Urgent Pointer):当设置时,表明紧急指针字段有效,指示应优先处理报文段中的紧急数据。
    • ACK(确认号有效 Acknowledgment):当设置时,表明确认号字段包含有效的确认信息。
    • PSH(推 Push):当设置时,指示接收方应尽快将数据推送给应用程序,而不是缓冲。
    • RST(重置 Reset):当设置时,表明需要立即重置连接。
    • SYN(同步序列编号 Synchronize Sequence Numbers):当设置时,用于建立连接时的握手阶段,同步双方的序列号。
    • FIN(结束 Finish):当设置时,表明发送方没有更多的数据要发送,请求释放连接。
  8. 窗口大小(Window Size)16位,用于流量控制,指示接收方可以接收的字节数。窗口大小随时间变化,取决于接收方的缓冲区容量。

  9. 校验和(Checksum)16位,用于错误检测。包含整个TCP报文段,包括头部和数据,但不包括数据链路层的头部。

  10. 紧急指针(Urgent Pointer)16位,仅当URG标志位被设置时使用,指示紧急数据的结束位置。

  11. 选项(Options):长度可变,用于各种用途,如:

    • 最大报文段大小(MSS - Maximum Segment Size):通常在连接建立时设置,指示发送方可以接收的最大TCP报文段大小。
    • 窗口缩放(Window Scale):允许使用更大的窗口大小,超过16位可以表示的范围。
    • 选择性确认(Selective Acknowledgment, SACK):允许接收方指示哪些数据段已经成功接收,有助于更有效地处理丢包。
  12. 填充(Padding):确保整个头部是32位的倍数,如果必要的话。

  13. 数据(Data):实际传输的数据负载。数据字段的大小取决于头部的长度和MSS。

TCP头部的设计旨在提供一种可靠、有序、全双工的数据传输方式,同时允许流量控制和拥塞控制,以适应不同的网络条件。

3.TCP的十个重要的机制

1.确认应答

对于TCP协议来说,最重要的就是可靠性问题,注意这里的可靠性并不是说使用TCP传输数据,数据100%可以从发送方传输到接收方,而是TCP尽最大的努力使数据从接收方传输到接收方.这其中最重要的就是让发送方知道,接收方是否收到数据.所以当发生方发生一个数据之后,接收方就会回复一个应答报文,表示自己接收到了信息.

那么如何知道这个报文是应答报文呢? 是通过TCP协议格式里的6位标志位来判断该报文为应答报文的

 而应答 - > acknowledge - > ACK,即ACK这个标志位为1,即可用来表示该报文为应答报文.并且该报文段是一个对之前接收到的数据的应答。接收方使用这个应答来告知发送方它已经成功接收了特定的数据,并准备好接收接下来的数据。

上述单纯的应答,如果是批量发送数据的时候,就会出现问题,例如下图这个情况:

可以看到接收方的ACK并没有按照顺序到达发送方,而是出现了后发先至的效果,这种情况在网络通信中是不可避免的,那我们该如何区分那个ACK是对应的发送方发送的数据的应答了,这个时候就需要对传输的数据进行编号,并且使应答报文的编号和发送的数据的编号对应起来,这个时候就需要用到TCP报头格式的32位序号和32位确认序号

 TCP序号是按照字节来编号的,并且每个字节都有编号,例如第一个字节序号为1,第二个字节序号为2,字节的编号是连续递增的,所以我们只要知道第一个字节的序号,剩下的字节的序号就明白了.注意序号不一定是由0或者1开始的,而是根据接收和发送双方协商好的,(这个后续解释)例:

如果 一个TCP数据报的载荷为1000字节那么它的序号就为1(这里只是为了简单就设为1,真实情况可能为别的数,但是其连续递增的性质不变);

它的确认序号就非常有意思了,它的取值就为要应答的数据的最后一个字节在 + 1;

也就是如果以上图这个TCP数据报举例,那么它的应答报文的确认序号就为1001,即:

其中的确认序号可以从两个方面来理解:

1.对于接收方来说, < 1001 的数据已经确认收到了

2.接收方在向发送方索要从1001开始的数据.

这个序号和确认序号设计的非常精妙不仅完美的解决了网络传输先发后至的问题,还未后续的机制带来了很多遍历.

2.超时重传

TCP中最核心的就是可靠性传输,而可靠性传输主要就是依靠确认应答,但是如果出现,ACK或者数据在网络传输的过程出现丢包的情况,这个是无法避免的,是客观存在的,这个时候TCP就是使用超时重传这个策略来应对这个情况的,可以说,超时重传时确认应答的重要补充,TCP之所以可以可靠性传输,全靠,确认应答和传输重传这两个机制.

网络传输的过程中,有可能是传输的数据出现丢包的情况,也有可能是ACK应答报文出现丢包的情况.不同的情况,自然TCP解决的方式就不一样了.

1.传输的数据出现丢包

正常情况下,TCP是依靠确认应答来确定接收方是否接收到发送方传输的数据.如果出现下图的情况,即发送方发送的数据在网络传输的过程中,出现丢包的情况,接收方没有收到数据自然也就不会发送ACK应答报文了

而发送方就可以根据是否收到ACK,来判断是否出现丢包的情况.正常情况下,发送方发送数据到接收方,到接收方发送ACK给发送方,在网络传输可能会经历一段时间的,但如果超过规定的超时时间的一个阈值(超时),发送方就会认为数据丢包了,就会重传数据给接收方,注意:哪怕数据并没有出现丢包,只是传输时间过长即超过阈值(这个是有可能发生的),也会被认为数据发生丢包.这个就是超时重传机制.至于接收方会收到两份相同的数据,不用担心,接收方会有相应的策略进行数据的去重(后续介绍).

2.ACK丢包

传输的数据出现丢包,ACK自然也可能出现丢包的情况.如下图所示:

 站在A的视角之中,它无法判断是数据发生了丢包,还是ACK发生了丢包的情况,只要超过一定的时间之内,它就会重传数据.这个时候B就会收到两份相同的数据了,发生这种情况如果B没有相应的策略的话,就会造成很严重的后果,就比如,你转账时,触发了超时重传机制就会发生重复转账的操作,所以TCP为了防止这个情况的发生,就设计了一个策略,通过序号对数据进行去重,那么是如何实现这个策略的呢;

在接收方操作系统的内核中,有一个数据结构 ->接收缓冲区 - > 优先级阻塞队列;

B(接收方)在收到数据的时候层层分用,到了传输层的时候,就会把传输的数据先存储到这个阻塞队列中,再从阻塞队列中以此的读取数据.在放的过程中是阻塞队列就会根据当前数据的序号来判断当前这个数据是否存在过,如果存在就丢弃,不存在才放到队列中.那么阻塞队列是如何判断数据是否存在过的呢,这个时候就要根据之前提到过的数据的序号是连续递增的,如果当前数据出现和在队首的数据的序号相同或者小于的情况就说明,当前数据曾经存在或者现在就在队列中,就会把这个数据给丢弃掉.从而达到去重的效果.

由于队首元素为1000,这个时候有个序号为500的数据想要加入队列中,就会被丢弃,因为队首元素的序号为1000就说明序号为500的数据曾经出现过,就不能重复传输了.

3.超时重传的超时时间如何设定

首先需要明确,这个时间并不是固定的,而是动态变化的,也就是说,假设第一次的超时时间为 50ms(这个时间是操作系统规定,我这里是随意写的),等待50ms后没有收到数据,触发超时重传,第二次的超时时间就为100ms如果还是没有收到,就继续重传,这个时候超时时间就变成了150ms.当然这个时间并不一定是这样增长的,具体的增长是由操作系统决定,总之,这个超时时间它是会动态增长的.而不是一直是固定某个数值,也不会缩短(毕竟发生多次丢包,后续成功的可能性也就越来越低了,缩短超时时间反而浪费系统资源).

注意:如果网络出现故障,重传多次后,依然不成功,达到一定的阈值之后,就会尝试重置连接即触发一个"复位报文"重置连接即6位标志位其中一位的"RST"这个时候就会把之前TCP连接传输的中间状态给清空,例如接收缓冲区的数据都会清空.重新开始传输.这个时候如果还是无法传输的话,就会断开连接

 4.TCP的可靠性是如何保证的(面试题)

这个时候就需要回答也只能回答依靠 确认应答机制和传输重传机制不能多也不能少然后在回答具体的机制的内容即可.这里就不过多的赘述了.

3.连接管理

1.三次握手

TCP建立起连接就是,通过实现三次握手实现的,三次握手即,通信的双方通过三次网络交互,互相保存对方的信息,即为建立起了连接.

注意:第一次发起连接的 一定是客户端,也就是说谁发起的谁就是客户端,假如是服务端发起的,它就不是服务器了,而是客户端了,同时这个在三次握手期间,发送的数据报是不携带任何业务数据的,也就是该数据的载荷部分是空的,只有TCP报头,如果是同步报文,它的六位标志位其中的一位"SYN"为1.

 同时在上述建立连接的过程中,其实就是客户端和服务端,互相给对方发送SYN,再各自给对方发送ACK,其实是四次交互,那为什么是三次握手呢,主要是因为在建立连接的过程中,传输的数据都没有载荷,就可以把,中间过程的服务端向客户端发送,ACK和SYN就可以合并,即在服务端给客户端发送数据的TCP报头的标志位中的"ACK"和 "SYN"的bit为1即可,实现合并操作就可以减少消耗,提高性能.

 这里解释一下双方为什么要向对方发送SYN:

首先我们要知道建立连接就是,通信双方要互相保存对方的信息,也就是客户端发送SYN给服务端,服务端收到SYN就保存了客服端的信息,并且给服务端发送ACK和 SYN,客服端接收到了ACK,就表明客服端的发送数据的能力没问题,服务端的接收能力没问题,并且客户端接收到了服务端的SYN后,也就保存了服务端的信息,就返回ACK,服务端接收到ACK后就说明服务端的发送数据的能力没问题,客户端接收数据的能力没问题,这样通信双方都保存了对方的信息,并且双方的接收能力和发送的能力没问题,就说明连接成功了.

1.三次握手的意义是什么,解决了啥问题

1.三次握手就是在正式传输数据之前,确认通信线路是否通畅,确保可以建立一个稳定的连接,相当于TCP可靠性传输的一个辅助机制,注意:如果如果在面试中被问到TCP如何保证可靠性传输,不可以回答三次握手,而是回答,确认应答和超时重传即可.

2.通过三次握手来确认通信双方,发送能力和接收能力都是正常的,这一点就不过多的解释了.

3.三次握手的过程中还需要协商一些必要的参数,这里重点介绍一下,TCP通信时使用的序号就是在三次握手的过程中出来的,也就是说序号一般并不是从 0 /1 开始的,而是在通信双方协商出来的,至于时如何协商的,协商出来的序号到底都是啥,比较复杂,再加上也不是重点,博主就不过多的解释了,这里的重点是要明白,每一次建立连接协商的序号的差异往往非常大的,至于`为什么要这样设计,这里重点解释一下:

就是在通信的过程中,通信双方先建立连接,进行数据交互,之后断开连接,过了一会,又建立了连接,进行数据交互,假如在这个过程中,某个数据报在网络传输的过程中花费的时间比较长  -> 等它传输到服务端的时候,这个服务端已经是断开连接后,又重新建立一个新的连接了 - > 这个时候服务端接收到这个数据是该丢弃还是按照正常的逻辑执行呢 - > 自然是应该丢弃,因为客户端和服务端断开连接后,又重新建立连接,重新建立连接的服务端不一定是之前的服务进程了,即执行的业务就不相同了,自然就得丢弃.  - > 如何判断这个数据报是之前的连接的数据报呢,自然是通过序号咯,如果是同一个连那么序号的差别就不大,不是一个连接序号的差别就非常大,这个时候就可以区分出来该数据报是否为前一个连接的数据报,这也就是为什么每一次连接协商的数据报的序号的起始差别要非常大的原因了.

通过序列号的机制,TCP协议可以防止旧的数据包干扰到新的连接,确保数据传输的准确性和可靠性。这确保了即使在网络条件不稳定、连接频繁断开和重新建立的情况下,数据传输也能够保持有序和正确。

2.TCP为啥要三次握手,两次可以吗,四次可以吗

两次不可以:服务端无法确保它的发送数据的能力和客户端的接收能力,无法确保双向通信的可靠性四次可以但不建议:如果把"ACK" 和 "SYN"改成分两次发送是可以实现,但是这样并不会提高传输数据的准确性,反而降低了性能,例如:网络交互增加,消耗的网络资源增加,还增加了丢包的可能性等等减低性能的缺点.

3.TCP三次握手的状态和Socket api

Socket api 就不过多的解释了,主要介绍一下三次握手过程中客户端和服务端的状态, 

listen: 只有服务端才会存在的状态,也就是在服务端绑定端口成功后,就会进入 Listen 状态,即为 监听状态,表示随时都会有客户端连接上来.

Established: 表明连接建立完成.

至于其他的状态就不是很重要了.

2.四次挥手

断开连接即通信的双方把对方的信息给删除掉,即四次挥手.同时注意三次握手,一定是客户端发起第一次的,而四次挥手,通信双方都可以都可以主动发起,这里就以客户端主动发起为例:

在四次挥手过程中传输的数据也是一样不携带任何业务数据,即TCP数据报的载荷为空,只有TCP报头,其中的FIN结束报文,就是6位标志位的FIN位的bit位为1.

1. 四次挥手是否可以像三次握手一样,合并成三次挥手呢?

答案是如能--- 在特殊情况下可以,一般情况下不能 为什么呢?

三次握手的ACK和 SYN的发送都是由系统内核控制的自动发送,也就是说他们的发送的时间是一至,所以就可以合并成一次`发送 ACK + SYN.

而四次挥手的ACK是由系统内核控制的,但是服务端的FIN(结束报文)却是由应用程序代码控制的,也就是说只有当代码调用 close 的时候才会发送FIN(结束报文),通常在应用程序完成数据传输并调用close()函数时触发,

所以这两个数据报的触发时间是不一样的,所以难以合并,至于什么时候可以合并,

那就是当,TCP触发延时应答机制的时候(要回复ACK,但是不是立即发送,而是稍等一会)这个情况就可以合并.

速关闭:如果客户端和服务器几乎同时决定关闭连接,并且他们的FIN包在网络中相遇,那么接收方可以在回复对方的FIN时同时发送ACK和自己的FIN,这样就减少了一次握手。

但毕竟是上述都是特殊情况,所以我们一般认为它就是进行四次挥手.

2.TCP四次挥手的状态(重要)

1.Close_Wait 

就是被动的一方进入的状态,等待代码调用 close()方法,代码调用close()越及时,就越不可能看到,假如我们在服务器中看到大量的 Close_Wait 状态的时候 => 代码可能忘调用close, 或者是 close 调用的不够及时.

2.Time_Wait

Time_Wait状态是TCP连接终止过程中的一个正常阶段,它发生在主动关闭连接的一方(通常是客户端)在发送完FIN包并接收到对方的FIN包后,进入的一个等待状态,它主要是为了应对最后一个ACK丢包的情况.同时需要注意 Close_Wait 并不是一定为服务端的状态,而是被动一方的状态,Time_Wait也不一定是客户端的状态,而是主动一方的状态

也就是当客户端在收到服务端返回的FIN以及发送ACK的时候并不会立即释放连接,就是为了服务端没有收到客户端的ACK(丢包)的情况,触发重传FIN,要是释放了连接后,客户端就无法返回ACK了,当然Time_Wait的持续时间是有限的,也就是说,客户端在等待一般是 2 msL 之后这个时间足够长,以确保所有可能的重传数据包都被处理完毕就不在等待而是释放连接了,默认服务端接收到了ACK了.当然也是有可能发送ACK又丢包的情况的,但是只要超过了限定的时间客户端直接就断开连接.

1.如果服务端出现大量的Time_Wait时该如何处理

服务端出现大量的Time_Wait就说明服务端在大量的主动断开TCP连接,这显然是不科学的,

代码审查:检查应用程序代码,特别是与网络通信相关的部分。

短连接模式:如果应用程序频繁地创建和关闭连接,而不是重用连接,这将导致大量的TIME_WAIT

网络问题:网络波动或不稳定可能导致连接意外中断,从而产生大量的TIME_WAIT

 3.TCP状态转换的详细图片

注意掌握博主之前提及的 四种状态即可,其余的状态遇到了,看一下这个图即可.

4.滑动窗口

TCP除了保证可靠性传输之外,也希望能够尽可能高效的完成数据传输,其中滑动窗口就是一种,TCP用来提高传输效率的机制.

不引入滑动窗口,数据传输的过程

对每⼀个发送的数据段, 都要给⼀个ACK确认应答. 收到ACK后再发送下一个数据报. 这样做有一个比较大的缺点, 就是性能较差. 尤其是数据往返的时间较长的时候.
引入滑动窗口,数据传输的过程

引入滑动窗口后,就把一条一条发送数据 = > 批量发送数据,批量发送就把等待时间重叠了,就可以提高性能.同时注意虽然是批量发送,但还是需要等待ACK否则无法保证可靠性.

窗口大小:不等待ACK,批量发送数据的多少,就是窗口大小,这个值不是固定的,它是通过其他的机制,确定的,这个下文博主会介绍到.

上图就表示,窗口大小为4,批量发送1001 - 5001这四组数据,同时等待这四组数据的ACK,此时如果收到 2001ACK,就说明1001 - 2000 这个数据就得到了确认,就标记为灰色了,此时就需要继续等待2001-5001的ACK,于此同时,发送新的数据5001- 6001,依旧是四组数据.也就是说批量发送四组数据并不是要等到接收四组ACK后再继续批量发送,而是收到一个ACK,就往后发一个新的数据,这个窗口就滑动起来了.

同时注意如果 收到的是3001的ACK,就说明 < 3001 的数据已经接收到了,窗口就会往后滑动两个"格子".

1. 滑动窗口出现丢包的情况:

要保证可靠性

情况一:数据包抵达了,ACK出现了丢包

假如是 1001 这个ACK丢包了,这个时候并不需要做任何的处理,因为,即使1001 这个 ACK丢了,主机A在收到2001这个ACK的时候,就说明1-1000 , 1001 - 2000 的数据都收到了,就涵盖了1001这个ACK起到的效果,所以只要数据包抵达了,ACK出现丢包的情况,并不需要做任何处理,也能保证可靠性.

情况二:数据包出现了丢包

 假如是1001 - 2000 这个数据包出现了丢包的情况,主机A依然会继续向主机B往后发送数据,主机B   此时向主机A发3次重复的1001 ACK向主机A索要1001 - 2000 的数据包,如果主机A收到了3个同样的ACK的话,就会进行重发操作,注意这里只重发 1001 - 2000 这个数据包,别的数据包并不会重发,当主机A收到了7001这个ACK后,就说明 1 - 6000 这些数据包都收到了,

上述这两个丢包情况,就是通过快速重传这个机制实现的,整个过程是十分高效的那么,快速重传和 超时重传是否存在冲突呢.

答案当然是不会咯,快速重传就相当于超时重传的在滑动窗口下的一个变种机制,本质上是一样的.并且在传输的数据不多的时候是不会触发滑动窗口机制的.这个时候发送丢包就需要用到超时重传了.

注意:哪怕滑动窗口在牛逼,TCP在效率方面还是UDP更牛一些.

5.流量控制

滑动窗口中最关键的就是窗口大小,其中窗口大小是可变的,可以通过控制窗口大小从而控制发送方的发送速度,即窗口越大,发送的数据也就越多,效率也就越高,但是如果窗口过大,接收方处理不过来,就会导致丢包的情况,从而影响到可靠性,所以呢,应该要接收方要根据自己的处理能力,然后反制约于发送方,使双方到达一个平衡.这就是流量控制.

给如何,衡量接收方的处理速度呢?

主要使根据接收方的接收缓冲区来衡量的

接收方的窗口大小就为未使用空间的大小.每次接收方接收到数据后,返回的ACK的报头中,就包含了16位窗口大小,告知,接收方的数据处理能力,从而控制发送方的窗口大小

 注意16位窗口大小并不意外着,窗口最大为64kb而是更大,因为窗口大小并不仅仅和16位窗口大小有关,还和选项的窗口扩展因子有关,这里就不展开说明如何计算了,只需要记住窗口大小是可以非常大的.

发送方接收到这个ACK后就根据上述接收方指定的窗口大小来发送数据.

 当接收方的接收缓冲区满了之后,接收方就会告诉发送方,停止发送数据,等过了一段数据后,接收方消费了一些数据后,接收缓冲区又有空闲空间了,但是发送方停止发送数据了,接收方自然就无法发送ACK告诉发送方了.那么该如何解决呢?

这个时候一旦发送方过了重发超时的时间,还没有接收到接收方的ACK,就会向接收方发送一个不带载荷的窗口探测包,询问窗口大小,只要没收到ACK或者没收到窗口大小更新的通知,就会定时的发送.这个还是非常之精妙的.

6.拥塞控制

和流量控制一样,拥塞控制也是用来和滑动窗口搭配的机制,也就是说拥塞控制也是控制窗口大小的.

流量控制是根据接收方的接受能力来反向制约发送方从而控制窗口大小.

拥塞控制考虑的则是考虑到发送方和接收方中间网络通信节点的像设备,路径等等之类的因素,然后把它们看做一个整体,通过实验的方式,来确定窗口大小

动态的找到一个速度快并且可靠性高的窗口大小.

注意:流量控制可以控制窗口大小\,拥塞控制也可以控制窗口大小,那么具体的窗口大小到底是根据那个机制呢 => 那个机制确定的窗口小,就根据那个机制

1.拥塞控制,拥塞窗口, 大小动态变化,具体是咋变的? 是否有规律??

1.慢开始:刚开始以,比较小的窗口 来传输数据
2.按照指数方式扩大窗口(*2)
注意这里的“慢”说的是刚开始 窗口大小比较小,传输速度慢而不是窗口大小的变化速度慢 (指数增长非常快的)
3.指数增长过程中,达到某个阃值,就要变成线性增长.(+n)
线性增长,也是增长,发送速度越来越快增长到一定程度,就会出现丢包.当发送端检测到丢包时(超时或者3个重复ACK)就开始缩小窗口(快速重传)

缩小有两种方式:
1.直接缩到底.(回到了最初慢启动的时候)接下来指数增长-线性增长
现在已经废弃了.(之前TCP的做法).

2.快速恢复:缩到出现丢包时窗口大小 一半 这样的位置接下来线性增长.(目前使用的方法).
 

注意这里只是大体介绍了一下过程,具体过程的算法及公式什么的还是比较复杂的,这里就不过多的解释了,感兴趣的可以去查一下资料.

7.延时应答

ACK不会立即返回,而是延时一会再返回.TCP四次挥手在特殊情况下就是因为这个机制就可以变成三次挥手.(简单提一下前面的知识).

为什么要延时应答呢,自然是为了提升效率,而TCP提升效率主要就是增加窗口大小,延时应答就是在保证可靠性的前提下,提高窗口大小.

举个例子

假设当前接收缓冲区的空闲空间为5kb,当接收到发送方的2kb数据后,空闲空间就变成3kb,这个时候就要根据空闲空间来返回窗口大小.如果为我们是立即返回这个ACK的话,那么窗口大小就是3kb(不考虑窗口扩展因子),如果延时返回ACK,用于接收缓冲区里的数据是会被消费和读取的啊,我空闲空间就会变大,也就是返回的窗口大小是大于 3kb的(具体多少不过多计算了)从而提高了效率.

注意:这里的延时时间主要是根据一定时间和接收的数据量结合来规定的,具体如何计算不多说.
 

 8.捎带应答

建立在延时应答的基础上的一个提升效率的机制.

在我们日常开发中,客户端和服务端通常是"一问一答"这样的模型

因为延时应答机制,ACK会延时一会,在返回,如果这个时候服务端刚好执行完业务逻辑要发送响应的时候,这个响应数据就会把TCP报头的ACK标志位设为1,以及窗口大小等.就把这个ACK给合并了.这样两次传输,就变成一次传输,提高了效率.

9.粘包问题

TCP是一个面向字节流的协议,这意味着TCP不保证数据包的边界。在TCP连接上发送的数据是按字节序列传输的,而TCP本身并不为每个消息添加开始和结束的标记。因此,如果应用程序发送了多个数据包,它们可能在接收端被“粘”在一起,变成一个更大的数据包,这就是所谓的“粘包”问题。

粘包问题可能导致接收方无法正确地识别单个消息的边界,从而无法正确地处理接收到的数据。以下是一些可能导致粘包问题的情况:

  1. 发送多个小数据包:如果发送方连续发送多个小的数据包,它们可能在网络中的某个点被合并为一个较大的数据包。

  2. 接收缓冲区大小:如果接收方的接收缓冲区足够大,它可以一次性接收多个数据包,导致这些数据包被粘在一起。

  3. 网络延迟:由于网络延迟或拥塞,发送方发送的数据包可能在网络中被延迟,导致它们到达接收方时被合并。

  4. 应用程序处理速度:如果接收方处理数据的速度比发送方慢,它可能来不及处理接收到的数据包,导致数据包在接收缓冲区中累积。

为了解决粘包问题,可以采取以下几种策略:

  1. 固定长度消息:发送固定长度的消息,接收方可以根据消息长度来确定消息边界。

  2. 消息边界标记:在每个消息的末尾添加特殊的分隔符或标记,接收方可以通过查找这些标记来识别消息边界。

  3. 消息长度字段:在每个消息的开头添加一个表示消息长度的字段,接收方可以根据这个长度来确定消息边界。

  4. 使用应用层协议:使用应用层协议(如HTTP、FTP等)来定义消息结构,这些协议通常有自己的消息边界定义。

粘包问题不仅仅是在TCP中会出现,它可能出现在任何需要处理数据流的场合.,这个还是比较关键的,我们可以根据TCP的解决方法来解决其他协议或者是其他别的应用粘包问题

10.异常情况

 1.其中某一个进程崩溃

进程崩溃也好,正常结束也好,操作系统,都能够回收释放对应的 PCB,可以释放里面的文件描述符表也就相当于调用 close.此时仍然会正常和对方进行四次挥手操作

2.某个主机被关机(正常关机)

对于这种正常流程的关机,操作系统会先尝试强制结束所有的用户进程,然后再进入关机流程这个过程也会和上面一样,结束进程之后,进行四次挥手.

如果主动触发 FIN 了之后的流程没走完,系统就已经关机了:
A 和 B 建立 TCP 连接, A 这边关机了A 关机之前给B发送FINB 这边收到了 FIN, B 返回 ACK, 代码进入下一阶段流程B准备发送 FIN此时如果 A 已经关机了,意味着 B 接下里的 FIN 就会反复重传几次A就算没有反应,B也会把保存A的信息给删除A关机了自然就把B给删除,这样就断开连接了.

3.某个主机电源断电(非正常关机)

A 和 B 通信,A 突然掉电了:
A 无法做出任何反应,就关机了!
B 还傻傻的以为 A 还存在呢,这个时候又分两种情况:
1.B 是发送数据方.
B 接下来发送给A的数据,都不会有 ack 了,这个时候B 就会触发超时重传,重传几次之后,发送复位报文 (RST).RST 也没有响应,B 就会单方面删除保存的 A 的信息
2.B 是接收方
接收方(被动一方),无法知道对方啥时候给我发数据当 A 没有发送数据之后, B 也不知道 A 是暂时暂停一会,还是 A挂了B 在一定时间之内没收到 A 的数据之后, 就会触发 心跳包,
心跳包,就可以认为是一个 没有载荷 的数据包只是为了触发 ack
B 给 A 发了一个心跳包, 如果 A 正常, A 就会回应 ACK如果 A 挂了,B 不会收到任何回应.连续发了若干次,A 都没有回应, 这个时候 B 就认为 A 挂了,于是单方面释放连接.

注意:
tcp 虽然内置了心跳包,但是这个心跳包,周期比较长,依赖通过这个心跳发现对端挂了,往往需要 分钟级别 这样的时间在实际开发中,经常会实现应用层的心跳包,用更高频率,更短周期发送心跳.(ping-pong)
A->B发-个 ping  B ->A 回复一个 pong 秒级/毫秒级~,一旦某个设备挂了,就可以更快速的发现问题

4.网线断开

本质上就是第三种情况A 和 B 之间建立 TCP 连接,A 和 B 之间的网线断开了
比如,A 是发送方,B 是接收方,A 的角度,就会触发超时重传, 触发 RST, 单方面删除信息,B 的角度,就会触发 心跳包, 对方无响应,单方面删除信息!

4.如何使用UDP实现可靠性传输(面试题)

这一题看似考的是UDP其实是考TCP如何保证可靠性传输,所以要使用UDP实现可靠性就要在应用层中引入确认应答,超时重传等机制.具体内容就根据上述TCP机制来回答就可.

以上就算关于TCP的一些重点内容,要想了解更详细的内容,博主推荐观看<TCP/IP协议>这本书,感谢你的阅读,祝你一天愉快.


http://www.ppmy.cn/embedded/42486.html

相关文章

通过python读取并发送二进制文件到串口

代码 #!python.exe """ filename send_bin.py brief According to the users input, read bin file, subpackage and send the file by UART. HowToUse send_bin.py -h author shadowThreeDgmail.com data 20220224 &q…

JAVA:深入了解JAVA中的23种设计模式(二)- 结构型模式

一、前言 在上一篇 《深入了解JAVA中的23种设计模式&#xff08;一&#xff09;- 创建型模式》 中介绍了Java中的23种设计模式的创建型模式中的一些设计模式&#xff0c;本文将继续介绍设计模式中的结构型模式。 二、结构型模式 1. 适配器模式 1.1 简介   适配器模式就是起…

Leecode热题100---114:二叉树展开为链表

题目&#xff1a; 给你二叉树的根结点 root &#xff0c;请你将它展开为一个单链表&#xff1a; 展开后的单链表应该同样使用 TreeNode &#xff0c;其中 right 子指针指向链表中下一个结点&#xff0c;而左子指针始终为 null 。 展开后的单链表应该与二叉树 先序遍历 顺序相同…

了解和熟悉多线程(四)《实践:使用线程池优化Web应用性能》

《实践&#xff1a;使用线程池优化Web应用性能》 在现代Web应用中&#xff0c;性能和响应速度至关重要。随着用户请求数量的增加&#xff0c;服务器需要高效地管理和处理这些请求&#xff0c;线程池作为一种高效的并发处理机制&#xff0c;可以显著提高Web应用的性能。本文将介…

【Python设计模式14】状态模式

状态模式&#xff08;State Pattern&#xff09;是一种行为型设计模式&#xff0c;它允许对象在其内部状态改变时改变其行为。状态模式将不同状态的行为封装到不同的状态类中&#xff0c;使得状态之间的转换独立于对象本身&#xff0c;减少了条件语句的使用&#xff0c;提高了代…

从ES5迈向ES6:探索 JavaScript 新增声明命令与解构赋值的魅力

个人主页&#xff1a;学习前端的小z 个人专栏&#xff1a;JavaScript 精粹 本专栏旨在分享记录每日学习的前端知识和学习笔记的归纳总结&#xff0c;欢迎大家在评论区交流讨论&#xff01; ES5、ES6介绍 文章目录 &#x1f4af;声明命令 let、const&#x1f35f;1 let声明符&a…

MySQL基础

文章目录 数据库操作创建数据库查看数据库修改数据库编码删除数据库选择数据库 常用数据类型整数小数位类型字符、文本枚举集合类型 set日期二进制JSON类型空间类型 数据表操作创建表查看表修改表字段重命名表删除表清空表 提交与回滚提交commit回滚rollback 表数据操作运算符添…

【设计模式深度剖析】【3】【创建型】【抽象工厂模式】| 要和【工厂方法模式】对比加深理解

&#x1f448;️上一篇:工厂方法模式 | 下一篇:建造者模式&#x1f449;️ 目录 抽象工厂模式前言概览定义英文原话直译什么意思呢&#xff1f;&#xff08;以运动型车族工厂&#xff0c;生产汽车、摩托产品为例&#xff09; 类图4个角色抽象工厂&#xff08;Abstract Fac…