【Linux 30】传输层协议 - TCP

文章目录

  • 🌈 一、TCP 协议介绍
    • ⭐ 1. TCP 协议的特点
  • 🌈 二、TCP 协议格式
    • ⭐ 1. TCP 报头中各字段的含义
    • ⭐ 2. 各 TCP 标志位的用途
    • ⭐ 3. 使用结构体描述 TCP 报头
  • 🌈 三、TCP 的窗口
    • ⭐ 1. TCP 的发送和接收缓冲区
    • ⭐ 2. TCP 为什么存在缓冲区
    • ⭐ 3. TCP 的缓冲区大小
  • 🌈 四、TCP 保证可靠性的机制
    • ⭐ 1. 确认应答
      • 🌙 1.1 TCP 的通信模式
      • 🌙 1.2 使用序号和确认序号唯一标识消息和应答
    • ⭐ 2. 超时重传
      • 🌙 2.1 TCP 的去重功能
      • 🌙 2.2 超时重传的等待时间
    • ⭐ 3. 连接管理
      • 🌙 3.1 操作系统如何实现对连接的管理
      • 🌙 3.2 TCP 通过三次握手建立连接
      • 🌙 3.3 TCP 通过四次挥手断开连接
    • ⭐ 4. 流量控制
      • 🌙 4.1 接收端应将自己接收数据的能力告知发送端
      • 🌙 4.2 设置 PSH 标志位让对端尽快处理数据
      • 🌙 4.3 第一次发数据时如何得知对方的窗口大小
    • ⭐ 5. 滑动窗口
      • 🌙 5.1 滑动窗口的功能
      • 🌙 5.2 滑动窗口的大小变化
      • 🌙 5.3 滑动窗口通过双指针实现
      • 🌙 5.4 滑动窗口如何解决丢包
      • 🌙 5.5 如何防止滑动窗口滑出缓冲区
    • ⭐ 6. 拥塞控制
      • 🌙 6.1 使用拥塞控制解决网络拥堵
      • 🌙 6.2 使用拥塞窗口实现拥塞控制
      • 🌙 6.3 如何调整拥塞窗口的大小
    • ⭐ 7. 延迟应答
      • 🌙 7.1 延迟应答的策略
    • ⭐ 8. 捎带应答
  • 🌈 五、面向字节流
    • ⭐ 1. TCP 以字节为单位发送和接收数据
    • ⭐ 2. TCP 程序的读和写不需要逐个对应
  • 🌈 六、粘包问题
    • ⭐ 1. 什么是粘包问题
    • ⭐ 2. 如何解决粘包问题
    • ⭐ 3. UDP 不存在粘包问题
  • 🌈 七、TCP 异常情况
    • ⭐ 1. 进程异常终止
    • ⭐ 2. 机器重启
    • ⭐ 3. 机器掉电 / 网线断开
  • 🌈 八、基于 TCP 实现的应用层协议
  • 🌈 九、TCP 与 UDP
    • ⭐ 1. TCP 与 UDP 的对比
    • ⭐ 2. 如何用 UDP 实现可靠传输 (面试题)

🌈 一、TCP 协议介绍

  • 传输控制协议 TCP (Transmission Control Protocol) 是互联网中使用最广泛的传输层协议,它用于对数据进行详细的控制
  • TCP 能占据如此重要的地位,其根本原因就在于它提供了详尽的可靠性保证
  • 基于 TCP 实现的上层应用有很多,诸如 HTTPS、HTTPS、FTP、SSH 等都是基于 TCP 协议实现的。
    • 甚至 MySQL 的底层使用的也是 TCP 协议。

⭐ 1. TCP 协议的特点

  • 面向连接:应用程序在使用 TCP 协议传送数据前,必须先建立 TCP 连接。在传送完数据后,还必须释放建立的 TCP 连接。
  • 保证可靠性:通过 TCP 连接传送的数据所具备的特点::无差错、不丢失、不重复、按序到达。
  • 全双工通信:通信双方的应用进程在任何时候都能发送数据。
  • 面向字节流:传输的数据是以字节为单位的字节流序列。

🌈 二、TCP 协议格式

image-20241104162948694

⭐ 1. TCP 报头中各字段的含义

  • 16 位源端口号:发送方主机的进程端口号,用来标识数据从哪个进程来。
  • 16 位目的端口号:目的主机的进程端口号,用来标识数据要到哪个进程去。
  • 32 位 TCP 序号:标识本报文段所发送的数据的第一个字节的编号。
  • 32 位 TCP 确认序号:接收方期望收到发送方下一个报文段的第一个字节数据的编号。
  • 4 位 TCP 首部长度:单位是 4 字节,记录 TCP 头部有多少个 32 位 bit (4 字节)。所以 TCP 报头最大长度是 15 * 4 = 60 字节。
    • 由于 TCP 选项最多占 40 个字节,需要能够表示 40 字节的 TCP 选项 + 20 字节的 TCP 报头的固定长度。
  • 6 位保留:为 TCP 将来的发展所预留的空间,目前这 6 位全部都必须为 0。
  • 6 位 TCP 标志位:用来区分 TCP 报文的类型。
  • 16 位窗口大小:表示发送该 TCP 报文的发送端的接收窗口还能接收多少字节的数据流,该字段主要用于流量控制
  • 16 位检验和:用于确认传输的数据是否损坏。
  • 16 位紧急指针:用于标识哪部分数据为紧急数据。
  • 32 位 TCP 选项:长度不定 (但必须是 32 的整数倍,最多为 40 字节),内容可变,必须使用 TCP 首部的实际长度来区分 TCP 选项的具体长度。
    • TCP 选项的具体长度 = TCP 报头的实际长度 - TCP 报头的固定长度 (20 字节)。

⭐ 2. 各 TCP 标志位的用途

  • URG:用来记录紧急指针是否有效。
  • ACK:用来记录确认序号是否有效,只要该位为 1,则表示该 TCP 报文是应答报文。
  • PSH:用来提示接收端的应用程序立刻将 TCP 接收缓冲区中的数据读走。
  • RST:要求对方重新建立连接 (重新执行三次握手的过程);通常将携带 RST 标识的报文称为复位报文段
  • SYN:请求与对方建立连接;通常将携带 SYN 标识的报文称为同步报文段
  • FIN:请求与对方断开连接;通常将携带 FIN 标识的报文称为结束报文段

⭐ 3. 使用结构体描述 TCP 报头

  • 网络协议都是用 C 语言写的通信双方都能认识的结构体类型,TCP 协议自然也不例外。
