【网络篇】TCP知识

TCP首部格式?

2a47f1dd0a954546a2ba2e9e343d5be7.png

为什么需要 TCP 协议? TCP 工作在哪一层?

IP 层是不可靠的,它不保证网络包的交付、不保证网络包的按序交付也不保证网络包中的数据的完整性。如果需要保障网络数据包的可靠性,那么就需要由上层(传输层)的 TCP 协议来负责。

什么是 TCP ?

TCP 是 面向连接的、可靠的、基于字节流的传输层通信协议。
  • 面向连接:只能一对一,不能一对多。
  • 可靠的:无论网络链路中出现了怎样的链路变化,都可以保证一个报文一定能够到达接收端。
  • 字节流:需要指出消息的边界,否则接收端无法读出有效用户信息。

什么是 TCP 连接?

用于保证可靠性和流量控制维护的某些状态信息,这些信息的组合,包括 Socket、序列号和窗口大小称为连接。

如何唯一确定一个 TCP 连接呢?

TCP 四元组可以唯一确定一个连接,四元组包括如下:
  • 源地址
  • 源端口
  • 目的地址
  • 目的端口

有一个 IP 的服务端监听了一个端口,它的 TCP 的最大连接数是多少?

最大 TCP 连接数 = 客户端的 IP 数 X 客户端的端口数

对 IPv4,客户端的 IP 数最多为 2 的 32 次方,客户端的端口数最多为 2 的 16 次方,也就
是服务端单机最大 TCP 连接数,约为 2 的 48 次方。
实际上,服务端最大并发 TCP 连接数远不能达到理论上限,会受文件描述符限制、内存限制这些因素的影响。

UDP 和 TCP 有什么区别呢?分别的应用场景是?

区别:
1. 连接 :TCP 是面向连接的,UDP面向无连接。
2. 服务对象 :TCP 是一对一的,UDP 支持一对一、一对多、多对多的交互通信。
3. 可靠性 :TCP 是可靠交付数据的,UDP 是尽最大努力交付,不保证可靠交付数据。
4. 拥塞控制、流量控制 :TCP 有拥塞控制和流量控制机制,UDP 则没有。
5. 首部开销 :TCP 首部长度为20字节,且长度不固定,如果使用了选项字段长度会变长。UDP 首部只有 8 个字节,且长度固定,开销较TCP小。
6. 传输方式 :TCP 是流式传输,没有边界,但保证顺序和可靠。 UDP 是一个包一个包的发送,是有边界的,但可能会丢包和乱序。
7. 分片不同 :TCP 的数据大小如果大于 MSS 大小,则会在传输层进行分片,目标主机收到后,也同样在传输层组装 TCP 数据包,如果中途丢失了一个分片,只需要传输丢失的这个分片。 UDP 的数据大小如果大于 MTU 大小,则会在 IP 层进行分片,目标主机收到后,在 IP 层组装完数据,接着再传给传输层。
应用场景:
由于 TCP 面向连接,能保证数据的可靠性交付,因此经常用于 FTP 文件传输、HTTP / HTTPS;
由于 UDP 面向无连接,它可以随时发送数据,再加上 UDP 本身的处理既简单又高效,因此经常
用于包总量较少的通信、视频、音频等多媒体通信以及广播通信。

为什么 UDP 头部没有首部长度字段,而 TCP 头部有首部长度字段呢?

原因是 TCP 有可变长的选项字段,而 UDP 首部长度是不会变化的,无需多一个字段去记录UDP的首部长度。

为什么 UDP 头部有包长度字段,而 TCP 头部则没有包长度字段呢?

TCP 数据的长度 = IP总长度 - IP首部长度 - TCP首部长度,其中 IP 总长度 和 IP 首部长度,在 IP 首部格式是已知的。TCP 首部长度在 TCP 首部格式已知的,所以就可以求得 TCP 数据的长度。

TCP 和 UDP 可以使用同一个端口吗?

可以,TCP 和 UDP 在内核中是两个完全独立的软件模块,TCP 和 UDP 各自的端口号是相互独立的,如 TCP 有一个 80 号端口,UDP 也可以有一个 80 号端口,二者并不冲突。

TCP 三次握手过程是怎样的?

  • 一开始,客户端和服务端都处于 CLOSE 状态。先是服务端主动监听某个端口,处于 LISTEN 状态。
  • 客户端会随机初始化序号(client_isn),同时将 SYN 标志位置为 1 ,表示 SYN 报文。接着把第一个 SYN 报文发送给服务端,表示向服务端发起连接,该报文不包含应用层数据,之后客户端处于 SYN-SENT 状态。
  • 服务端收到客户端的 SYN 报文后,首先服务端也随机初始化自己的序号(server_isn),然后把 TCP 首部的确认应答号字段填入client_isn + 1 , 接着把 SYN 和 ACK 标志位置为 1 。最后把该报文发给客户端,该报文也不包含应用层数据,之后服务端处于 SYN-RCVD 状态。
  • 客户端收到服务端报文后,还要向服务端回应最后一个应答报文,首先该应答报文 TCP 首部 ACK 标志位置为 1 ,其次确认应答号字段填入 server_isn + 1 ,最后把报文发送给服务端,这次报文可以携带客户到服务端的数据,之后客户端处于 ESTABLISHED 状态。
  • 服务端收到客户端的应答报文后,也进入 ESTABLISHED 状态。
注:第三次握手是可以携带数据的,前两次握手是不可以携带数据的。
一旦完成三次握手,双方都处于 ESTABLISHED 状态,此时连接就已建立完成,客户端和服务端就 可以相互发送数据了。
在 Linux 下可以通过 netstat -napt 命令查看 TCP 的连接状态。

为什么是三次握手?不是两次、四次?

三个原因:

  1. 防止历史连接的建立(主要原因)
  2. 帮助双方同步初始化序列号
  3. 减少双方不必要的资源开销
不使用两次握手和四次握手的原因:
  • 两次握手:无法防止历史连接的建立,会造成双方资源的浪费,也无法可靠的同步双方序列号
  • 四次握手:三次握手就能保证建立可靠连接,所以不需要使用更多的通信次数
