欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 文旅 > 美景 > UDP / TCP 协议

UDP / TCP 协议

2025/5/5 3:31:18 来源:https://blog.csdn.net/Excuse_lighttime/article/details/147662253  浏览:    关键词:UDP / TCP 协议

目录

一、前言:

 数据封装与分用: 

二、网络协议分层模型:

三、UDP / TCP 协议

 UDP 协议:

1、UDP 协议段格式:

2、UDP 的特点:

TCP 协议:

1、TCP 协议段格式:

2、TCP 协议的十大核心机制:

1、确认应答:

2、超时重传:

3、连接管理:

4、滑动窗口:

5、流量控制:

6、拥塞控制:

7、延时应答:

8、捎带应答:

9、面向字节流:

10、异常情况:


一、前言:

        网络通信是一个很复杂事情,在这个过程中会涉及到很多细节的问题。如果只使用一个协议来约定上述的所有细节,那这个协议就会非常复杂。

        所以,我们可以将一个功能复杂繁琐的协议,拆分成多个功能更单一的协议。拆分,是为了管理复杂程度,对协议进行 “分类” 、“分层”,每个部分负责一个功能,使结构更清晰明。

        协议分层,就是把很多协议,按照功能分成不同的层级,每个层级都有对应的任务。下层协议会给上层协议提供服务,上层协议会调用下层协议的功能。

下层协议会给上层协议提供服务,具体体现在:

        例如,传输层(TCP)依赖网络层(IP)提供的“尽力而为”的路由能力,TCP 在其基础上增加可靠性(如重传机制)。

上层协议会调用下层协议的功能,具体体现在:

        应用层开发者无需关心 UDP 如何实现传输,只需调用 socket.send() 接口发送数据。(这个我之前文章实现回显服务器出现过这个代码)。

 数据封装与分用: 

        发送方(封装):数据从上层向下传递时,每层添加自己的头部信息(Header)。
示例:应用层数据 → 添加 TCP / UDP 头 → 添加 IP 头 → 添加以太网头 → 物理信号。

        接收方(分用):数据从下层向上传递时,逐层剥离头部并处理。
示例:物理信号 → 解析以太网头 → 解析 IP 头 → 解析 TCP / UDP 头 → 交付应用数据。

二、网络协议分层模型:

        在网络通信中,可以分为五层模型:

        

上到下分为:

应用层 -> 传输层 -> 网络层 -> 数据链路层 -> 物理层

其中,不同的网络设备涉及到的层级也是不一样的:

        1、对于一台主机,他的操作系统内核实现了从传输层到物理层的内容,也就是 TCP / IP 五层模型的下四层。(如果考虑主机的应用程序,五层都会涉及到)。

        2、对于一台路由器,他实现了从网络层到物理层,也就是 TCP / IP 五层模型的下三层

        3、对于一台交换机,他实现了从数据链路层到物理层,也就是 TCP / IP 五层模型的下两层

并且,不同的层有着不同的协议:

 下面我们讨论的就是传输层的UDP / TCP 协议。

三、UDP / TCP 协议

 UDP 协议:

1、UDP 协议格式:

源端口号:

        占 2 个字节,他标识了发送端应用程序所使用的端口号。

目的端口号:

        占 2 个字节,用于指定接受端接收数据的应用程序端口号。这是 UDP 数据包能够正确交付到目标应用程序的关键标识。

UDP 长度:

        占 2 个字节。表示整个 UDP 数据包( UDP 首部和 UDP 数据)的长度。因为这个字段是 16 位,所以它能够表示的最大值 65535。这就限定了 UDP 数据包在理论上能够达到的最大长度为 65535 字节,即64KB。