// TCP 报文头部,总长度 20 字节
typedef struct _tcp_hdr
{unsigned short src_port;        // 源端口号unsigned short dst_port;        // 目的端口号unsigned int seq_no;            // 序列号unsigned int ack_no;            // 确认号
#if LITTLE_ENDIANunsigned char reserved_1 : 4;   // 保留 6 位中的 4 位首部长度unsigned char thl : 4;          // 记录 tcp 报文头部的长度unsigned char flag : 6;         // 6 位的 TCP 标志位unsigned char reseverd_2 : 2;   // 保留 6 位中的 2 位
#elseunsigned char thl : 4;          // 记录 tcp 报文头部的长度unsigned char reserved_1 : 4;   // 保留 6 位中的 4 位首部长度unsigned char reseverd_2 : 2;   // 保留 6 位中的 2 位unsigned char flag : 6;         // 6 位的 TCP 标志位
#endifunsigned short wnd_size;        // 16 位窗口大小unsigned short chk_sum;         // 16 位 TCP 检验和unsigned short urgt_p;          // 16 位紧急指针
} tcp_hdr;

🌈 三、TCP 的窗口

⭐ 1. TCP 的发送和接收缓冲区

  • TCP 的窗口采用缓冲区的形式实现。
  • TCP 通信双方在进行通信时,本质上是将 TCP 发送方的发送窗口 (缓冲区) 中的数据拷贝到 TCP 接收方的接收窗口 (缓冲区) 。
  • 发送缓冲区:用来暂时保存还未发送的数据。
  • 接收缓冲区:用来暂时保存接收到的数据。

image-20241106113654276

  • 由应用层中的应用程序往 TCP 的发送缓冲区中写入数据。当上层应用调用 write 或 send 这样的系统调用接口时,实际上不是将数据直接发到网络中,而是向下交付给传输层 (将数据从应用层拷贝到 TCP 的发送缓冲区)。
  • 由应用层中的应用程序从 TCP 的接收缓冲区中读取数据。当上层应用调用 read 或 recv 这样的系统调用接口时,实际上也不是直接从网络中读取数据,而是将数据从 TCP 的接收缓冲区中拷贝到应用层。

image-20241106121449799

  • 网络通信的本质将发送方的发送缓冲区中的数据拷贝到接收方的接收缓冲区

⭐ 2. TCP 为什么存在缓冲区

  • 为什么要有发送缓冲区:数据在网络传输的过程中可能会出现丢包的情况,这时候就需要发送方重新发送数据,因此需要提供一个发送缓冲区来暂存未发送 / 未成功发送的数据。只有当被发送出去的数据被接收方确认收到,发送缓冲区中的这部分数据所占用的空间才可以被其他数据使用。
  • 为什么要有接收缓冲区:接收端处理数据的速度是有限的,为了不大面积丢弃没来得及处理的数据,TCP 提供了接收缓冲区用来暂存这些待处理数据。

⭐ 3. TCP 的缓冲区大小

  • 发送端给接收端发送数据时,说白了就是将发送方自己的发送缓冲区中的数据拷贝到接收方的接收缓冲区。
  • 但是缓冲区的容量不可能是无限的。如果接收端处理数据的速度 < 发送端发送数据的速度,那么接收端的接收缓冲区就肯定会被填满。之后发送端再有数据到达的话,接受端就只能将这些数据丢弃了。
  • 因此在 TCP 报头中就存在一个 16 位的窗口大小字段,用来标识自身的接收缓冲区中还有多少字节的空间。
    • 因此,TCP 的缓冲区范围是 0 ~ 65535 字节。
  • 接收端在给发送端传来的报文作应答时,就可以填充这个 TCP 报文中的窗口大小字段,告诉发送端自己的接收缓冲区内还有多少空间,让发送方自行决定发送速度。

🌈 四、TCP 保证可靠性的机制

⭐ 1. 确认应答

  • 确认应答机制 (ACK) 是 TCP 所有用来保证可靠性的机制中最重要的机制

  • ACK 机制通过 TCP 报头中的 32 位序号以及 32 位确认序号来实现。

  • ACK 机制并不能保证通信双方的全部消息的可靠性,而是通过收到对方的应答,来保证发送方曾经发送给对方的某条信息被对方可靠的收到了。

    • 注:应答报文单纯就只是一个 TCP 报头,不携带任何数据。
    • 发送方只要收到了应答,就能保证发送方发送的数据一定被对方收到。
  • 通信双方都可以作为发送方和接收方,只要发送方没收到接收方传回来的应答,就可以判断出自己曾经发送的数据丢了。

  • 确认应答机制保证的并不是最新的消息的可靠性,而是保证历史消息的可靠性

    • 发送方能通过确认应答机制知道自己曾经发送的数据到底丢没丢,这才是可靠。

image-20241104174207585

  • 通信双方都使用确认应答机制,就能保证通信双方所发送的历史消息的可靠性。
  • 注:接收方只会对接收到的消息做应答,而不会对接收到的应答做应答。即不保证应答的可靠性,只保证数据的可靠性。

🌙 1.1 TCP 的通信模式

  • 上面所演示的都是发送方一次向接收方发送一条消息,发送方只有在收到应答之后才能继续发送下一条消息,这种通信模式的发送效率低。
  • 为了解决这个问题,可以让发送方批量消息,然后接收方再对这些请求批量进行应答。

image-20241104193034653

  • 这时候其他问题又来了,发送方没有办法区分收到的多条 ACK 分别对应的是自己曾经发送过的哪一条消息。
  • 为了解决这个问题,在 TCP 报头引入了序号与确认序号来为每个消息与应答都分配独一无二的编号,这样就不会混乱了。

🌙 1.2 使用序号和确认序号唯一标识消息和应答

  • 由于 TCP 是面向字节流进行传输的,可以讲 TCP 的 发送和接收 缓冲区都当成一个字符数组。
  • 序号就是字节缓冲区的数组下标,通信双方都各自持有一套发送缓冲区和接收缓冲区。

image-20241104193807055

  • 发送方发送的 data 的序号范围在 1 ~ 100,那么接收方要返回的 ACK 应答报文中的确认序号就是 100 + 1 = 101。
  • 发送方在收到 ACK 后提取出确认序号 101,用 101 - 1 就判断出这个 ACK 是对序号范围在 1 ~ 100 的请求的应答。

image-20241105134840656

  • 确认序号的定义:确认序号用来表示在确认序号之前的数据都已经全部被收到
  • 根据确认序号的定义,即使应答报文在传输过程中丢失了一部分,只要有一个应答被发送方收到,都能知道确认序号之前的数据成功被接收。
    • 例:接收方返回了确认序号为 1001、2001、3001 的 3 个应答报文给发送方。即使前两个应答在半路丢了,只要 3001 这个应答被发送方接收,发送方就能确定在 3001 之前的所有报文都成功被接收。
  • 确认序号除了告知发送方之前发送的数据已经被成功接收,还能告诉发送方接下来发送的数据的序号应该从哪个数字开始。

image-20241105143250723

  • 引入了确认序号之后,还能减少接收方要返回的应答报文的数量。
    • 既然发送方能够通过一个确认序号知道确认序号之前的数据被接收,那么接收方也可以只返回对最后一个数据的应答。