1.为什么两次握手无法阻止历史连接呢?
在两次握手的情况下,服务端没有中间状态给客户端来阻止历史连接,导致服务端可能建立一个历史连接,造成资源浪费。
在两次握手的情况下,服务端在收到 SYN 报文后,就进入 ESTABLISHED 状态,意味着这时可以给对方发送数据,但是客户端此时还没有进入 ESTABLISHED 状态,假设这次是历史连接, 客户端判断到此次连接为历史连接,那么就会回 RST 报文来断开连接,但如果在此之前服务端就建立了历史连接,这样就会导致服务端白白浪费一个资源。
2.同步双方初始序列号
TCP 协议的通信双方, 都必须维护一个序列号,当客户端发送携带初始序列号的SYN 报文时,需要服务端回一个 ACK 应答报文,表示客户端的 SYN 报文已被服务端成功接收,那当服务端发送初始序列号给客户端的时候,依然也要得到客户端的应答回应, 这样一来一回,才能确保双方的初始序列号能被可靠的同步。
四次握手其实也能够可靠的同步双方的初始化序号,但没必要;两次握手只保证了一方的初始序列号能被对方成功接收,没办法保证双方的初始序列号都能被确认接收。

3.避免资源浪费

如果只有两次握手,当客户端发送的 SYN 报文在网络中阻塞,客户端没有接收到 ACK 报文,就会重新发送 SYN ,由于没有第三次握手,服务端不清楚客户端是否收到了自己回复的ACK 报文,所以服务端每收到一个 SYN 就只能先主动建立一个连接,如果此时客户端由于网络阻塞重复发送多次 SYN 报文,那么服务端在收到请求后就会建立多个冗余的无效链接,造成不必要的资源浪费

为什么每次建立 TCP 连接时,初始化的序列号都要求不一样呢?

两个原因:
  • 为了防止历史报文被下一个相同四元组的连接接收(主要方面);
  • 为了安全性,防止黑客伪造的相同序列号的 TCP 报文被对方接收;

初始序列号 ISN 是如何随机产生的?

起始 ISN 是基于时钟的,每 4 微秒 + 1,转一圈要 4.55 个小时。
ISN = M + F
  • M 是一个计时器,这个计时器每隔 4 微秒加 1。
  • F 是一个 Hash 算法,根据源 IP、目的 IP、源端口、目的端口生成一个随机数值。

既然 IP 层会分片,为什么 TCP 层还需要 MSS 呢?

IP 层不保证可靠,如果一个 IP 分片丢失,整个报文的所有分片都需要重传。

第一次握手丢失了,会发生什么?

如果第一次握手丢失了,导致客户端迟迟收不到服务端的 SYN-ACK 报文, 客户端就会触发 超时重传机制,重传 SYN 报文,而且重传的 SYN 报文的序列号都是一样的。
当客户端达到最大重传次数时(最大重传次数由内核参数决定),会再等待一段时间(上次超时时间的2倍),如果还是没有收到服务端的第二次握手,会直接断开连接。

第二次握手丢失了,会发生什么?

当第二次握手丢失了,客户端和服务端都会发生超时重传

第三次握手丢失了,会发生什么?

当第三次握手丢失了,服务端会发生超时重传

注:ACK 报文是不会有重传的,当 ACK 丢失了,就由对方重传对应的报文。

什么是 SYN 攻击?如何避免 SYN 攻击?

SYN 攻击:攻击者在短时间伪造不同 IP 地址的 SYN 报文,服务端每接收到一个 SYN 报文,就进入 SYN_RCVD 状态,但服务端发送出去的 ACK + SYN 报文, 无法得到未知 IP 主机的 ACK 应答,久而久之就会 占满服务端的 TCP 半连接队列,当 TCP 半连接队列满了,后续再收到 SYN 报文就会丢弃,导致客户端无法和服务端建立连接。
  • 半连接队列,也称 SYN 队列(收到 SYN 但没有收到 ACK)
  • 全连接队列,也称 accept 队列(收到 ACK)

避免方法:

  1. 增大 TCP 半连接队列;
  2. 开启 tcp_syncookies;
  3. 减少 SYN+ACK 重传次数

TCP 四次挥手过程是怎样的?

  • 客户端打算关闭连接,此时会发送 FIN 报文,之后客户端进入 FIN_WAIT_1 状态
  • 服务端收到该报文后,就向客户端发送 ACK 应答报文,接着服务端进入 CLOSE_WAIT 状态
  • 客户端收到服务端的 ACK 应答报文后,之后进入 FIN_WAIT_2 状态
  • 等待服务端处理完数据后,也向客户端发送 FIN 报文,之后服务端进入 LAST_ACK 状态
  • 客户端收到服务端的 FIN 报文后,回一个 ACK 应答报文,之后进入 TIME_WAIT 状态
  • 服务端收到了 ACK 应答报文后,就进入了 CLOSE 状态,至此服务端已经完成连接的关闭
  • 客户端在经过 2MSL 一段时间后,自动进入 CLOSE 状态,至此客户端也完成连接的关闭

为什么挥手需要四次?

服务端通常需要等待完成数据的发送和处理,所以服务端的 ACK 和 FIN 一般都会分开发送,因此是需要四次挥手。

双方都可以主动断开连接,只有主动断开连接的一方,才有TIME_WAIT 状态。

第一次挥手丢失了,会发生什么?

客户端超时重传。如果达到最大重传次数,还没有收到服务端的 ACK,则自动进入 CLOSE 状态

第二次挥手丢失了,会发生什么?

客户端超时重传。

第三次挥手丢失了,会发生什么?

服务端超时重传。

第四次挥手丢失了,会发生什么?

服务端超时重传。

为什么 TIME_WAIT 等待的时间是 2MSL?

若 ACK 在一个 MSL 内丢失,那么被动方重发的 FIN 会在第 2 个 MSL 内到达,这样相当于至少允许报文丢失一次。

为什么需要 TIME_WAIT 状态?

  • 防止历史连接中的数据,被后面相同四元组的连接错误的接收
  • 保证被动关闭连接的一方,能被正确的关闭

TIME_WAIT 过多有什么危害?

一是占用系统资源,二是占用端口资源。

如何优化 TIME_WAIT?

  • 复用处于 TIME_WAIT 的 socket 为新连接所用。
  • 当系统中处于 TIME_WAIT 的连接超过最大值时,系统就会将后面的 TIME_WAIT 连接状态重置。
  • 跳过 TIME_WAIT 状态,当调用 close 后,会立即发送一个 RST 标志给对端,直接关闭。

服务器出现大量 TIME_WAIT 状态的原因有哪些?