UDP 校验和:

        占 2 个字节。用于检测数据包在传输过程中是否出现错误。它是对 UDP 首部和 UDP 数据部分进行计算得到的一个数值,接收方可以通过重新计算校验和来验证数据包的完整性,如果校验和出错,就会直接丢弃这个数据包。

        校验和出错,是因为网络数据包传输过程中有可能会出现比特翻转。因为在传输介质周围,存在各种电磁信号,如附近的电力线路、无线通信设备、工业设备等产生的电磁波。这些电磁波会对网络信号产生叠加干扰,使传输的信号发生畸变。在数字信号中,这种畸变可能导致某些比特位的值发生改变,即出现比特翻转。

        

2、UDP 的特点:

1、无连接:

        UDP 传输的过程类似于寄信,知道接收方的 IP 和 端口号就可以直接传输,不需要建立连接。

2、不可靠:

        没有确认机制,没有重传机制,如果因为网络故障该数据包没有发送给到对方(也称为丢包),UDP 协议层也不会给应用层返回任何错误信息。

3、面向数据报:

        不能够灵活的控制读写数据的次数和数量。

        在传输数据时,会严格按照应用层交付给它的报文进行原样发送。这意味着 UDP 不会对应用层传来的报文进行任何拆分或合并操作。例如,应用层交给 UDP 一个长度为 100 字节的报文,UDP 就会将这 100 字节作为一个完整的数据报封装并发送出去,不会因为任何原因将其拆分成多个小的数据报,也不会将它与其他应用层报文合并成一个更大的数据报。

        在接收数据时,UDP 要求接收端的操作与发送端相对应。如果发送端通过一次sendto函数发送了 100 个字节的数据,那么接收端就必须通过一次recvfrom函数来接收这 100 个字节的数据。这是因为 UDP 数据报是一个独立的、不可分割的整体,接收端必须以完整的数据报为单位进行接收。不能通过循环调用 10 次recvfrom,每次接收 10 个字节来获取发送端发送的 100 个字节数据。如果这样做,可能会导致以下问题:首先,除了第一次recvfrom可能接收到完整的数据报外,其余 9 次recvfrom可能会因为 UDP 缓冲区中没有新的数据报而阻塞,直到有新的数据报到达;其次,即使后续有新的数据报到达,每次接收 10 个字节也无法保证能够正确地组合成发送端发送的原始 100 字节数据,因为 UDP 数据报在网络传输过程中可能会乱序,这样的接收方式会破坏数据的完整性和正确性。

        如果需要传输的数据量超过了 64K,就会面临数据无法一次性完整传输的问题,此时就需要在应用层手动进行分包和拼装操作。

TCP 协议:

1、TCP 协议段格式:

源端口号:

        占 2 字节,标识了发送端应用程序所使用的端口号。

目的端口号:

        占 2 个字节,用于指定接受端接收数据的应用程序端口号。这是 TCP 数据流能够正确交付到目标应用程序的关键标识。

32位序号:

        TCP 将发送的数据看成是一连串的字节流,32 位序号用来标识每个字节在这个字节流中的位置。比如,一个 TCP 报文段的序号是 1000,数据长度是 1000 字节,那就表示这个报文段里的数据是从字节流的第 1000 号字节开始,到第 1999 号字节结束。这样接收方就可以按照序号把收到的报文段重新组装成正确顺序的数据,即使报文段在网络中乱序到达,也能还原出原始的数据序列。

32位确认序号:

        确认序号是接收方告诉发送方,希望收到的下一个字节的序号。例如,接收方收到了序号为 1 - 1000 的字节数据,并且都正确无误,那么它返回的确认号就是 1001,表示希望发送方接下来发送序号为 1001 及以后的字节数据。通过确认号,发送方可以知道哪些数据已经被接收方成功接收,哪些数据还需要重发。

        总的来说,32 位序号起到保证数据流顺序,而32 位确认号保证数据有没有接收到。

4位首部长度:

        明确TCP头部的大小,从而让接收方准确找到数据部分的起始位置。假如首部长度值为8(即头部占8 × 4 = 32字节),接收方会跳过前32字节,从第33字节开始读取数据。这样确保接收方能准确区分头部与数据。