image-20241105143618122

⭐ 2. 超时重传

  • 发送方在发送完消息后,如果一段时间内没有收到 ACK 应答,发送方就会重新发送该消息
  • 丢包分为两种情况:一是发送方发送的数据报文丢失;二是接收方返回的应答报文丢失。不管是哪种情况,只要发送方在规定时间内没有收到 ACK 应答报文,都会触发超时重传机制。

image-20241105160147623

🌙 2.1 TCP 的去重功能

  • 如果发送方认为数据包丢失,从而触发了超时重传机制。但接收方实际上已经收到了发送方的数据报文,只是因为某些原因而没有及时返回应答报文。这时候接收方就会收到重复的数据报文,为了应对这种情况,TCP 引入了去重功能。
  • TCP 中存在着一个叫接收缓冲区的的存储空间,接收端会将接收到的数据放到对应缓冲区中。根据数据的序号判断是否有重复的数据,如果没重复,就将接收到的数据拷贝到对应位置的接收缓冲区中;如果重复,则将后面到来的数据丢弃。

举个例子

  • 发送方发送一段序号在 1 ~ 8 的数据,而接收方将收到的数据存储在接收缓冲区中的 1 ~ 8 号位置。

image-20241105160833801

  • 然后,发送方因为某些原因触发了超时重传机制,又向接收方发送了一段序号在 1 ~ 8 的数据。接收方在收到这段数据后,会先查看接收缓冲区中的 1 ~ 8 号位置是否已经被占用了。如果没被占用,则将收到的数据拷贝到对应位置,如果被占用了,则将新接收到的数据丢弃。

image-20241105161335833

🌙 2.2 超时重传的等待时间

  • 既然发送方会在等待时间之后触发超时重传机制,那么如何设置这个等待时间就是一个问题。

1. 触发超时重传的等待时间不能太长或太短

  • 等待时间太长:会导致在丢包后,对方长时间收不到对应的数据,进而影响整体重传的效率。
  • 等待时间太短:会导致接收方收到大量的重复数据。没准接收方的响应报文正在奔向发送方的路上呢,发送方就急不可耐的又发了一份数据给接收方。发送方发送报文也是有消耗的。

2. 如何设置触发超时重传的等待时间

  • 超时重传等待时间需要合理的设置,最理想的状况就是找到一个最小时间,保证应答报文一定能在这个时间内返回。
  • 实际上,因为网络环境的问题,这个等待时间是会变的,不可能是固定的值。
  • TCP 为了保证不管在什么环境下都能有较高的性能,会动态计算超时重传的最大超时时间
    • Linux 以 500ms 为一个单位控制超时时间,每次判断超时重传的超时时间都是 500ms 的整数倍。
    • 如果在触发了一次超时重传后,依然收不到应答,下一次触发超时重传的等待时间就是 2 × 500ms。
    • 如果还是收不到应答,下一层触发超时重传的等待时间就是 4 × 500ms,按指数形式递增。
    • 当累计触发了一定次数的超时重传机制后,TCP 就认为是网络或对端主机出现异常,直接强制关闭连接。

⭐ 3. 连接管理

  • TCP 协议在通信前需要建立连接,而 TCP 需要经过三次握手建立连接TCP 需要经过四次挥手断开连接
  • TCP 的可靠性保证的是连接的可靠性,要保证传输数据的可靠性就要建立一条稳定的通信链路。

image-20241105172210203

🌙 3.1 操作系统如何实现对连接的管理

  • 只有建立一条连接 TCP 才能实现它的各种可靠性机制。一台机机器上同时会存在大量的连接,操作系统需要将这些连接管理起来
  • 操作系统想要管理好这些连接,就需要一个用来描述连接的结构体类型,这个结构体中包含了连接的各种属性字段,所有定义出来的连接结构体对象最终都会以某种数据结构组织管理起来。此时操作系统对连接的管理就变成了对这个数据结构的增删查改。
  • 建立连接,就是在操作系统中用连接结构体定义一个对象,然后填充该对象内的各种字段,最后将这个连接结构体对象插入到用以管理连接的数据结构中
  • 断开连接,就是将某个连接结构体对象从用来管理连接对象的数据结构中删除,释放该连接对象曾经占用的各种资源。

🌙 3.2 TCP 通过三次握手建立连接

  • 通信双方在进行 TCP 通信前,需要先建立连接。这个建立连接的过程被称为三次握手。
  • 三次握手就是发送方和接收方互相交换自己的 TCP 报头通过 TCP 标志位中的 SYN 和 ACK 标志位的状态 (0 和 1) 来建立连接

image-20241105194415021

1. 三次握手的过程

  1. 第一次握手:客户端发送一个 TCP 标志位中的 SYN 标志位 (请求建立连接标志位) 为 1 的 TCP 报文给服务端。服务端在收到客户端发来的 TCP 报文后,看到 TCP 标志位中的 SYN 标志位为 1,知道了客户端想要与自己建立连接。
  2. 第二次握手:服务端收到客户端发来的连接请求报文后,向客户端返回一个 ACK 标志位为 1 的 TCP 应答报文,表示确认收到客户端的连接请求报文。并且将该报文的 SYN 标志位也置为 1,表示服务端请求与客户端建立连接 (同意和客户端建立连接)。
  3. 第三次握手:客户端收到服务端返回的报文后,看到这个报文的 ACK 标志位为 1,知道了服务端已经收到了自己在第一次握手时发出的连接请求报文。同时,看到了 SYN 的标志为也为 1,知道服务端同意与自己建立连接。最后客户端对服务端返回一个 ACK 应答报文,不管服务端有没有收到这个 ACK 应答报文,客户端都认为连接建立完成。

2. 建立连接的本质

  • :赌在第三次握手后,客户端发出去的 ACK 应答报文能够被服务端收到。
  • 如果不赌的话,服务端就必须对第三次握手时客户端发过来的应答报文作应答。但这时候服务端就又不知道自己的这个对应答报文的应答报文是否被客户端成功接收,又需要客户端返回一个对应答报文的应答的应答报文。最终会导致没完没了的套下去,因此只能靠赌。
  • 即使赌输了也没关系,客户端不知道自己的应答报文服务端有没有收到,但服务端知道啊。如果服务端没有收到客户端发来的对第二次握手时自己发送出去的 ACK + SYN 报文的应答,服务端会触发超时重传机制

image-20241105194605486

3. 如果服务端正处在触发超时重传的等待时间,此时客户端发来了消息该怎么办

  • 虽然服务端会触发超时重传机制,但这也是需要时间的。
  • 客户端只会认为在三次握手完了之后,连接建立成功。客户端立马就会发起通信,但此时服务端还没有到触发超时重传的时间,这就尬住了。
  • 说是这么说,但服务端不会真的呆住。如果服务端没有收到客户端的 ACK 报文,但却收到了客户端发来的其他消息。服务端就会在准备返回给客户端的 ACK 应答报文中将 RST 标志位置 1,让客户端重新执行三次握手建立连接
  • 通过上述方式,即使后续通信过程中连接断开了,接收方也能向发送方返回 RST 标志位为 1 的 TCP 报文,让对方重新执行三次握手建立连接。