服务器主动断开了很多 TCP 连接。
场景:
  • HTTP 没有使用长连接
  • HTTP 长连接超时
  • HTTP 长连接的请求数量达到上限

服务器出现大量 CLOSE_WAIT 状态的原因有哪些?

服务端的程序没有调用 close 函数关闭连接。

如果已经建立了连接,但是客户端突然出现故障了怎么办?

TCP 存在保活机制,超过保活时间,会定期发送探测报文,如果连续几个都没回应,则断连。

web 服务软件一般都会提供 keepalive_timeout 参数,用来指定 HTTP 长连接的超时时间。如果设置了 HTTP 长连接的超时时间是 60 秒,web 服务软件就会启动一个定时器,如果客户端 60 秒内没有发起新的请求,定时器时间一到,就会触发回调函数来释放该连接。

如果已经建立了连接,但是服务端的进程崩溃会发生什么?

TCP 的连接信息是由内核维护的,所以当服务端的进程崩溃后,内核需要回收该进程的所有 TCP连接资源,于是内核会发送第一次挥手 FIN 报文,后续的挥手过程也都是在内核完成,并不需要 进程的参与,所以即使服务端的进程退出了,还是能与客户端完成 TCP 四次挥手的过程。

服务端没有listen,客户端发起连接,会发生什么?

服务端回复 RST 报文。

listen 时候参数 backlog 的意义?

通常认为 backlog 是 accept 队列。

accept 发生在三次握手的哪一步?

客户端 connect 成功返回是在第二次握手,服务端 accept 成功返回是在三次握手成功之后。

没有 accept,能建立 TCP 连接吗?

可以。建立连接的过程不需要accept参与,执行accept只是为了从全连接队列队列中取出一条连接

没有 listen,能建立 TCP 连接吗?

可以。客户端可以自己连接自己,也可以两个客户端同时向对方发出请求建立连接,这两个情况都是在没有listen情况下就能建立连接。

重传机制

  • 超时重传

在发送数据时,设置一个定时器,当超过指定时间后,没有收到对方的应答报文,就重传。

  • 快速重传

发送端连续收到三个 ACK,就立即重传,不用等待定时器超时。

  • SACK

选择性确认,在 TCP 头部选项字段中加入 SACK,它可以将已收到的数据的信息发送给发送方,这样发送方就可以知道哪些数据收到了,哪些数据没收到,这样就可以只重传丢失的数据。

  • D-ACK
使用了 SACK 来告诉「发送方」有哪些数据被重复接收了。

滑动窗口

窗口大小就是指无需等待确认应答,而可以继续发送数据的最大值。

通常窗口的大小是由接收方的窗口大小来决定的。

流量控制

可以让发送方根据接收方的实际接收能力控制发送的数据量。

窗口关闭

如果窗口大小为 0 ,会阻止发送方给接收方传递数据,直至窗口变为非 0 为止。

糊涂窗口综合征

如果接收方腾出几个字节并告诉发送方现在有几个字节的窗口,而发送方会义无反顾地发送这几个字节,这就是糊涂窗口综合症。

如何避免?

需要接收方满足不通告小窗口给发送方同时发送方开启 Nagle 算法。

  • 注:如果接收方不满足不通告小窗口给发送方,即使开了 Nagle 算法,也无法避免糊涂窗口综合征,如果对端 ACK 回复很快的话,Nagle 算法就不会拼接太多的数据包,这种情况下依然会有小数据包的传输,网络总体利用率依然很低。
Nagle 算法的思路是延时处理,只有满足下面两个条件中的任意一个条件,才可以发送数据
  • 条件一:要等到窗口大小 >= MSS 并且 数据大小 >= MSS ;
  • 条件二:收到对端 ACK;
Nagle 算法默认是打开的,如果对于一些需要小数据包交互的场景的程序,比如,telnet 或 ssh 这样的交互性比较强的程序,则需要关闭 Nagle 算法。
可以在 socket 设置 TCP_NODELAY 选项来关闭这个算法:
setsockopt(sock_fd, IPPROTO_TCP, TCP_NODELAY, (char *)&value, sizeof(int));

拥塞控制

只要发送方没有在规定时间内接收到 ACK 应答报文,也就是发生了超时重传,就会认为
网络出现了拥塞。
拥塞控制四种算法:
  • 慢启动
  • 拥塞避免
  • 拥塞发生
  • 快速恢复

慢启动:

慢启动算法,发包的个数是指数性的增长。 

当 cwnd < ssthresh 时,使用慢启动算法。
当 cwnd >= ssthresh 时,就会使用拥塞避免算法。

拥塞避免:

由慢启动的指数增长变为线性增长,达到一定程度,网络发生拥塞,触发重传机制,进入拥塞发生算法。

拥塞发生:

重传机制主要有两种:
  • 超时重传
  • 快速重传

发生超时重传的拥塞发生算法:

  • ssthresh 设为 cwnd/2;
  • cwnd 恢复到初始值;
发生快速重传的拥塞发生算法:
  • cwnd = cwnd/2;
  • ssthresh = cwnd;
  • 进入快速恢复算法;

如何优化TCP?

TCP 三次握手性能提升

  1. 客户端优化:修改 SYN 重传次数。
  2. 服务端优化:修改 SYN+ACK 重传次数;修改半连接队列大小;修改全连接队列大小;当半连接队列满了开启 syncookie 功能;当全连接队列满了用 RST 通知客户端建立连接失败。
  3. 绕过三次握手。

如何绕过三次握手?

使用 TCP Fast Open(TFO)功能,以减少 TCP 连接建立的时延。
工作过程:
  • 客户端发送 SYN 报文,该报文包含 Fast Open 选项,且该选项的 Cookie 为空,这表明客户端请求 Fast Open Cookie;
  • 支持 TCP Fast Open 的服务器生成 Cookie,并将其置于 SYN-ACK 数据包中的 Fast Open 选项以发回客户端;
  • 客户端收到 SYN-ACK 后,本地缓存 Fast Open 选项中的 Cookie。