六位标志位:  

        URG:紧急指针是否有效。

        ACK:确认号是否有效。

        PSH:提示接收端应用程序即将从TCP缓冲区把数据读走。

        RST:对方要求重新建立连接,我们将携带RST标识的报文段称为复位报文段。

        SYN:请求建立连接,我们把携带SYN标识的报文段称为同步报文段。

        FIN:通知对方,本端要关闭了,我们称携带FIN的报文段称为结束报文段。

16位窗口大小:

        占 2 字节。用于流量控制,它表示接收方当前愿意接收的数据量,以字节为单位。发送方根据接收方通告的窗口大小来调整自己的发送速率,避免发送过快导致接收方缓存溢出。当接收方的处理能力较强,能够快速处理接收到的数据时,接收方会增大通告窗口的大小,发送方收到后会相应地增加发送数据的速率,从而充分利用网络带宽。反之,当接收方处理能力下降时,通告窗口变小,发送方也会降低发送速率,避免数据在网络中堆积。

16位校验和:

        占 2 字节,用于检测 TCP 报文段在传输过程中是否发生了错误。计算包括 TCP 首部,也包括 TCP 数据部分。

        校验和有问题时,接收方丢弃报文段且不发送ack,通过超时重传或快速重传机制让发送方重新发送该报文段,以此保证数据的可靠传输。

16位紧急指针:

        当 TCP 报文段中的 URG 标志位被置为 1 时,表明该报文段中有紧急数据,其作用是标识报文段中紧急数据的结束位置,以便接收方能快速定位并优先处理紧急数据。例如,序号为1000,紧急指针为 50 ,则紧急数据范围为 1000~1049(共 50 字节)。

选项:

        实现了协议的功能扩展和性能增强,比如,可以实现窗口缩放,将 16 位窗口大小字段扩展为 30 位(通过左移 0~14 位),支持高速网络的大容量数据传输。

2、TCP 协议的十大核心机制:

1、确认应答:

        确认应答是 TCP 协议实现可靠数据传输的关键机制之一,通过接收方对已收到数据的确认,让发送方能够确定数据是否成功传输。

        当接收方成功接收到发送方发送的数据后,会向发送方发送一个确认应答(ACK)报文段。这个报文段是有确认序号的,这个 ACK 报文段就像是接收方给发送方的一个 “收据”,告诉发送方数据已经收到了。发送方只有收到接收方的 ACK 后,才会认为数据成功传输,然后继续发送下一批数据。如果发送方在一定时间内没有收到 ACK,就会认为数据传输出现问题,进而触发重传机制,重新发送未被确认的数据。

        确认序号表示接收方期望收到的下一个字节的序号,也就意味着接收方已经成功接收了该序号之前的所有数据。例如,发送方发送了序号为 1000、长度为 1000 字节的数据,接收方正确接收后,会在 ACK 中把确认序号设置为 2000,告诉发送方下一次请从 2000 号字节开始发送数据。

2、超时重传:

        超时重传是确认应答机制的重要补充。是针对丢包的场景的。

        这里存在两种丢包情况,一是发送方的数据包丢失,二是接收方返回的ack丢失。

        发送方发送的数据报丢包:        

        如果主机A在一个特定时间间隔内没有收到B发来的确认应答,就会进行重发。

接收方返回的 ack 丢包:

         这里就会出现主机B收到重复的数据,那么 TCP 协议就需要能识别出哪些包是重复的包,并且把重复的包丢弃。这时候我们就可以利用前面提到的序列号,从而达到去重的效果。

        TCP 在接收数据的时候,会在操作系统内核中,会在内存空间维护一个 “接收缓冲区” ,如果又收到同一个数据,此时就可以根据数据的序号来在接收缓冲区中进行 “去重” 的操作。当应用程序对数据进行读取,数据就会从缓冲区删除,如果下次接收的数据序号在已经被应用程序读取的数据序号之前,接收方会直接丢弃这个数据包。