image-20241106105734620

4. 为什么是三次握手 (高频面试题)

  • 如果一次握手就能建立起连接,就可能产生 SYN 洪水,即客户端可以低成本疯狂的向服务器发送 SYN 报文,服务器就会瞬间挂满不会被使用的连接 (连接是有成本的),从而导致服务器的可用资源越来越少。
  • 如果二次握手就能建立起连接,同一次握手同理。客户端发一个 SYN 报文,服务端就得返回一个 ACK 报文。如果客户端屏蔽掉服务端发回来的 ACK 报文,只向服务端发送 SYN 请求报文,同样能造成 SYN 洪水问题。
  • 虽然三次握手也会存在 SYN 洪水攻击的问题,但客户端想要攻击服务端,就必须接收第二次握手时服务端发回来的 ACK 报文,然后向服务端发送 ACK 报文,客户端得先完成三次握手。直接提升了客户端发起 SYN 洪水攻击的成本。
  • 总结起来就两句话:
  1. 三次握手是验证双方通信信道的最小次数 (快速验证网络的连通性)

    • 一次握手只能验证服务端能接收客户端的连接请求。并不能验证客户端能正确收还是正确能发。

    • 二次握手只能验证客户端能正确发送连接请求,不能验证服务端能不能正确发。

  2. 三次握手能够保证建立双方通信的共识意愿

    • 客户端想和服务端建立连接得获得服务端的同意 (服务端的 ACK 报文)。
    • 服务端想和客户端建立连接得获得客户端的同意 (客户端的 ACK 报文)。

🌙 3.3 TCP 通过四次挥手断开连接

  • 操作系统维护连接也是需要成本的,因此 TCP 通信双方在通信结束之后还需要断开连接,这个过程被称为四次挥手。

四次挥手的过程

image-20241105205450994

  1. 第一次挥手:客户端向服务端发送一个 TCP 标志位中的 FIN 标志位为 1 的 TCP 报文,表示请求与服务端断开连接。
  2. 第二次挥手:服务端在收到客户端的请求报文后,发现 FIN 标志位为 1,知道客户端想要断开连接。于是执行第二次挥手,对客户端的 FIN 报文作 ACK 应答,表示同意与客户端断开连接。但是此时服务端还不能直接与客户端断开连接,因为可能还有其他数据没发送给客户端,需要先将这些数据发给客户端才行。
  3. 第三次挥手:服务端此时已经没有数据需要发给客户端了。但由于 TCP 是全双工通信 (通信双方的地位对等),断开连接必须征得双方同意,光服务端同意客户端的断开连接请求还不够,还需要客户端同意服务端的断开连接请求。于是服务端执行第三次挥手,服务端向客户端发送一个 FIN 标志位为 1 的 TCP 报文,请求与客户端断开连接 。
  4. 第四次挥手:客户端在收到服务端的报文后,通过值为 1 的 FIN 标志位知道服务端请求和自己断开连接。于是执行第四次挥手,发送一个 ACK 报文给服务端,表示同意服务端的 FIN 请求。

⭐ 4. 流量控制

  • 通信双方在进行通信时,本质上是在将发送方的发送缓冲区中的数据拷贝到接收方的接收缓冲区。
  • 接收端处理数据的速度是有限的。如果发送端发送数据的速度太快,导致接收端的接收缓冲区很快被占满,发送端之后再发给接收端的数据就会被接收端丢弃 (丢包)。
  • TCP 会根据接收端接收数据的能力来决定发送端发送数据的速度,这种机制被称为流量控制

🌙 4.1 接收端应将自己接收数据的能力告知发送端

  • 接收端会在要返回给发送端的 ACK 报文中填充 16 位窗口大小字段,告知自己的接收缓冲区中还能存放多少数据。
  • 由于 TCP 是全双工通信,通信双方都要执行流量控制。任何一方在作为发送方发送 TCP 报文时,都要填写 TCP 报头中的 16 位窗口大小字段,来告知对端主机自己的接收缓冲区的剩余空间。

image-20241106162931086

  • 发送端在收到 ACK 报文后,会根据获取到的接收端的接收缓冲区大小来调整自己发送数据的速度。
  • 当发送端得知接收端接收数据的能力 (接收缓冲区没空间) 时,就会暂时停止发送数据。
  • 但发送端并不会一直停止发送数据,等到接收端的接收缓冲区中又有足够的空间时,发送端就会重新开始发送数据。现在的问题就成了发送端该咋知道啥时候能继续发数据。

1. 发送端怎么知道什么时候可以继续发送数据

  1. 等待告知:在接收端的上层应用将接收缓冲区的数据读走后,接收端就会向发送端发送一个 TCP 报文,主动将自己的窗口大小告知给发送端,发送端在得知有空间了后,就会继续向接收端发送数据。
  2. 主动询问:发送端每隔一段时间就会向接收端发送一个裸的 TCP 报文 (只有报头),接收端在对这个报文作应答时,会自动在 ACK 报文中填充 16 位窗口大小,发送方就能知道接收方的接收缓冲区大小了。
  • 上述两种策略在网络中被同时使用,没有选择谁这种说法,同时使用才能最快速建立对接收缓冲区的共识。

🌙 4.2 设置 PSH 标志位让对端尽快处理数据

  • 如果接收端的窗口大小一直为 0 时,发送端不可能会一直向接收端发送探测报文,这时候就要用上 TCP 标志位中的 PSH 标志位了。
  • PSH 标志位的全称是 PUSH,用来提示接收端的应用程序立刻将 TCP 接收缓冲区中的数据读走。

image-20241106190836895

  • 虽然以这种例子来说明 PSH 标志位的功能,但不代表这个标志位只能在接收缓冲区中没空间时使用。PSH 标志位本质上是让对端尽快处理数据

🌙 4.3 第一次发数据时如何得知对方的窗口大小

  • 通信双方在进行 TCP 通信时需要经过三次握手建立连接。双方在握手的过程中,也是在交换各自的报文。在交换的报文中,除了验证双方的通信信道是否畅通外,还交互了其他信息。
  • 这个交互的其他信息中,就包括了自己的接收缓冲区的剩余容量。因此,通信双方就可以在通信之间就得知对方接收数据的能力。

⭐ 5. 滑动窗口