所以,第一次发起 HTTP GET 请求的时候,还是需要正常的三次握手流程。
之后,如果客户端再次向服务器建立连接时的过程:
  • 客户端发送 SYN 报文,该报文包含数据以及此前记录的 Cookie;
  • 支持 TCP Fast Open 的服务器会对收到 Cookie 进行校验:如果 Cookie 有效,服务器将在 SYN-ACK 报文中对 SYN 和数据进行确认,服务器随后将数据递送至相应的应用程序;如果 Cookie 无效,服务器将丢弃 SYN 报文中包含的数据,且其随后发出的 SYN-ACK 报文将只确认 SYN 的对应序列号;
  • 如果服务器接受了 SYN 报文中的数据,服务器可在握手完成之前发送数据,这就减少了握手带来的 1 个 RTT 的时间消耗;
  • 客户端将发送 ACK 确认服务器发回的 SYN 以及数据,但如果客户端在初始的 SYN 报文中发送的数据没有被确认,则客户端将重新发送数据;
所以,之后发起 HTTP GET 请求的时候,可以绕过三次握手,这就减少了握手带来的 1 个 RTT 的
时间消耗。

TCP 四次挥手性能提升

  1. 主动方的优化:修改 FIN 报文重传次数;调整 TIME_WAIT 状态的上限个数,一旦超过,直接释放;复用处于 TIME_WAIT 状态的连接(只适用于客户端);调整 TIME_WAIT2 状态的时间(只适用于 close 关闭的连接);调整孤儿连接(调用 close 关闭的连接)的上限个数(只适用于 close 关闭的连接),一旦超过,将不再走四次挥手,直接发送 RST 复位关闭。
  2. 被动方的优化:如果出现大量 CLOSE_WAIT 状态,从程序中找原因(考虑未调用 close 函数);修改 FIN 报文重传次数;

TCP 传输数据的性能提升

扩大窗口大小;调整发送缓冲区范围;调整接收缓冲区范围;打开接受缓冲区动态调节;调整内存范围。

如何理解 TCP 是面向字节流的协议?

之所以会说 TCP 是面向字节流的协议,UDP 是面向报文的协议,是因为操作系统对 TCP 和 UDP 协议的发送机制不同。对于 UDP,操作系统不会对消息进行拆分,每个 UDP 报文就是一个用户消息的边界;而对于 TCP,操作系统可能会对消息进行分组。

如何解决粘包?

粘包问题的出现是因为不知道用户消息的边界,如果知道边界在哪,接收方就可以通过边界来划分出有效的用户消息。

解决:

  • 固定长度的消息
  • 特殊字符作为边界
  • 自定义消息结构(由包头和数据组成,包头大小固定,包头中包含数据长度)

SYN 报文什么时候情况下会被丢弃?

  • 开启 tcp_tw_recycle 参数,并且在 NAT 环境下,造成 SYN 报文被丢弃
  • TCP 两个队列满了(半连接队列和全连接队列),造成 SYN 报文被丢弃
Linux 提供了两个系统参数来快速回收处于 TIME_WAIT 状态的连接,这两个参数默认关闭:
  • net.ipv4.tcp_tw_reuse,如果开启该选项的话,客户端(连接发起方) 在调用 connect() 函数时,如果内核选择到的端口已经被相同四元组的连接占用的时候,就会判断该连接是否处于 TIME_WAIT 状态,如果该连接处于 TIME_WAIT 状态并且 TIME_WAIT 状态持续的时间超过了 1 秒,那么就会重用这个连接,然后就可以正常使用该端口了。所以该选项只适用于连接发起方。
  • net.ipv4.tcp_tw_recycle,如果开启该选项的话,允许处于 TIME_WAIT 状态的连接被快速回收;
使这两个选项生效的前提是要打开 TCP 时间戳(默认开启),即 net.ipv4.tcp_timestamps=1

第一种场景,对于服务器来说,如果同时开启了recycle 和 timestamps 选项,则会开启一种称之为per-host 的 PAWS 机制。

什么是 PAWS 机制?

tcp_timestamps 选项开启之后, PAWS 机制会自动开启,它的作用是防止 TCP 包中的序列号发生绕回。在开启 tcp_timestamps 选项情况下,一台机器发的所有 TCP 包都会带上发送时的时间戳,PAWS 要求连接双方维护最近一次收到的数据包的时间戳(Recent TSval),每收到一个新数据包都会读取数据包中的时间戳值跟 Recent TSval 值做比较, 如果发现收到的数据包中时间戳不是递增的,则表示该数据包是过期的,就会直接丢弃这个数据包
什么是 per-host 的 PAWS 机制?
当开启 recycle 和 timestamps 选项时,就会开启一种叫 per-host 的 PAWS 机制。 perper-host 是对相同的 IP 做 PAWS 检查,而不是对 TCP 四元组进行检查。
当客户端 A 通过 NAT 网关和服务器建立 TCP 连接,然后服务器主动关闭并且快速回收 TIME-WAIT 状态的连接后,客户端 B 也通过 NAT 网关和服务器建立 TCP 连接,注意客户端 A 和 客户
端 B 因为经过相同的 NAT 网关,所以是用相同的 IP 地址与服务端建立 TCP 连接,如果客户端 B
的 timestamp 比客户端 A 的 timestamp 小,那么由于服务端的 per-host 的 PAWS 机制的作用, 服务端就会丢弃客户端主机 B 发来的 SYN 包。

 tcp_tw_recycle 在 Linux 4.12 版本后,直接取消了这一参数。

已建立连接的TCP,收到SYN会发生什么?

问题:一个已经建立的 TCP 连接,客户端中途宕机了,而服务端此时也没有数据要发送,
一直处于 Established 状态,客户端恢复后,向服务端建立连接,此时服务端会怎么处理?

1. 客户端的 SYN 报文里的端口号与历史连接不相同

  • 如果客户端恢复后,发送的 SYN 报文中的源端口号跟上一次连接的源端口号不一样,此时服务端会认为是新的连接要建立,于是就会通过三次握手来建立新的连接。
2. 客户端的 SYN 报文里的端口号与历史连接相同
  • 如果客户端恢复后,发送的 SYN 报文中的源端口号跟上一次连接的源端口号一样(注意此时的 SYN 报文是乱序的,因为 SYN 报文的初始化序列号是一个随机数),服务端会回复一个携带了正确序列号和确认号的 ACK 报文,这个 ACK 被称之为 Challenge ACK。 接着,客户端收到这个 Challenge ACK,发现确认号并不是自己期望收到的,于是就会回 RST 报文,服务端收到后,就会释放掉该连接。

如何关闭一个 TCP 连接?

1.简单粗暴:杀掉进程。在客户端杀掉进程只会影响当前客户端,在服务端杀掉进程会导致所有 TCP 连接都被关闭。
2.伪造一个能关闭 TCP 连接的 RST 报文。
要伪造一个能关闭 TCP 连接的 RST 报文,必须同时满足以下两个条件:
  • 四元组相同
  • 序列号是对方期望的