那么,超时重传的超时时间是如何确定的?

         在理想的情况下,找到一个最小的时间,保证 “确认应答一定能在这个时间内返回”。但这个时间的长短,随着网络环境的不同,是有差异的。如果超时的时间太长,会影响整体的重传效率;如果超时时间设的太短,有可能会频繁的发送重复的包

         TCP为了保证无论在任何环境下都能比较高性能的通信,因此会动态计算这个最大超时时间。超时以 500 ms 为一个单位进行控制,每次判定超时重发的超时时间都是 500 ms 的整数倍。比如,如果重发一次后仍然得不到回应,等待2*500ms后再进行重传。如果仍然得不到回应,等待4*500ms后再进行重传。如果还是得不到回应,以此类推,以指数形式递增。

        并且,重传次数 和 总的重传时间是存在上限的。当累计到一定的重传次数,重传还没有成功, TCP 就会认为网络或者对端主机出现异常,并强制关闭连接。

3、连接管理:

        在正常情况下,TCP 要经过三次握手来建立连接,通过四次挥手来断开连接。

三次握手:

三次握手由客户端发起。 

        

第一次握手(客户端发送SYN):

        客户端像服务器发送一个 SYN (同步)报文段,就像一个人拿起电话,拨通对方号码后说 “我想和你通话,你准备好了吗”,此时客户端只是发出了请求,还不知道服务器是否能正常接收和处理信息。

第二次握手(服务器发送SYN + ack):

        服务器收到客户端的 SYN 报文段后,发送一个 SYN + ack(同步确认)报文段。这相当于对方在电话里回答 “我准备好了,你也准备好接收我的信息了吗”,服务器通过这个报文段不仅确认了自己可以接收客户端的信息,同时也询问客户端是否准备好接收自己发送的信息。

第三次握手(客户端发送 ack ):

        客户端收到服务器的 SYN + ack 报文段后,发送一个 ack(确认)报文段。这就如同打电话的人回应 “好的,我们可以开始通话了”,客户端通过这个报文段告知服务器,自己已经准备好接收服务器发送的信息,至此双方都确认了彼此的状态,可以开始正式的数据传输,就像两个人开始正式通话一样。

三次握手的意义:

        1、相当于 “投石问路” ,验证通信链路是否畅通。

        2、验证通信双方发送能力和接收能力是否正常。

        3、TCP 建立连接的过程中,有一个信息是需要协商的,就是 TCP 数据的起始序号,每次建立连接协商出来的起始序号都是不同的。例如,假设上一次连接的序号范围是 1 - 1000,由于某些原因,一个序号为 500 的数据包在网络中延迟了。当下一次连接建立时,协商出的起始序号是 10000,那么即使序号为 500 的旧数据包后来到达,接收方一看序号就知道它不属于当前连接,不会将其纳入本次连接的数据处理流程,也就不会出现 “前朝的剑斩本朝的官” 这种错误情况,保证了新连接数据的准确性和完整性。

那么,两次 / 四次握手可以吗?

两次握手:

        可以看到,如果只有两次握手,客户端可以明确自身的 “接受能力” 和 “发送能力”是完善的,但服务器只能确定自己可以收到客户端发送的数据,无法确定客户端是否能收到自己发出的数据。因此,只有两次握手是不可以的。

四次握手:

四次握手可以,但没有必要,会降低效率。因为三次握手就能确定建立连接了,何必四次握手?

四次挥手:

四次挥手可以是客户端发起,也可以是服务器端发起。在这里,定义发起断开连接的一方为客户端。

第一次挥手(客户端发送 FIN ):

        客户端向服务器发送一个 FIN(结束)报文段,就像在电话里说 “我说完了,我准备挂电话了”,此时客户端不再发送数据,但还可以接收服务器的数据。