image-20241106193024421

  1. 流量控制是让发送方根据接收方的数据接收能力来调整发送数据的速度。问题是,发送方应如何根据接收方的数据接收能力来调整发送速度?
  2. 发送报文后, 收到 ACK 应答前,在这期间处于超时重传的等待时间。在等待时间以内,发送方不能将已经发送的报文丢弃,而是要保存起来。 问题是,这些数据应该保存在哪?
  • 为了解决上述问题,引入了滑动窗口机制,滑动窗口是发送缓冲区中的一部分。

1. 将发送缓冲区中的数据分成三部分

  1. 已经发送 & 已经收到应答的数据。
  2. 已经发送 & 还没收到应答的数据 (滑动窗口)。
  3. 待发送的数据。

image-20241106202259284

🌙 5.1 滑动窗口的功能

  • 在滑动窗口内的数据不需要等待应答,可直接发送给接收端

image-20241106202513896

  • 发送端在收到某一个确认序号时,表示该确认序号之前的数据已经被接收端接收,可以直接将滑动窗口的起始位置移动到确认序号处。

image-20241106232438129

🌙 5.2 滑动窗口的大小变化

image-20241106211303605

  • 滑动窗口的大小不是固定的,会根据接收端的数据接收能力来调整滑动窗口的大小。
    • 接收端的数据接收能力强,滑动窗口就大;接收端的数据接收能力弱,滑动窗口就小。
    • 注:滑动窗口的实际大小不止依靠接收端的窗口大小,还要根据之后的拥塞控制中的拥塞窗口来判断。这里暂时以滑动窗口大小等于接收端窗口大小来举例。
  • 且由于滑动窗口左侧的都是已经收到应答 (被接收端成功接收) 的数据,因此滑动窗口不会向左滑

1. 扩大滑动窗口

  • 假设接收端的应用层从接收缓冲区中拿走了 1000 个字节的数据,那么接收端的接收缓冲区就会多出 1000 字节的可用空间。接收端会在返回给发送端的 TCP 报文中告知发送端,接收端的接收缓冲区空余空间变成了 4000 字节。此时发送端的发送缓冲区的滑动窗口就会扩大成 4000 个字节。

image-20241106211640024

2. 缩小滑动窗口

  • 以滑动窗口的大小为 4000 字节为例:如果接收端已经接收到了 1001 ~ 2000 这段数据,将这 1000 字节的数据放进接收缓冲区中,并对这段数据作出应答。
  • 但是应用层却不从接收缓冲区中拿取数据,就会导致接收缓冲区的可用空间少了 1000 字节,接收端会告诉发送端自己的接收缓冲区空闲空间变成了 3000 字节,于是发送方的发送窗口大小就缩小到 3000 字节。

image-20241106212016455

🌙 5.3 滑动窗口通过双指针实现

image-20241106220206786

  • 前面已经提到过,TCP 的缓冲区本质上就是个字符数组,而 TCP 报文中的序号就是这个字符数组的下标。

  • 那么滑动窗口本质上就是通过两个指针 (数组下标) 来进行维护

    • 可定义 win_start 和 win_end 两个整型变量,用 win_start 标识滑动 窗口的起始下标,用 win_end 标识滑动窗口就的结尾下标。
  • 当发送端收到接收端返回的的应答报文时,假设应答报文中的确认序号为 N,窗口大小为 M (接收端返回的 TCP 报文中的 16 位窗口大小)。此时就可以将 win_start 更新为 N,将 win_end 更新为 win_start + M。

    • 注:win_start 和 win_end 维护的是一个前闭后开的区间,即为 [win_start, win_end)

image-20241106225348156

  • 滑动窗口为零时,就是让 win_start == win_end
  • 滑动窗口扩大时,就是固定住 win_start,让 win_end++
  • 滑动窗口缩小时,就是固定住 win_end,让 win_start++

🌙 5.4 滑动窗口如何解决丢包

  • 当发送端一次性发送多个报文数据时,可以将丢包问题大体上分为以下 2 种:

    1. 数据包已经抵达,ACK 报文丢了。

    2. 数据包丢失。

1. 数据包已经抵达,ACK 报文丢了

  • 根据确认序号的定义,发送端能够根据接收端返回的 ACK 报文中所携带的确认序号来判断该序号前的数据是否被成功接收。
  • 例:主机 A 收到了主机 B 最后发来的 ACK (6001),此时主机 A 就能知道序号在 6001 之前的数据已经成功被主机 B 接收。

image-20241106235216897

2. 数据包丢失

  • 如果接收端没有接收到序号在前的报文,反而是收到了序号在后的报文,接收端就会提醒发送端应该应该发送序号是多少的报文。
  • 例:丢失的是 1001 ~ 2000 这个报文,主机 B 并没有收到该报文,但是却收到了 2001 ~ 3000 的报文,主机 B 就会在 ACK 报文中将确认序号填充为 1001,提醒主机 A 下一个应该发送序号以 1001 开头的报文。
  • 如果主机 A 连续收到了三次确认序号都是 1001 的 ACK 报文,就会将 1001 ~ 2000 的数据包重新发送。这种数据包补发的机制被称为快重传
    • 如果通信已经接近末期,做不到连续收到三次确认序号相同的 ACK 报文,就无法触发快重传机制,转而触发超时重传机制。
  • 之后如果接收端收到了这个 1001 ~ 2000 的数据包,就会直接发送确认序号为 6001 的 ACK 报文,表示 6001 之前的数据已被接收。

image-20241106235557387

🌙 5.5 如何防止滑动窗口滑出缓冲区

  • ACK 报文中的确认序号只会增加,这就导致了滑动窗口只会向右滑动。
  • 如果将发送缓冲区想象成一种环状结构,滑动窗口就只会在这个环形队列中顺时针转圈圈,不可能发生越界的问题。

⭐ 6. 拥塞控制

🌙 6.1 使用拥塞控制解决网络拥堵

  • 在网络中进行通信时,不止要考虑通信双方的问题,还应该考虑通信双方之间的网络通路的问题。
  • 虽然 TCP 可以使用滑动窗口来高效可靠的发送大量数据。但如果在通信的初始阶段就发送大量的数据,就可能造成网络拥塞的问题。
  • 互联网中存在着大量的主机,这些主机通过网络进行连接。在不清楚当前网络状况的情况下,贸然发送大量数据,就可能使得网络变拥堵。
  • 为了知道网络状况,TCP 引入了慢启动机制:先发送少量的数据,用来摸清楚当前的网络拥堵情况,再决定发送数据的速度。
    • 发送端发送少量数据给接收端,由于 TCP 通信双方都做了流量控制,如果还出现大面积丢包,则说明是网络出问题了。
  • 如果通过慢启动机制确定了是因为网络问题导致的丢包,就不能再搞什么超时重传、快重传了。
    • 在网络拥堵的情况下,继续往网络发数据只会导致网络状况越来越差。
  • 为了解决上述问题网络拥堵的问题,TCP 引入了拥塞控制 机制。拥塞控制实现的是让使用同一个网络进行通信的主机,具有拥塞避免的共识!
    • 如果所有使用同一个网络进行通信的主机都有意识的避免造成网络拥塞,就能大大减少发生网络拥塞的概率。
  • 在同一网络下的主机如果通过慢启动机制发现网络出现了拥堵,那么这些主机都会主动进行拥塞避免,减少往网络中发送数据。
  • 如果通过慢启动机制发现网络状况良好,就逐步增大数据的发送量。