两种关闭 TCP 连接的工具:tcpkill 和 killcx 工具。
这两种工具都是通过伪造 RST 报文来关闭 TCP 连接的,但是它们获取对方下一次期望收到的序
列号的方式是不同的,也正因此,造就了这两个工具的应用场景有区别。
  • tcpkill 工具只能用来关闭活跃的 TCP 连接,无法关闭非活跃的 TCP 连接,因为 tcpkill 工具是等双方进行 TCP 通信后,才去获取正确的序列号,如果这条 TCP 连接一直没有任何数据传输,则永远获取不到正确的序列号。
  • killcx 工具可以用来关闭活跃和非活跃的 TCP 连接,因为 killcx 工具是主动发送 SYN 报文,这时对方就会回复 Challenge ACK ,然后 killcx 工具就能从这个 ACK 获取到正确的序列号。

四次挥手中收到乱序的 FIN 包会如何处理?

在 FIN_WAIT_2 状态时,如果收到乱序的 FIN 报文,那么就被会加入到乱序队列,并不会进
入到 TIME_WAIT 状态。 等再次收到前面被网络延迟的数据包时,会判断乱序队列有没有数据,然后会检测乱序队列中是否有可用的数据,如果能在乱序队列中找到与当前报文的序列号保持序列号连续的报文,就会看该报文是否有 FIN 标志,如果发现有 FIN 标志,这时才会进入 TIME_WAIT 状态。

在 TCP 正常挥手过程中,处于 TIME_WAIT 状态的连接,收到相同四元组的 SYN 后会发生什么?

如果双方开启了时间戳机制:
  • 如果客户端的 SYN 的「序列号」比服务端「期望下一个收到的序列号」要并且SYN 的「时间戳」比服务端「最后收到的报文的时间戳」要。那么就会重用该四元组连接,跳过 2MSL 而转变为 SYN_RECV 状态,接着就能进行建立连接过程。
  • 如果客户端的 SYN 的「序列号」比服务端「期望下一个收到的序列号」要或者SYN 的「时间戳」比服务端「最后收到的报文的时间戳」要。那么就会再回复一个第四次挥手的 ACK 报文,客户端收到后,发现并不是自己期望收到确认号,就回 RST 报文给服务端

在 TIME_WAIT 状态,收到 RST 会断开连接吗?

  • 如果 net.ipv4.tcp_rfc1337 参数为 0,则提前结束 TIME_WAIT 状态,释放连接(默认情况)
  • 如果 net.ipv4.tcp_rfc1337 参数为 1,则会丢掉该 RST 报文

TCP 连接,一端断电和进程崩溃有什么区别?

问题:在没有开启 TCP keepalive,且双方一直没有数据交互的情况下,如果客户端的主机崩溃/进程崩溃,会发生什么?
主机崩溃:客户端主机崩溃了,服务端是 无法感知到的,在加上服务端没有开启 TCP keepalive,又没有数据交互的情况下, 服务端的 TCP 连接将会一直处于 ESTABLISHED 连接状态,直到服务端重启进程。 由此还可以得出,在没有使用 TCP 保活机制且双方不传输数据的情况下,一方的TCP 连接处在 ESTABLISHED 状态,并不代表另一方的连接还一定正常。
进程崩溃:即使没有开启 TCP keepalive,且双方也没有数据交互的情况下,如果其中一方的进程发生了崩溃,这个过程操作系统是可以感知的到的,于是就会发送 FIN 报文给对方,然后与对方进行 TCP 四次挥手。
有数据传输的场景下的异常情况:
第一种,客户端主机宕机,又迅速重启,会发生什么?
在客户端主机宕机后,服务端向客户端发送的报文会得不到任何的响应,在一定时长后,服务端就会触发 超时重传机制,重传未得到响应的报文。
服务端重传报文的过程中,客户端主机重启完成后,客户端的内核就会接收重传的报文,然后根据报文的信息传递给对应的进程:
  • 如果客户端主机上没有进程绑定该 TCP 报文的目标端口号,那么客户端内核就会回复 RST 报文,重置该 TCP 连接
  • 如果客户端主机上进程绑定该 TCP 报文的目标端口号,由于客户端主机重启后,之前的 TCP 连接的数据结构已经丢失了,客户端内核里协议栈会发现找不到该 TCP 连接的 socket 结构体, 于是就会回复 RST 报文,重置该 TCP 连接
只要有一方重启完成后,收到之前 TCP 连接的报文,都会回复 RST 报文,以断开连接
第二种,客户端主机宕机,一直没有重启,会发生什么?
这种情况,服务端超时重传报文的次数达到一定阈值后,内核就会判定出该 TCP 有问题,然后通过 socket 接口告诉应用程序该 TCP 连接出问题了,于是服务端的 TCP 连接就会断开。

拔掉网线后, 原本的 TCP 连接还存在吗?

有数据传输的情况:
  • 在客户端拔掉网线后,如果服务端发送了数据报文,那么在服务端重传次数没有达到最大值之 前,客户端就插回了网线,那么双方原本的 TCP 连接还是能正常存在,就好像什么事情都没有发生。
  • 在客户端拔掉网线后,如果服务端发送了数据报文,在客户端插回网线之前,服务端重传次数 达到了最大值时,服务端就会断开 TCP 连接。等到客户端插回网线后,向服务端发送了数据, 因为服务端已经断开了与客户端相同四元组的 TCP 连接,所以就会回 RST 报文,客户端收到后就会断开 TCP 连接。至此, 双方的 TCP 连接都断开了。
没有数据传输的情况:
  • 如果双方都没有开启 TCP keepalive 机制,那么在客户端拔掉网线后,如果客户端一直不插回网线,那么客户端和服务端的 TCP 连接状态将会一直保持存在。
  • 如果双方都开启了 TCP keepalive 机制,那么在客户端拔掉网线后,如果客户端一直不插回网 线,TCP keepalive 机制会探测到对方的 TCP 连接没有存活,于是就会断开 TCP 连接。而如果在 TCP 探测期间,客户端插回了网线,那么双方原本的 TCP 连接还是能正常存在。

tcp_tw_reuse 为什么默认是关闭的?