第二次挥手(服务器发送 ack ):

        服务器收到客户端的 FIN 报文段后,发送一个 ack 报文段作为确认。这就好比对方在电话里回答 “我知道你说完了,我还没说完,你先别挂,我继续说”,服务器通过这个确认告知客户端,已经收到关闭请求,但自己还有数据要发送。

第三次挥手(服务器发送 FIN ):

        当服务器完成数据发送后,向客户端发送一个 FIN 报文段,表示 “我也说完了,我也准备挂电话了”,此时服务器也不再有数据要发送。

第四次挥手(客户端发送 ack ):

        客户端收到服务器的 FIN 报文段后,发送一个 ack 报文段进行确认。这就像是回答 “好的,那我们挂电话吧”,客户端发送完这个 ack 报文段后,会进入 TIME - WAIT 状态,等待一段时间以确保服务器能收到 ack 报文,然后才真正关闭连接,就像双方确认后才真正挂掉电话一样。

在某些情况下,四次挥手可能会合并成三次。

        服务器端收到客户端的 FIN 报文后,由于服务器端开启了 TCP 延迟确认机制且此时没有数据要发送,所以不会立即发送 ack 报文进行确认。而是等待一段时间,看是否有数据需要发送给客户端。如果在延迟等待期间,服务器端依然没有数据要发送,那么服务器端会将原本需要分开发送的 ack 和 FIN 报文合并成一个报文发送给客户端。这个报文既确认了客户端的 FIN 请求,又表示服务器端也准备关闭连接。

四次挥手过程中的两个状态,CLOSE - WAIT 和 TIME - WAIT 状态:

CLOSE - WAIT :

        当被动关闭方(一般是服务器)收到主动关闭方发送的 FIN 报文后,发送 ack 报文进行确认,然后就进入 CLOSE - WAIT 状态。

        持续时间:

        从进入该状态开始,直到被动关闭方处理完所有剩余数据,并准备好关闭连接时结束。这个时间取决于被动关闭方应用程序处理数据的速度。

        作用:

        为被动关闭方提供处理剩余数据的时间,保证数据完整性。在此期间,被动关闭方会继续处理应用层数据,完成后才会发送 FIN 报文给主动关闭方,以完成连接的最终关闭。

TIME - WAIT :

        在四次挥手的最后阶段,当主动关闭方(一般是客户端)发送出最后一个 ack 确认报文后,就会进入 TIME - WAIT 状态。

        持续时间:

        通常会在此状态停留 2 倍的 MSL(报文最大生存时间)。Linux 系统通常默认 MSL 为 30 秒,那么 TIME - WAIT 状态持续时间就是 60 秒。

        作用:

        保证被动关闭方能够收到最后一个 ack 报文。若被动关闭方未收到该 ack(可能是丢包了),会重发 FIN 报文,处于 TIME - WAIT 状态的主动关闭方能够重新发送 ack ,并且 TIME - WAIT 状态的计时器会重新计时,确保连接正常释放。

4、滑动窗口:

        我们之前讨论的确认应答机制,对每一个发送的数据段,都要给一个 ack 确认应答,收到 ack 后再发送下一个数据段,这样做有一个比较大的缺点,就是传输性能较差,尤其是数据往返的时间比较长的时候。

         既然像那样一收一发的方式性能较低,那么我们可以一次发送多条数据,就可以大大的提高性能。(将多个数据段的等待时间重叠到一起,节省时间)。

滑动窗口的工作原理:        

         1、滑动窗口的窗口大小指的是无需等待确认应答而可以继续发送数据的最大值,上图的窗口就是4000个字节(四个段)。

        2、发送前四个段的时候,不需要等待任何的 ack ,直接发送。

        3、收到第一个 ack 后,滑动窗口向后移动,继续发送第五个段的数据,以此类推。

        4、操作系统内核为了维护滑动窗口,发送方需要开辟 发送缓冲区 来记录当前还有哪些数据没有应答。只有确认应答过的数据才能从缓冲区删除。

        5、滑动窗口的接收方会有接收缓冲区,接收方的接收缓冲区用于暂存接收到的数据。当数据到达接收方时,会先进入接收缓冲区,即使应用程序暂时没有读取这些数据,数据也不会丢失。

        6、窗口越大,则网络的吞吐量就越高。