image-20241107155352958

🌙 6.2 使用拥塞窗口实现拥塞控制

  • 在进行 TCP 通信时,不仅要考虑接收端能不能抗住发送端的数据 (滑动窗口),还要考虑网络能不能扛得住发送端发送的数据 (拥塞窗口)。
  • 为了实现拥塞控制机制,TCP 引入了拥塞窗口 (整型值),用来记录网络的数据接收能力

拥塞窗口的定义

  • 如果一次性向网络中发送的数据量不超过拥塞窗口,则不会引发网络拥塞。如果一次性向网络中发送的数据量超过了拥塞窗口,则可能引发网络拥塞。
  • 发送端发送的数据量既要在接收方的数据接收能力内,也要在网络的数据接收能力内。
  • 因此,滑动窗口的实际大小应该是 min(应答窗口,拥塞窗口),取接收端的数据接收能力和网络的数据接受能力的较小值。
  • 拥塞窗口的大小是根据网络的拥堵状况来动态变化的。

🌙 6.3 如何调整拥塞窗口的大小

  • 就如同没人一开始就知道网络的拥堵状况一样,也没法知道拥塞窗口一开始该设置成多大。
  • 假设拥塞窗口最开始的值为 20,通过慢启动机制,发送端一开始发送一条报文后如果能够收到对这条,则说明网络状况良好。将拥塞窗口的大小调整为 21,然后让发送端发送 2 条报文,如果能正常收到应答,则继续将拥塞窗口的大小调整为 22。以此类推,直到开始出现大面积丢包,才开始减小拥塞窗口的大小。

慢启动的阈值

  • 像上面那样的拥塞窗口增长速度,是指数级别的。慢启动只是启动比较慢而已,但是增长速度非常快。
  • 为了控制增长速度,不能单纯的以指数的方式扩大增长窗口,因此慢启动的阈值就出现了。
  • 慢启动的阈值:当拥塞窗口的大小超过某个阈值时,不再按照指数的方式,而是实行线性增长。

image-20241108103615948

  • 指数增长:在进行 TCP 通信的初期,拥塞窗口的值为 20,并不断以指数的方式进行增长。
  • 加法增大:慢启动的阈值初始设为接收端的接收缓冲区的剩余容量,图中慢启动的阈值就是 16。当拥塞窗口的值增大道 16 时就从指数增长转为线性增长。
    • 这个慢启动阈值 16 是上一次发生网络拥塞时,拥塞窗口的一半。
  • 乘法减小:当拥塞窗口增长到会造成网络拥堵的情况时,必须要尽快恢复通信。因此,慢启动的阈值变成拥塞窗口的值 * 0.5 ,然后让拥塞窗孔的值重新变成 1 进行指数增长。

⭐ 7. 延迟应答

  • 接收端如果在收到数据时,立刻对该数据作出应答。那么每一次的应答,所返回的接收窗口就可能比较小。

举个例子

  • 假设接收端的接收缓冲区剩余空间为 1 MB,在收到一份 500 KB 的数据后,如果立刻对这个分数据作应答,此时返回的窗口大小就是 500 KB。
  • 实际上,接收端处理数据的速度是相当快的,10ms 内就能将接收缓冲区内的这 500 KB 数据消费掉。
  • 但是在这种情况之下,接收端还没有达到自己处理数据的极限,即使窗口再大一些,也能搞得定。
  • 如果接收端对收到的数据进行延迟应答,譬如说等待个 30 ms,将收到的 500 KB 数据处理完,此时接收端的数据接收窗口又是 1 MB。

🌙 7.1 延迟应答的策略

  • 窗口越大,网络吞吐量就越大,传输效率也就越高。TCP 就要要再保证网络不发生拥堵的情况下,尽可能的提升传输效率。
  • 但并不是所有的数据包都可以被延迟应答

不是所有的包都可以被延迟应答

  • 数量限制:每接收到 N个包就对这批数据的最后一个进行应答。
  • 时间限制:延迟时间应该在触发超时重传的等待时间内,一旦超过最大延迟时间就应答一次。
  • 具体的数量和超时时间,根据操作系统的不同也有差异。一般将 N 取 2,超时时间取 200ms。

image-20241108151359610

⭐ 8. 捎带应答

  • 由于 TCP 实现的是全双工通信,通信双方都可以作为发送方和接收方进行发送数据和接收数据。
  • 作为接收方返回应答报文给发送方时,如果此时接收方还有其他消息要捎带着发给对方,就可以也作为发送方将自己的消息跟着 ACK 报文一起发出。
  • 通过这种方式,某一方就不需要连发两条报文,而是直接将应答和消息合并。

image-20241105211914366

  • 为了实现捎带应答机制,TCP 报文中才会同时有序号和确认序号 (对端发过来的报文中,可能会携带数据)。
  • 捎带应答最直接的好处就是提升了发送数据的效率,让通信双方就不用发送单纯的确认报文
  • 由于执行了捎带应答的报文携带了有效数据,对端主机在收到该报文后就会对齐作出应答。当收到这个应答后,就可以知道自己发送的数据 和 ACK 报文都被对方可靠的收到了。

🌈 五、面向字节流

  • 在创建一个 TCP 的 socket 时,会在内核中分别创建一个发送缓冲区接收缓冲区

⭐ 1. TCP 以字节为单位发送和接收数据

  • 在上层应用调用 write 系统调用将数据写入到内核中时,数据会先写入到发送端的发送缓冲区中。
  • 如果要发送的数据的字节数太多,就会将这些数据拆分成多个 TCP 数据包发出。
  • 如果要发送的数据的字节数太少,就会让这些数据先在缓冲区中等待。待缓冲区内积压了差不多的待发送数据,或者其他合适的时候将这些数据发送出去。
  • 接收数据时,数据也是从网卡的驱动程序到达接收端的接收缓冲区中。然后由接收端的应用程序调用 read 系统调用从接收缓冲区中拿取数据。

⭐ 2. TCP 程序的读和写不需要逐个对应

  • TCP 不关心 发送 / 接收 缓冲区中装的是什么数据,它只知道这些都只是字节数据。TCP 只会按照发送需求不断的往接收端发送数据。
  • TCP 的任务就是将这些字节数据可靠的发送到对端的接收缓冲区中。写入数据的次数和读取数据的次数不会完全一致。
  • 往发送缓冲区中写入 100 个字节的数据时,不需要考虑如何写。可以调用一次 write 写 100 个字节,也可以调用 100 次 write,每次写入一个字节。
  • 从接收缓冲区中读取 100 个字节的数据时,不需要考虑如何读。可以调用一次 read 读100 个字节,也可以调用 100 次 read,每次读取一个字节。