tcp_tw_reuse 的作用是让客户端快速复用处于 TIME_WAIT 状态的端口,相当于跳过了 TIME_WAIT 状态,这可能会出现这样的两个问题:
  • 历史 RST 报文可能会终止后面相同四元组的连接,因为 PAWS 检查到即使 RST 是过期的,也不会丢弃。
  • 如果第四次挥手的 ACK 报文丢失了,有可能被动关闭连接的一方不能被正常的关闭。

HTTPS 中 TLS 和 TCP 能同时握手吗? 

这个场景在特定条件下可以存在:
  • 客户端和服务端都开启了 TCP Fast Open 功能,且 TLS 版本是 1.3;
  • 客户端和服务端已经进行一次通信;
TCP Fast Open + TLSv1.3
客户端和服务端同时支持 TCP Fast Open 功能的情况下,在第二次以后的通信过程中,客户端可以绕过三次握手直接发送数据,而且服务端也不需要等收到第三次握手后才发送数据。
如果 HTTPS 的 TLS 版本是 1.3,那么 TLS 过程只需要 1-RTT。 因此如果 TCP Fast Open + TLSv1.3 情况下,在第二次以后的通信过程中,TLS 和 TCP 的握手过程是可以同时进行的。 如果基于 TCP Fast Open 场景下的 TLSv1.3 0-RTT 会话恢复过程,不仅 TLS 和 TCP 的握手过程是可以同时进行的,而且 HTTP 请求也可以在这期间内一同完成。

TCP Keepalive 和 HTTP Keep-Alive 是一个东西吗?

这两个是完全不一样的东西

  • HTTP 的 Keep-Alive 也叫 HTTP 长连接,该功能是由「应用程序」实现的,可以使得用同一个 TCP 连接来发送和接收多个 HTTP 请求/应答,减少了 HTTP 短连接带来的多次 TCP 连接建立和释放的开销。
  • TCP 的 Keepalive 也叫 TCP 保活机制,该功能是由「内核」实现的,当客户端和服务端长达一定时间没有进行数据交互时,内核为了确保该连接是否还有效,就会发送探测报文,来检测对方是否还在线,然后来决定是否要关闭该连接。

TCP 协议有什么缺陷?

1.升级 TCP 的工作很困难:

TCP 协议是在内核中实现的,应用程序只能使用不能修改,对于升级内核要涉及底层软件和运行库的更新,非常麻烦。

2.TCP 建立连接的延迟:

基于 TCP 实现的应用协议,都是需要先建立三次握手才能进行数据传输,比如 HTTP 1.0/1.1、
HTTP/2、HTTPS。
3.TCP 存在队头阻塞问题:
TCP 是字节流协议,TCP 层必须保证收到的字节数据是完整且有序的,如果序列号较低的 TCP 段
在网络传输中丢失了,即使序列号较高的 TCP 段已经被接收了,应用层也无法从内核中读取到这部分数据。

4.网络迁移需要重新建立 TCP 连接:

基于 TCP 传输协议的 HTTP 协议,由于是通过四元组(源 IP、源端口、目的 IP、目的端口)确定一条 TCP 连接。那么当移动设备的网络从 4G 切换到 WIFI 时,意味着 IP 地址变化了,那么就必须要断开连接,然后重新建立 TCP 连接。 而建立连接的过程包含 TCP 三次握手和 TLS 四次握手的时延,以及 TCP 慢启动的减速过程,给用户的感觉就是网络突然卡顿了一下,因此连接的迁移成本是很高的。

如何基于 UDP 协议实现可靠传输?

QUIC 协议实现可靠传输

在UDP头部和HTTP之间加入两层头部,分别是Packet Header 和 QUIC Frame Header,Packet Header 细分为两种,Long Packet Header 用来首次建立连接,Short Packet Header 用于日常数据传输,QUIC首次也需要三次握手,主要为了协商出 ID。Short Packet Header中的 Packet Number 是每个报文独一无二的编号,它是严格递增的,如果 Packet N 丢失了,重传的编号就不是N了,而是一个比N大的值。

Packet Number 单点递增两个好处:

  • 更加精准计算 RTT,没有TCP重传的歧义性问题(计算RTT的起始时间应该从首次发送开始还是从超时重传时开始呢?);
  • 支持乱序确认(不必像TCP一样必须有序确认,只要有新数据确认,无论之前是否丢包,当前窗口都会右移)

既然 Packet Number 是严格递增的,那么重传数据包与丢失数据包的编号并不一致,那么如何确定包中的内容是否相同呢?

所以引入QUIC Frame Header,通过比较两个数据包的 Stream ID 和 Offset,如果一致,则内容相同。后续再根据这两个字段进行数据包的正确组装。

总结:QUIC 通过单向递增的 Packet Number,配合 Stream ID 与 Offset 字段信息,可以支持乱序确认而不影响数据包的正确组装,摆脱了TCP必须按顺序确认应答,解决TCP因丢包导致后续其他数据包阻塞。

QUIC是如何解决队头阻塞问题?

在http那篇文章中,介绍到了hhtp/1.1解决了hhtp/1.0的发送端队头阻塞,但没有解决接收端队头阻塞,hhtp/2.0利用Stream解决了http层面的队头阻塞,但存在TCP层队头阻塞问题(hhtp/2.0多个Stream共用一个滑动窗口,当数据丢失,滑动窗口无法移动,此时会阻塞所有http请求)

对于QUIC,它给每个Stream都分配了一个独立的滑动窗口,这样使得一个TCP连接上的多个Stream之间相互独立,解决了队头阻塞问题。

总结:通过Stream解决http层的队头阻塞,通过给每个Stream分配一个滑动窗口解决了TCP层队头阻塞问题。

QUIC如何做流量控制?

QUIC实现了两种级别的流量控制,分别是Stream和Connection两种级别:

  • Stream级别的流量控制:Stream可以被认为是一条HTTP请求,每个Stream都有独立的滑动窗口,所以每个Stream都可以做流量控制。
  • Connection流量控制:限制连接中所有Stream相加的总字节数,防止发送方超过连接的缓冲量。

 QUIC对拥塞算法改进

QUIC处在应用层,可以根据不同的应用设置不同的拥塞控制算法,而TCP拥塞控制是基于内核和操作系统支持的,对系统所有应用都生效,无法针对性设置。

 QUIC更快建立连接

QUIC使用TLS1.3,只需一个RTT就可以同时完成建立连接与密钥协商,在第二次时,数据包可以同时和QUIC握手信息(连接信息+TLS信息)一起发送,达到0RTT的效果。