滑动窗口的滑动过程:

 

如果滑动的过程出现了丢包,怎么处理?

这里分为两种丢包情况讨论,一种数据段到达,返回的 ack 丢包,另一种是数据包丢包。

数据段到达,返回的 ack 丢包:

        如果只是 ack 丢包,不需要特殊处理。因为 TCP 是基于累积确认的,只要后续有正确的 ack  到达,发送方就可以知道哪些数据已经被接收方成功接收。

        例如,发送方发送了序号为 1 - 1000、1001 - 2000、2001 - 3000 的数据段,接收方收到了所有数据段,但返回的第一个 ack(下一个是 1001)丢失了,而发送方收到了第二个 ACK(下一个是 2001),这就说明 1 - 1000 的数据段已经被接收方成功接收,发送方可以正常滑动窗口并继续发送数据。

特殊情况:如果最后一个 ack 丢失,怎么办?

        这是说明批量传输已经结束了,已经由超时重传接管了,如果使用 TCP 传输较大量的数据时,自然会触发滑动窗口,快速重传(滑动窗口下的重传机制,相当于超时重传的变种)机制。如果使用 TCP 传输较少量的数据,此时任然是按照确认应答和超时重传的机制。

      

数据包丢包:

        当发送方发现数据包丢包时,会采用快速重传机制。如果发送方连续收到三次相同的 ack 应答,就会将对应的数据进行重新发送。

        例如,接收方没有收到序号为 1001 - 2000 的数据段,就会一直给发送方发送 ack(下一个是 1001),当发送方连续三次收到这个相同的 ACK 时,就会意识到该数据段丢失,从而重新发送 1001 - 2000 的数据段。接收方收到重传的数据段后,会发送新的 ack 应答,告知发送方数据已成功接收,发送方可以继续滑动窗口发送后续数据。 

        这个时候收到了1001之后,再次返回的 ack 就是7001了,因为 2001 - 7000 接收端其实已经收到了,被放到了接收端的接收缓冲区中。

        就像是这样:

        就像拼图丢失了一块,只要把原本缺失的那一块拼上,之后的就可以正常传输数据了,接下来就可以从队列最后一个数据的序号继续往后索要。