🌈 六、粘包问题

  • 由于 TCP 使用面向字节流的方式进行通信,因此必然会遇到粘包问题

⭐ 1. 什么是粘包问题

  • 站在 TCP 的角度看,它没有报文的概念。在 TCP 眼里只有缓冲区中以字节为单位的数据。
  • TCP 只会按照需求不断的发送数据给接收端,TCP 不在乎一次性到底发送了几个数据报文,它只知道自己一次性发送了多少字节的数据。
  • 在应用层看来,它无法识别接收缓冲区的这一堆字节数据中到底有几个数据包。以及判断出这些数据包位于接收缓冲区的第几个字节。

⭐ 2. 如何解决粘包问题

  • 粘包问题不是 TCP 能解决的,这个问题得依靠应用层来解决。
  • 想要避免出现粘包问题,总结起来就是明确报文和报文间的边界

1. 解决定长数据包的粘包问题

  • 保证每次读取都按照固定的大小读取即可。假设一个数据包固定为 16 KB,从缓冲区头部开始依次按照 16 KB 读取即可。

2. 解决变长数据包的粘包问题

  1. 在包头的位置,约定用于记录该数据包总长度的字段,从而知道包的结束位置。
    • 如 HTTP 报头中就包含 Content-Length 字段,用来表示正文的长度。
  2. 在包和包之间使用明确的分隔符 (应用层协议,是程序猿自己来定的,只要保证分隔符不和正文冲突即可)。

⭐ 3. UDP 不存在粘包问题

image-20241108191653600

  • UDP 报头的大小固定只有 8 个字节,且 UDP 报头中还包含着 UDP 报文长度这个字段,用以记录整个 UDP 报文的大小。
  • 站在应用层的角度看,只能发送和接收完整的的 UDP 报文,不存在发送 半个 UDP 报文这种说法,报文与报文之间界限明确。

🌈 七、TCP 异常情况

  • 当客户端正常访问服务器时,如果客户端突然出现以下情况,建立好的连接会怎么样?

⭐ 1. 进程异常终止

  • 当进程退出时,会释放该进程曾经打开的文件描述符所指向的文件。
  • 当客户端进程退出时,相当于自动调用了 close 系统调用关闭了文件描述符所对应的文件。此时双方的操作系统会在底层正常完成四次挥手的过程,然后释放对应的连接资源。
  • 即,进程进程异常终止时,会自动释放文件描述符,TCP 底层可以正常发送 FIN 报文,和进程正常退出没区别。

⭐ 2. 机器重启

  • 重启机器的情况和进程异常终止的情况一样。操作系统会先 kill 掉所有进程然后再重启主机。
  • 此时双方操作系统也能正常完成四次挥手的过程,然后释放对应的连接资源。

⭐ 3. 机器掉电 / 网线断开

  • 当客户端掉线后,服务端没办法短时间内知道,因此服务端会暂时维持着与客户端的连接。但服务端由于保活策略的原因,不会一直维持着与客户端的连接。

1. 保活策略

  • 服务端会定期检查客户端的存在状况,判断客户端是否在线。如果服务端连续多次没有收到来自客户端的 ACK 应答,就可以判断出客户端已经离线,从而直接断掉与客户端的这条连接。
  • 服务端也有可能通过定期向服务端发送信息来报平安,如果服务端长时间没有收到来自客户端的报信,也可能超时关闭与客户端的连接。

2. 操作系统基本不使用保活策略

  • 在正常通信中,TCP 的保活间隔是几十分钟或一个小时。
  • 服务端可能每隔一个小时才会发出一个询问报文,用来询问客户都是否在线。
  • 对于操作系统来说,维持连接是一件很困难的事。保活策略主要还是由应用层程序来做。

🌈 八、基于 TCP 实现的应用层协议

  • 以下应用层协议都是基于传输层的 TCP 协议来实现的:
协议说明
HTTP超文本传输协议
HTTPS安全数据传输协议
SSH安全外壳协议
TELNET远程终端协议
FTP文件传输协议
SMTP电子邮件传输协议

🌈 九、TCP 与 UDP

⭐ 1. TCP 与 UDP 的对比

  • 是否保证可靠性不能用来判断这两个协议谁好谁坏,而应该作为一种特点看待。
  • TCP 保证可靠性的前提是引入前面那一堆用来保证可靠性的机制,需要比 UDP 做更多的工作。
协议应用场景
TCP用于需要保证可靠传输的情况,应用于文件传输、重要状态更新等场景
UDP用于对高速传输和实时性要求较高的通信领域,如早期的 QQ、视频传输等
  • 一般而言,如果对可靠性有点要求,或者自己都不知道选择什么协议时,选 TCP 就行。

⭐ 2. 如何用 UDP 实现可靠传输 (面试题)

  • 当被问到这个问题时,一定要想到 TCP 协议是如何保证可靠性的。
  • 想要让 UDP 也能变得可靠,只要选择性为 UDP 添加 TCP 的保证可靠的机制即可。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/pingmian/59986.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【Linux杂货铺】IO多路复用

目录 &#x1f308;前言&#x1f308; &#x1f4c1; 五种IO模型 &#x1f4c2; 阻塞IO &#x1f4c2; 非阻塞IO &#x1f4c2; 信号驱动IO &#x1f4c2; 多路复用 &#x1f4c2; 异步IO &#x1f4c1; 非阻塞IO实现 &#x1f4c1; select &#x1f4c2; 接口使用 &#x…

Kafka 的一些问题,夺命15连问

kafka-中的组成员 kafka四大核心 生产者API 允许应用程序发布记录流至一个或者多个kafka的主题&#xff08;topics&#xff09;。 消费者API 允许应用程序订阅一个或者多个主题&#xff0c;并处理这些主题接收到的记录流 StreamsAPI 允许应用程序充当流处理器&#xff08;s…

ANNOVAR下载

1.官网 https://annovar.openbioinformatics.org/en/latest/user-guide/startup/ 都填英文 要不然会报错 tar -xzvf annovar.latest.tar.gztree . ├── annotate_variation.pl ├── coding_change.pl ├── convert2annovar.pl ├── example │ ├── ex1.avinput…

集群架构中Lua脚本的限制以及出现的报错

&#x1f680; 博主介绍&#xff1a;大家好&#xff0c;我是无休居士&#xff01;一枚任职于一线Top3互联网大厂的Java开发工程师&#xff01; &#x1f680; &#x1f31f; 在这里&#xff0c;你将找到通往Java技术大门的钥匙。作为一个爱敲代码技术人&#xff0c;我不仅热衷…

大语言模型:解锁自然语言处理的无限可能