QUIC如何迁移连接?

QUIC通过连接ID确定连接,客户端和服务端可以各自选择一组ID来标记自己,即使后面网络发生变化(如4G切换到WIFI)导致IP发生变化,仍可以无缝复用原连接。

TCP和UDP可以使用同一个端口吗?

TCP和UDP能绑定同一个端口吗?

可以。传输层有两个传输协议分别是TCP和UDP,在内核中是两个完全独立的模块,当主机收到数据包以后,可以根据IP包头的协议号确定是TCP还是UDP,进而决定交给哪个模块(TCP/UDP)处理,送给TCP/UDP的模块会根据端口号确定给哪个应用进程处理。(TCP、UDP的端口号之间也相互独立,如TCP 80 端口和UDP 80 端口并不冲突)

多个TCP进程可以绑定绑定同一个端口吗?

如果两个 TCP 服务进程同时绑定相同的IP地址和端口号,那么执行bind的时候会出错,错误为“Address already in use”;如果绑定端口相同,但IP地址不同,那么执行bind不会报错。

如何解决服务端重启,报错“Address already in use”的问题?

当我们重启 TCP 服务进程的时候,意味着通过服务器端发起了关闭连接操作,于是就会经过四次
挥手,而对于主动关闭方,会在 TIME_WAIT 这个状态里停留一段时间,这个时间大约为 2MSL
TCP 服务进程重启时,服务端会出现 TIME_WAIT 状态的连接, TIME_WAIT 状态的连接使用的 IP+PORT 仍然被认为是一个有效的 IP+PORT 组合,相同机器上不能够在该 IP+PORT 组合上进行绑定,那么执行 bind() 函数的时候,就会返回了 Address already in use 的错误。
解决办法:对 socket 设置 SO_REUSEADDR 属性
这样即使存在一个和绑定 IP+PORT 一样的 TIME_WAIT 状态的连接,依然可以正常绑定成功,因此可以正常重启成功。
客户端的端口可以重复使用吗?
客户端在执行connect的时候,会在内核中随机选择一个端口,然后与服务端进行三次握手。由于TCP连接是由四元组唯一确认的,因此只要一个发生变化,就表示是不同的TCP连接。所以可以重复使用。
多个客户端可以bind同一个端口吗?
客户端在connect的时候,是由内核随机挑选的端口号,如果我们想自己指定,可以用bind实现:客户端通过bind绑定一个端口,然后调用connect就会跳过端口选择的过程直接使用bind确定的端口。如果多个客户端同时绑定相同的IP+PORT,那么执行bind的时候会出错,一般客户端不建议使用bind函数,交给connect函数选择端口比较好,因为客户端的端口通常没有什么意义。
客户端TCP连接的TIME_WAIT状态过多,会导致端口耗尽而无法建立新的连接吗?
如果客户端都是与同一个服务器(目标IP和目标PORT一样)建立连接,那么如果客户端TIME_WAIT 状态过多,当端口资源被耗尽,就无法与服务器建立新连接了。
但只要客户端连接的服务器不同,端口资源是可以重复使用的。
如何解决客户端 TCP 连接 TIME_WAIT 过多,导致无法与同一个服务器建立连接的问题?
打开 net.ipv4.tcp_tw_reuse 这个内核参数。 开启了这个内核参数后,客户端调用 connect 函数时,如果选择到的端口已经被相同四元组的连接占用,就会判断该连接是否处于TIME_WAIT 状态
如果该连接处于 TIME_WAIT 状态并且 TIME_WAIT 状态持续的时间超过了 1 秒,那么就会重用这
个连接,然后就可以正常使用该端口了。
注意:该内核参数是在客户端调用 connect 函数时才起作用,所以服务端开启这个参数是没有效果的。

用了 TCP 协议,数据一定不会丢吗?

不一定。可以通过ping和mtr进行查看,ping只能查看是否发生丢包,无法查看具体哪个节点发生丢包,而mtr可以查看具体那个节点发生丢包。

TCP四次握手可以变成三次吗?

可以。当被动关闭方在TCP挥手过程中,如果没有数据要发送,同时开启TCP延迟确认机制(默认开启),那么第二次和第三次挥手就会合并传输,变成三次挥手。
怎么关闭 TCP 延迟确认机制?
可以在socket设置里启用TCP_QUICKACK.
完结!

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

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

相关文章

Spring Boot漫画之家:漫画爱好者的数字图书馆

2 系统开发环境 2.1 JAVA简介 JavaScript是一种网络脚本语言&#xff0c;广泛运用于web应用开发&#xff0c;可以用来添加网页的格式动态效果&#xff0c;该语言不用进行预编译就直接运行&#xff0c;可以直接嵌入HTML语言中&#xff0c;写成js语言&#xff0c;便于结构的分离&…

一次“okhttp访问间隔60秒,提示unexpected end of stream“的问题排查过程

一、现象 okhttp调用某个服务&#xff0c;如果第二次访问间隔上一次访问时间超过60s&#xff0c;返回错误&#xff1a;"unexpected end of stream"。 二、最终定位原因&#xff1a; 空闲连接如果超过60秒&#xff0c;服务端会主动关闭连接。此时客户端恰巧访问了这…

一、开启 GD32 单片机的学习之门

文章目录 一、开启GD32单片机的学习之门二、筑牢根基&#xff1a;GD32单片机基础知识全解析&#xff08;一&#xff09;单片机概述 三、开发环境搭建&#xff08;一&#xff09;软件下载与安装&#xff08;二&#xff09;安装GD32F450设备支持包&#xff08;三&#xff09;编译…

渗透测试---burpsuite(6)暴力破解与验证码识别绕过

声明&#xff1a;学习素材来自b站up【泷羽Sec】&#xff0c;侵删&#xff0c;若阅读过程中有相关方面的不足&#xff0c;还请指正&#xff0c;本文只做相关技术分享,切莫从事违法等相关行为&#xff0c;本人与泷羽sec团队一律不承担一切后果 视频地址&#xff1a;泷羽---bp&…

PSHuman 部署笔记

目录 github地址&#xff1a; 依赖项&#xff1a; xformers安装&#xff1a; 解决方法&#xff0c;安装xformers smpl_data下载&#xff1a; 推理步骤&#xff1a; SMPLDataset 香港科技大学提出了一种叫PSHuman的新框架。这个方法利用了一个多视角扩散模型的“先验知识…