5、流量控制:

        接收端处理数据的速度是有限的,如果发送端发送的太快,或者接收方应用程序处理缓冲区的数据比较慢,就会导致接收端的缓冲区满了,这个时候如果继续发送数据,就会造成 丢包,继而引起一系列连锁反应~

        因此TCP支持根据接收端的处理能力,来决定发送端的发送速度。这个机制就叫做流量控制

        1、接收端将自己可以接收的缓冲区大小放到 TCP 首部的 “16位窗口大小” 字段,通过 ack 端通知发送端。( 16位数字最大可表示65535,但并不代表TCP窗口最大是65535字节,即64kb。实际上,TCP首部40字节选项中还包含了一个窗口扩大因子M,实际窗口大小是窗口字段的值左移M位。左移一位相当于 * 2,通过这种方式,TCP 窗口的实际大小能够远超 65535 字节,从而适应高速网络和长距离网络的需求,提高数据传输的效率。

        2、窗口越大,说明网络吞吐量越高。

        3、接收端一旦发现自己的缓冲区快满了,就会将窗口大小设置为一个更小的值发送给发送端。

        4、发送端收到这个窗口之后,就会减慢自己的发送速度。

        5、如果接收端缓冲区满了,就会将窗口设置为 0,这时发送端无法再发送数据。但发送端会定期发送一个窗口探测数据段,使接收端把窗口大小告诉发送端,当窗口大小不为0则可以继续发送。

6、拥塞控制:

        拥塞指的是网络中间链路出现丢包的情况,一次性传输太多的数据可能会造成拥塞。虽然TCP有了滑动窗口这个“大杀器”,能够高效可靠的发送大量数据。但是如果在刚开始阶段就发送大量的数据,仍然可能引发问题。因为网络上有很多计算机,可能当前的网络状态就比较拥堵,如果这个时候发送大量数据,可能会让本就拥堵的网络状态雪上加霜,造成丢包

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

这里有个概念:拥塞窗口

        拥塞窗口是发送端维护的一个状态变量,它表示发送端在当前网络状况下能够安全发送的数据量。其大小会根据网络的拥塞程度动态调整,目的是在不引起网络拥塞的前提下,尽可能高效地利用网络带宽进行数据传输。

拥塞控制流程:

        刚开始发送的时候,定义拥塞窗口的大小为1,每收到一个ACK应答,拥塞窗口大小加1(可以理解为每一轮拥塞窗口是上一轮的 2 倍)。每次发送数据包的时候,将拥塞窗口和接收端主机反馈的窗口大小对比,取较小的值作为实际发送的窗口。拥塞窗口的增长速度是指数级的,虽然启动速度慢,但是增长很快

        当指数增长到一定程度(达到阈值),指数增长变成线性增长,主要是防止指数增长某一次翻倍,导致超出上限太多。当线性增长到一定程度,终究会触发丢包(网络的承载能力到达上限了)。

        出现丢包后,一种方式是回到最初慢启动窗口大小,接下来重复上述的流程;另一种方式是重新计算阈值(丢包的窗口大小 / 2),从阈值开始作为新的拥塞窗口,继续线性增长(这种方式较常见)。

总结:

        少量的丢包,我们仅仅是触发超时重传;大量的丢包,我们就认为此时网络拥塞。当TCP通信开始后,网络吞吐量(窗口)会逐渐上升;随着网络发送拥堵,吞吐量会立即下降。 拥塞控制归根结底就是 TCP协议 想尽可能快的把数据传输给对方,但是又要避免速度太快给网络造成太大压力的折中方案。

7、延时应答:

如果接收数据的主机在收到数据后立刻返回ACK应答,这时候返回的窗口可能较小。 

         假如接收端缓冲区大小为 1M。收到了 500K 的数据,如果此时立即应答,返回的窗口大小就是 500K;但实际上处理端处理的速度可能很快,10ms之内应用程序就把500K的数据从缓冲区消费掉了。在这种情况下,接收端处理能力还远远没有到达自己的极限,即使窗口再大一点,也能处理。如果接收端稍微等待一会再应答(应用程序很快就把接收缓冲区的数据读取消并除了),那么这个时候返回的窗口大小就会更大。窗口越大,网络的吞吐量越大,传输效率越高。通过延时应答机制可以尽可能的提高窗口大小。

   

但并不是所有的包都可以延时应答,延时应答也是有限制的: 

数量限制:每隔N个包就应答一次。

时间限制:超过最大延迟时间就应答一次。

8、捎带应答:

         在 TCP 通信里,当接收方收到发送方的数据后,需要发送 ack(确认应答)报文来告知发送方数据已成功接收。通常而言,接收方可能会有自己的响应数据要发送给发送方(接收方要发送的响应数据一般需要较多时间处理)。

        捎带应答就是指接收方在有响应数据要发送给发送方时,不单独发送 ack ,而是等响应数据要发送的时候,把 ack 也带上,一起发送给发送方。

        由于延时应答的存在,ack 不一定立即返回,可以让 ack 稍等一会(这里涉及到了延时应答),等待响应数据一起返回,这样可以通过把两次传输合并在一起,提高效率。

9、面向字节流:

        TCP 将应用层交付的数据看作无结构的字节序列,不关心数据的具体含义、格式和边界,只负责准确且按序地在收发两端传输字节。例如,对于一个视频文件,TCP 只把它当作一连串的字节进行处理。数据传输像水流一样连续,发送方不断将数据字节写入 TCP 连接,接收方持续接收,TCP 保证字节顺序,即便网络中数据包乱序,接收端也会重新排序。

        所以这里引发了一个问题:粘包问题

        例如,发送端连续发多个数据包,接收端可能无法准确区分数据包边界,多个数据包粘连成一个连续字节流,难以正确分割。例如,发送 “Hello”“World” 两个数据包,接收端可能收到 “HelloWorld”,无法确定两个数据包的起止位置。主要原因有两个 一,发送方数据发送快,接收方读取慢,多个数据包在接收缓冲区积累,导致粘包。如发送方短时间内发送多个小数据包,接收方还没来得及读取,它们就会在缓冲区粘连。二,应用层发送和接收数据时,没正确处理数据边界,如未按规定格式发送或未正确解析数据长度信息,使接收方无法区分数据包。

所以,如何避免 “粘包问题”呢?归根结底就一点:明确两个包之间的边界

        对于定长的包,保证每次都按固定大小读取即可。

        对于变长的包,可以在包头的位置,约定一个包总长度的字段,从而就知道了包的结束位置。

        对于变长的包,还可以在包和包之间使用明确的分隔符(分隔符不能和正文冲突)。

        但是,实际开发中,很多时候是基于一些现成的框架 / 库 进行开发的(粘包问题就已经被框架 / 库 解决了)。

         

10、异常情况:

异常情况指的是那些偏离正常流程、可能干扰系统稳定运行和数据准确传输的状况。

进程终止 :

        当进程正常终止时,通常会按照 TCP 协议的规范来关闭连接。它会向对端发送 FIN 包,此包用来表示自己不再发送数据,这是 TCP 连接关闭流程中的主动关闭操作。对端收到 FIN 包后,会返回 ack  确认,然后对端也可以发送自己的 FIN 包,主动关闭方向对端返回 ack  确认,这样就完成了 TCP 连接的四次挥手过程,连接正常关闭。

主机关机:

        在主机关机时,操作系统会进行一系列的清理操作。对于正在进行的 TCP 连接,操作系统会尝试有序地关闭它们。这意味着操作系统会模拟进程正常终止时的行为,向对端发送 FIN 包,然后等待对端的响应,完成四次挥手过程。在这个过程中,操作系统会确保已经发送出去的数据都能得到确认,并且尽量让对端正常关闭连接。

主机掉电 / 网线断开:

        主机掉电 / 网线断开是一种突发的、不可预测的情况。

        如果掉电 / 网线断开的一方是接收方。发送方是不知道的,会继续发送数据包,但接收方不会返回 ack ,发送发会触发超时重传,重传到一定次数,还是没有收到接收方的 ack ,就会断开当前连接。

       在正常情况下,发送方会按照设定的固定频率向接收方发送心跳包,同时也会发送业务数据。心跳包的作用是让接收方能够实时了解发送方的运行状态,确认发送方是否正常在线并能够进行数据传输。接收方在收到心跳包后,会根据约定的规则进行相应的处理,例如更新发送方的状态信息,表示发送方处于活跃状态。如果掉电 / 网线断开的一方是发送方。发送方无法再向接收方发送心跳包和其他数据。接收方会根据心跳包机制来判断发送方的状态。接收方内部设有一个定时器,每次收到发送方的心跳包时,定时器会被重置。如果在设定的时间内没有收到心跳包,定时器就会超时。当连续多个心跳周期都出现定时器超时的情况,即连续多个心跳周期都没有收到心跳包时,接收方就会认为与发送方的连接出现了问题。从而断当前的连接。

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

热搜词