0.引言 在当今的科技时代&#xff0c;自然语言处理技术正以前所未有的速度发展&#xff0c;语言大模型作为其中的核心力量&#xff0c;对各个领域产生了深远的影响。本文旨在探讨语言大模型的发展历程、核心技术以及广泛的应用场景&#xff0c;以帮助读者更好地理解这一前沿技…

MATLAB实现智能水滴算法(Intelligent Water Drops Algorithm, IWDA)

1.智能水滴算法介绍 智能水滴算法&#xff08;Intelligent Water Drops Algorithm&#xff0c;IWDA&#xff09;是一种基于水滴特性的智能优化算法&#xff0c;它借鉴了水滴在自然界中的运动和形态变化规律&#xff0c;通过模拟水滴的形成、发展和消亡过程&#xff0c;实现问题…

【计网】基于TCP协议的Echo Server程序实现与多版本测试

目录 前言&#xff1a; 1、InitServer类的实现 1.1. 创建流式套接字 1.2. bind 绑定一个固定的网络地址和端口号 1.3.listen监听机制 1.4.完整代码 2. 循环接收接口与服务接口 2.1.accept函数讲解 讲个商场拉客的故事方便我们理解&#xff1a; 2.2.服务接口实现 3.服…

easyexcel实现自定义的策略类, 最后追加错误提示列, 自适应列宽,自动合并重复单元格, 美化表头

easyexcel实现自定义的策略类, 最后追加错误提示列, 自适应列宽,自动合并重复单元格, 美化表头 原版表头和表体字体美化自动拼接错误提示列自适应宽度自动合并单元格使用Easyexcel使用poi导出 在后台管理开发的工作中,离不开的就是导出excel了. 如果是简单的导出, 直接easyexce…

边缘计算的学习

文章目录 概要何为边缘计算&#xff1f;现阶段&#xff0c;企业使用边缘计算相对云计算 整体架构流程边缘网络组件边缘计算与云安全 研究方向结合引用 概要 edge 何为边缘计算&#xff1f; 边缘计算&#xff08;英语&#xff1a;Edge computing&#xff09;&#xff0c;是一种…

SpringBoot在城镇保障性住房管理中的应用

1系统概述 1.1 研究背景 随着计算机技术的发展以及计算机网络的逐渐普及&#xff0c;互联网成为人们查找信息的重要场所&#xff0c;二十一世纪是信息的时代&#xff0c;所以信息的管理显得特别重要。因此&#xff0c;使用计算机来管理城镇保障性住房管理系统的相关信息成为必然…

算法|牛客网华为机试41-52C++

牛客网华为机试 上篇&#xff1a;算法|牛客网华为机试21-30C 文章目录 HJ41 称砝码HJ42 学英语HJ43 迷宫问题HJ44 SudokuHJ45 名字的漂亮度HJ46 截取字符串HJ48 从单向链表中删除指定值的节点HJ50 四则运算HJ51 输出单向链表中倒数第k个结点HJ52 计算字符串的编辑距离 HJ41 称砝…

粒子群优化双向深度学习!PSO-BiTCN-BiGRU-Attention多输入单输出回归预测

粒子群优化双向深度学习&#xff01;PSO-BiTCN-BiGRU-Attention多输入单输出回归预测 目录 粒子群优化双向深度学习&#xff01;PSO-BiTCN-BiGRU-Attention多输入单输出回归预测效果一览基本介绍程序设计参考资料 效果一览 基本介绍 1.Matlab实现PSO-BiTCN-BiGRU-Attention粒子…

「Mac玩转仓颉内测版1」入门篇1 - Cangjie环境的搭建

本篇详细介绍在Mac系统上快速搭建Cangjie开发环境的步骤&#xff0c;涵盖VSCode的下载与安装、Cangjie插件的离线安装、工具链的配置及验证。通过这些步骤&#xff0c;确保开发环境配置完成&#xff0c;为Cangjie项目开发提供稳定的基础支持。 关键词 Cangjie开发环境搭建VSC…

协程6 --- HOOK

文章目录 HOOK 概述链接运行时动态链接 linux上的常见HOOK方式修改函数指针用户态动态库拦截getpidmalloc 第一版malloc 第二版malloc/free通过指针获取到空间大小malloc 第三版strncmp 内核态系统调用拦截堆栈式文件系统 协程的HOOK HOOK 概述 原理&#xff1a;修改符号指向 …

ResNet 残差网络 (乘法→加法的思想 - 残差连接是所有前沿模型的标配) + 代码实现 ——笔记2.16《动手学深度学习》

目录 前言 0. 乘法变加法的思想 1. 函数类 2. 残差块 (讲解代码) QA: 残差这个概念的体现&#xff1f; 3. ResNet模型 (代码讲解) 补充&#xff1a;更多版本的ResNet 4. 训练模型 5. 小结 6. ResNet的两大卖点 6.1 加深模型可以退化为浅层模型 6.2 用加法解决梯度消…

iphone怎么删除重复的照片的新策略

Phone用户常常面临存储空间不足的问题&#xff0c;其中一个主要原因是相册中的重复照片。这些重复项不仅占用了大量的存储空间&#xff0c;还会影响设备的整体性能。本文将向您展示iphone怎么删除重复的照片的方法&#xff0c;包括一些利用工具来自动化这个过程的创新方法。 识…

软件缺陷等级评定综述

1. 前言 正确评估软件缺陷等级&#xff0c;在项目的生命周期中有着重要的作用&#xff1a; 指导缺陷修复的优先级和资源分配 在软件开发和维护过程中&#xff0c;资源&#xff08;包括人力、时间和资金&#xff09;是有限的。通过明确缺陷的危险等级&#xff0c;可以帮助团队合…

【Pikachu】Cross-Site Scripting跨站脚本攻击实战

只管把目标定在高峰&#xff0c;人家要笑就让他去笑&#xff01; 1.XSS&#xff08;跨站脚本&#xff09;概述 XSS&#xff08;跨站脚本&#xff09;概述 Cross-Site Scripting 简称为“CSS”&#xff0c;为避免与前端叠成样式表的缩写"CSS"冲突&#xff0c;故又称…

【SpringBoot】 黑马大事件笔记-day2

目录 用户部分 实体类属性的参数校验 更新用户密码 文章部分 规定josn日期输出格式 分组校验 上期回顾&#xff1a;【SpringBoot】 黑马大事件笔记-day1 用户部分 实体类属性的参数校验 对应的接口文档&#xff1a; 基本信息 请求路径&#xff1a;/user/update 请求方式&#…

大数据面试题--kafka夺命连环问

1、kafka消息发送的流程&#xff1f; 在消息发送过程中涉及到两个线程&#xff1a;一个是 main 线程和一个 sender 线程。在 main 线程中创建了一个双端队列 RecordAccumulator。main 线程将消息发送给双端队列&#xff0c;sender 线程不断从双端队列 RecordAccumulator 中拉取…