基于VTX356语音识别合成芯片的智能语音交互闹钟方案

一、方案概述 本方案旨在利用VTX356语音识别合成芯片强大的语音处理能力&#xff0c;结合蓝牙功能、APP或小程序&#xff0c;打造一款功能全面且智能化程度高的闹钟产品。除了基本的时钟显示和闹钟提醒功能外&#xff0c;还拥有正计时、倒计时、日程安排、重要日提醒以及番茄钟…

20241206-Windows 10下使用IDEA 2024.2.3(JDK 18.0.2.1)搭建Hadoop 3.3.6开发环境

Windows 10下使用IDEA 2024.2.3(JDK 18.0.2.1)搭建Hadoop 3.3.6开发环境 1. 配置好本地hadoop之后 2. idea 新建或导入 Maven 项目 3. 编写 pom.xml 文件: 有些版本和项目信息需要根据自己的项目进行调整 JDK 18.0.2.1 Hadoop 3.3.6 <?xml version"1.0" encod…

如何使用WinCC DataMonitor基于Web发布浏览Excel报表文档

本文介绍使用 WinCC DataMonitor 的 "Excel Workbooks" 功能&#xff0c;通过 Excel 表格显示 WinCC 项目的过程值、归档变量值和报警归档消息。并可以通过 Web 发布浏览访问数据 1&#xff0e;WinCC DataMonitor是什么 ? DataMonitor 是 SIMATIC WinCC 工厂智能中…

Fastadmin地图插件在表单中的使用

表单中实现地图选择获取经纬度 1.Fastadmin后台安装地图选择插件地图位置(经纬度)选择插件 - 支持百度地图、高德地图、腾讯地图 – 基于ThinkPHP和Bootstrap的极速后台开发框架 2.腾讯地图开放平台后台创建应用创建KEY&#xff0c;配置逆地址解析额度。插件配置中配置腾讯地图…

解决view-ui-plus 中表单验证不通过问题,select 组件开启multiple模式 总是提示错误,即使不验证也提示,有值也验证失败

&#x1f609; 你好呀&#xff0c;我是爱编程的Sherry&#xff0c;很高兴在这里遇见你&#xff01;我是一名拥有十多年开发经验的前端工程师。这一路走来&#xff0c;面对困难时也曾感到迷茫&#xff0c;凭借不懈的努力和坚持&#xff0c;重新找到了前进的方向。我的人生格言是…

JDK8新特性之Stream流03

收集Stream流中的结果 IntStream intStream = Stream.of(1, 2, 3, 4, 5).mapToInt(Integer::intValue); intStream.filter(n -> n > 3).forEach(System.out::println); intStream.filter(n -> n > 3).count; intStream.filter(n -> n > 3).reduce(0, Integer…

图社区发现算法--Leiden算法

Leiden算法出自2019年的论文《From Louvain to Leiden: guaranteeing well-connected communities》&#xff0c;它是Louvain算法的改进社区发现算法&#xff0c;相比Louvain得到的社区质量更高&#xff0c;因为其移动策略速度也更快。Leiden算法也是以论文作者所在城市来命名的…

基于APO四步实现炫酷的NGINX请求分析看板

APO 充分利用 Vector ClickHouse 实现的日志方案&#xff0c;做到了开箱即用、高效、低成本。利用 APO 的日志功能&#xff0c;不仅可以检索日志内容本身&#xff0c;还可以实现很多有意思的功能。本次为大家介绍使用 APO 的日志功能实现炫酷的 NGINX 请求分析看板&#xff0c…

QT 中基于 TCP 的网络通信

基础 基于 TCP 的套接字通信需要用到两个类&#xff1a; 1&#xff09;QTcpServer&#xff1a;服务器类&#xff0c;用于监听客户端连接以及和客户端建立连接。 2&#xff09;QTcpSocket&#xff1a;通信的套接字类&#xff0c;客户端、服务器端都需要使用。 这两个套接字通信类…

X推出新AI图像生成器Aurora:更接近真实的创作效果

每周跟踪AI热点新闻动向和震撼发展 想要探索生成式人工智能的前沿进展吗&#xff1f;订阅我们的简报&#xff0c;深入解析最新的技术突破、实际应用案例和未来的趋势。与全球数同行一同&#xff0c;从行业内部的深度分析和实用指南中受益。不要错过这个机会&#xff0c;成为AI领…

后端工程搭建

后端工程通过maven聚合工程的形式来搭建 1.1创建spzx-parent工程(父工程) 存放公共依赖 锁定公共依赖版本 1.2创建spzx-common工程(公共模块) 存放一些工具类/公共服务 1.3创建spzx-model工程(数据模型) 存放实体类 1.4创建spzx-menager工程(后台管理系统) 后台管理系统服务模…

Co-Slam论文及复现记录

Overview 输入RGB-D流&#xff1a; { I t } t 1 N { D t } t 1 N \{I_t\}^{N}_{t1}\{D_t\}^{N}_{t1} {It​}t1N​{Dt​}t1N​&#xff0c;它们带有已知相机内参 K ∈ R 3 3 K\in \mathbb{R}^{3\times 3} K∈R33。通过联合优化相机姿态 { ξ t } t 1 N \{\xi_t\}^{N}_{t1} {…

无代码,无问题:面向手动测试人员的人工智能自动化

“质量比数量更重要。一个本垒打比两个二垒打好得多。” ——史蒂夫乔布斯 在软件测试领域&#xff0c;这句话再贴切不过了。如果你是一名手动测试人员&#xff0c;你就会知道交付高质量结果的压力&#xff0c;而且通常是在紧迫的期限和有限的资源内。 然而&#xff0c;在当今…

transformers生成式对话机器人

简介 生成式对话机器人是一种先进的人工智能系统&#xff0c;它能够通过学习大量的自然语言数据来模拟人类进行开放、连贯且创造性的对话。与基于规则或检索式的聊天机器人不同&#xff0c;生成式对话机器人并不局限于预定义的回答集&#xff0c;而是可以根据对话上下文动态地…

模版方法模式的理解和实践

在软件开发中&#xff0c;设计模式为我们提供了一套经过验证的解决方案&#xff0c;用于解决常见的设计问题。其中&#xff0c;模版方法模式&#xff08;Template Method Pattern&#xff09;是一种行为设计模式&#xff0c;它定义了一个算法的框架&#xff0c;并允许子类在不改…