• TCP重头戏来!了!(1) —— 小林图解学习摘记


    TCP重头戏来!了!—— 小林图解学习摘记1

    TCP重头戏来!了!

    TCP 头格式/报文报文包含哪些信息?

    TCP 头格式

    源端口和目的端口号:它用于多路复用/分解来自或送往上层应用的数据,其和 IP 数据报中的源 IP 与目的 IP 地址一同确定一条 TCP 连接。

    首部长度:该字段指示了以 32 比特的字为单位的 TCP 的首部长度。其中固定字段长度为 20 字节,由于首部长度可能含有可选项内容,因此 TCP 报头的长度是不确定的,20 字节是 TCP 首部的最小长度。

    序列号:在建立连接时由计算机生成的随机数作为其初始值,通过 SYN 包传给接收端主机,每发送一次数据,就「累加」一次该「数据字节数」的大小。用来解决网络包乱序问题。

    确认应答号:指下一次「期望」收到的数据的序列号,发送端收到这个确认应答以后可以认为在这个序号以前的数据都已经被正常接收。用来解决丢包的问题。

    控制位:

    • ACK:该位为 1 时,「确认应答」的字段变为有效,TCP 规定除了最初建立连接时的 SYN 包之外该位必须设置为 1
    • RST:该位为 1 时,表示 TCP 连接中出现异常必须强制断开连接。
    • SYN:该位为 1 时,表示希望建立连接,并在其「序列号」的字段进行序列号初始值的设定。
    • FIN:该位为 1 时,表示今后不会再有数据发送,希望断开连接。当通信结束希望断开连接时,通信双方的主机之间就可以相互交换 FIN 位为 1 的 TCP 段。

    **接收窗口:主要用于 TCP 流量控制。**该字段用来告诉发送方其窗口(缓冲区)大小,以此控制发送速率,从而达到流量控制的目的。

    校验和:奇偶校验,此校验和是对整个 TCP 报文段,包括 TCP 头部和 数据部分。该校验和是一个端到端的校验和,由发送端计算和存储,并由接收端进行验证,主要目的是检验数据是否发生改动,若检测出差错,接收方会丢弃该 TCP 报文。

    紧急数据指针:紧急数据用于告知紧急数据所在的位置,在URG标志位为 1 时才有效。当紧急数据存在时,TCP 必须通知接收方的上层实体,接收方会对紧急模式采取相应的处理。

    **选项:**该字段一般为空,可根据首部长度进行推算。主要有以下作用:

    • ① TCP 连接初始化时,通信双方确认最大报文长度。
    • ② 在高速数据传输时,可使用该选项协商窗口扩大因子。
    • ③ 作为时间戳时,提供一个 较为精准的 RTT。(RTT的变化在一定程度上反映了网络拥塞程度的变化,所以可以去更好的实现拥塞控制)

    **数据:**TCP 报文中的数据部分也是可选的,例如在 TCP 三次握手和四次挥手过程中,通信双方交换的报文只包含头部信息,数据部分为空,只有当连接成功建立后,TCP 包才真正携带数据。

    UDP头格式/报文报文包含哪些信息?

    UDP 头部格式
    • 目标和源端口:主要是告诉 UDP 协议应该把报文发给哪个进程。
    • 包长度:该字段保存了 UDP 首部的长度跟数据的长度之和。
    • 校验和:校验和是为了提供可靠的 UDP 首部和数据而设计,防止收到在网络传输中受损的 UDP包。

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

    IP 层是「不可靠」的,它不保证网络包的交付、不保证网络包的按序交付、也不保证网络包中的数据的完整性。如果需要保障网络数据包的可靠性,那么就需要由上层(传输层)的 TCP 协议来负责。因为 TCP 是一个工作在传输层可靠数据传输的服务,它能确保接收端接收的网络包是无损坏、无间隔、非冗余和按序的。

    什么是 TCP ?

    TCP 是面向连接的、可靠的、基于字节流的传输层通信协议。

    • 面向连接:一定是「一对一」才能连接,不能像 UDP 协议可以一个主机同时向多个主机发送消息,也就是一对多是无法做到的;
    • 可靠的:无论的网络链路中出现了怎样的链路变化,TCP 都可以保证一个报文一定能够到达接收端;
    • 字节流:用户消息通过 TCP 协议传输时,消息可能会被操作系统「分组」成多个的 TCP 报文,如果接收方的程序如果不知道「消息的边界」,是无法读出一个有效的用户消息的。并且 TCP 报文是「有序的」,当「前一个」TCP 报文没有收到的时候,即使它先收到了后面的 TCP 报文,那么也不能扔给应用层去处理,同时对「重复」的 TCP 报文会自动丢弃。

    什么是 TCP 连接?

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

    所以我们可以知道,建立一个 TCP 连接是需要客户端与服务器端达成上述三个信息的共识。

    • Socket:由 IP 地址和端口号组成
    • 序列号:用来解决乱序问题等
    • 窗口大小:用来做流量控制

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

    服务器通常固定在某个本地端口上监听,等待客户端的连接请求。

    因此,客户端 IP 和 端口是可变的,其理论值计算公式如下:

    img

    对 IPv4,客户端的 IP 数最多为 232 次方,客户端的端口数最多为 216 次方,也就是服务端单机最大 TCP 连接数,约为 248 次方。

    当然,服务端最大并发 TCP 连接数远不能达到理论上限,会受以下因素影响:

    • 文件描述符限制

      ,每个 TCP 连接都是一个文件,如果文件描述符被占满了,会发生 too many open files。Linux 对可打开的文件描述符的数量分别作了三个方面的限制:

      • 系统级:当前系统可打开的最大数量,通过 cat /proc/sys/fs/file-max 查看;
      • 用户级:指定用户可打开的最大数量,通过 cat /etc/security/limits.conf 查看;
      • 进程级:单个进程可打开的最大数量,通过 cat /proc/sys/fs/nr_open 查看;
    • 内存限制,每个 TCP 连接都要占用一定内存,操作系统的内存是有限的,如果内存资源被占满后,会发生 OOM。

    UDP 和 TCP 有什么区别呢?分别的应用场景是?(重要!)

    UDP 不提供复杂的控制机制,利用 IP 提供面向「无连接」的通信服务。

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-rvnfiM3I-1656397091393)(C:\Users\Nothingserious\AppData\Roaming\Typora\typora-user-images\image-20220627174358882.png)]

    TCP 和 UDP 区别:

    1. 连接

    • TCP 是面向连接的传输层协议,传输数据前先要建立连接。
    • UDP 是不需要连接,即刻传输数据。

    2. 服务对象

    • TCP 是一对一的两点服务,即一条连接只有两个端点。
    • UDP 支持一对一、一对多、多对多的交互通信

    3. 可靠性

    • TCP 是可靠交付数据的,数据可以无差错、不丢失、不重复、按需到达。
    • UDP 是尽最大努力交付,不保证可靠交付数据。

    4. 拥塞控制、流量控制

    • TCP 有拥塞控制和流量控制机制,保证数据传输的安全性。
    • UDP 则没有,即使网络非常拥堵了,也不会影响 UDP 的发送速率。

    5. 首部开销

    • TCP 首部长度较长,会有一定的开销,首部在没有使用「选项」字段时是 20 个字节,如果使用了「选项」字段则会变长的。
    • UDP 首部只有 8 个字节,并且是固定不变的,开销较小。

    6. 传输方式

    • TCP 是流式传输,没有边界,但保证顺序和可靠。
    • UDP 是一个包一个包的发送,是有边界的,但可能会丢包和乱序。

    7. 分片不同

    • TCP 的数据大小如果大于 MSS 大小,则会在传输层进行分片,目标主机收到后,也同样在传输层组装 TCP 数据包,如果中途丢失了一个分片,只需要传输丢失的这个分片。
    • UDP 的数据大小如果大于 MTU 大小,则会在 IP 层进行分片,目标主机收到后,在 IP 层组装完数据,接着再传给传输层。

    TCP 和 UDP 应用场景:

    由于 TCP 是面向连接,能保证数据的可靠性交付,因此经常用于:

    • FTP 文件传输;
    • HTTP / HTTPS;

    由于 UDP 面向无连接,它可以随时发送数据,再加上UDP本身的处理既简单又高效,因此经常用于:

    • 包总量较少的通信,如 DNSSNMP 等;
    • 视频、音频等多媒体通信;
    • 广播通信;

    TCP 连接建立 / 三次握手 (重要!)

    TCP 是面向连接的协议,所以使用 TCP 前必须先建立连接,而建立连接是通过三次握手来进行的。三次握手的过程如下图:

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

    从上面的过程可以发现第三次握手是可以携带数据的,前两次握手是不可以携带数据的,这也是面试常问的题。

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

    总的来说三次握手的主要目的是确认自己和对方的发送和接收都是正常的,从而保证了双方能够进行可靠通信。若采用两次握手,当第二次握手后就建立连接的话,此时客户端知道服务器能够正常接收到自己发送的数据,而服务器并不知道客户端是否能够收到自己发送的数据。

    但详细分析的话还可以细化分成三点原因:

    • 三次握手才可以阻止重复历史连接的初始化(主要原因)
    • 三次握手才可以同步双方的初始序列号
    • 三次握手才可以避免资源浪费
    原因一:避免历史连接

    简单来说,三次握手的首要原因是为了防止旧的重复连接初始化造成混乱。

    客户端连续发送多次 SYN 建立连接的报文,在网络拥堵情况下:

    • 一个「旧 SYN 报文」比「最新的 SYN 」 报文早到达了服务端;
    • 那么此时服务端就会回一个 SYN + ACK 报文给客户端;
    • 客户端收到后可以根据自身的上下文,判断这是一个历史连接(序列号过期或超时),那么客户端就会发送 RST 报文给服务端,表示中止这一次连接。

    在两次握手的情况下,「被动发起方」没有中间状态给「主动发起方」来阻止历史连接,导致「被动发起方」可能建立一个历史连接,造成资源浪费

    你想想,两次握手的情况下,「被动发起方」在收到 SYN 报文后,就进入 ESTABLISHED 状态,意味着这时可以给对方发送数据给,但是「主动发起方」此时还没有进入 ESTABLISHED 状态,假设这次是历史连接,主动发起方判断到此次连接为历史连接,那么就会回 RST 报文来断开连接,而「被动发起方」在第一次握手的时候就进入 ESTABLISHED 状态,所以它可以发送数据的,但是它并不知道这个是历史连接,它只有在收到 RST 报文后,才会断开连接。

    两次握手无法阻止历史连接

    可以看到,上面这种场景下,「被动发起方」在向「主动发起方」发送数据前,并没有阻止掉历史连接,导致「被动发起方」建立了一个历史连接,又白白发送了数据,妥妥地浪费了「被动发起方」的资源。

    因此,要解决这种现象,最好就是在「被动发起方」发送数据前,也就是建立连接之前,要阻止掉历史连接,这样就不会造成资源浪费,而要实现这个功能,就需要三次握手

    所以,TCP 使用三次握手建立连接的最主要原因是防止「历史连接」初始化了连接。

    原因二:同步双方初始序列号

    TCP 协议的通信双方, 都必须维护一个「序列号」, 序列号是可靠传输的一个关键因素,它的作用:

    • 接收方可以去除重复的数据;
    • 接收方可以根据数据包的序列号按序接收;
    • 可以标识发送出去的数据包中, 哪些是已经被对方收到的(通过 ACK 报文中的序列号知道);

    可见,序列号在 TCP 连接中占据着非常重要的作用,所以当客户端发送携带「初始序列号」的 SYN 报文的时候,需要服务端回一个 ACK 应答报文,表示客户端的 SYN 报文已被服务端成功接收,那当服务端发送「初始序列号」给客户端的时候,依然也要得到客户端的应答回应,这样一来一回,才能确保双方的初始序列号能被可靠的同步。

    四次握手与三次握手

    四次握手其实也能够可靠的同步双方的初始化序号,但由于第二步和第三步可以优化成一步,所以就成了「三次握手」。而两次握手只保证了一方的初始序列号能被对方成功接收,没办法保证双方的初始序列号都能被确认接收。

    原因三:避免资源浪费

    如果只有「两次握手」,当客户端的 SYN 请求连接在网络中阻塞,客户端没有接收到 ACK 报文,就会重新发送 SYN ,由于没有第三次握手,服务器不清楚客户端是否收到了自己发送的建立连接的 ACK 确认信号,所以每收到一个 SYN 就只能先主动建立一个连接,如果客户端的 SYN 阻塞了,重复发送多次 SYN 报文,那么服务器在收到请求后就会**建立多个冗余的无效链接,造成不必要的资源浪费。**即两次握手会造成消息滞留情况下,服务器重复接受无用的连接请求 SYN 报文,而造成重复分配资源。

    两次握手会造成资源浪费
    小结

    TCP 建立连接时,通过三次握手保证了双方能够进行可靠通信(发送和接收都是正常的),能防止历史连接的建立,能减少双方不必要的资源开销,能帮助双方同步初始化序列号(序列号能够保证数据包不重复、不丢弃和按序传输)。

    不使用「两次握手」和「四次握手」的原因:

    • 「两次握手」:无法防止历史连接的建立,会造成双方资源的浪费,也无法可靠的同步双方序列号;
    • 「四次握手」:三次握手就已经理论上最少可靠连接建立,所以不需要使用更多的通信次数。

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

    服务器不会进行任何相应的动作,而**客户端如果在一段时间内没有收到服务器发来的确认报文(也就是第二次握手:服务端的 SYN-ACK 报文),就会触发「超时重传」机制,重传 SYN 报文。**若仍然没有回应,则重复上述过程直到发送次数超过最大重传次数限制后,建立连接的系统调用会返回 -1。

    那到底重发几次呢?

    在 Linux 里,客户端的 SYN 报文最大重传次数由 tcp_syn_retries内核参数控制,这个参数是可以自定义的,默认值一般是 5。通常,第一次超时重传是在 1 秒后,第二次超时重传是在 2 秒,第三次超时重传是在 4 秒后,第四次超时重传是在 8 秒后,第五次是在超时重传 16 秒后。没错,每次超时的时间是上一次的 2 倍。当第五次超时重传后,会继续等待 32 秒,如果服务端仍然没有回应 ACK,客户端就不再发送 SYN 包,然后断开 TCP 连接。

    所以,总耗时是 1+2+4+8+16+32=63 秒,大约 1 分钟左右。

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

    第二次握手的 SYN-ACK 报文其实有两个目的 :

    • 第二次握手里的 ACK, 是对第一次握手的确认报文;
    • 第二次握手里的 SYN,是服务端发起建立 TCP 连接的报文;

    因为第二次握手报文里是包含对客户端的第一次握手的 ACK 确认报文,所以,如果客户端迟迟没有收到第二次握手,那么客户端就觉得可能自己的 SYN 报文(第一次握手)丢失了,于是客户端就会触发超时重传机制,重传 SYN 报文

    然后,因为第二次握手中包含服务端的 SYN 报文,所以当客户端收到后,需要给服务端发送 ACK 确认报文(第三次握手),服务端才会认为该 SYN 报文被客户端收到了。如果第二次握手丢失了,服务端就收不到第三次握手,于是服务端这边会触发超时重传机制,重传 SYN-ACK 报文

    因此,当第二次握手丢失了,客户端和服务端都会重传:

    • 客户端会重传 SYN 报文,也就是第一次握手,最大重传次数由 tcp_syn_retries内核参数决定;

    • 服务端会重传 SYN-ACK 报文,也就是第二次握手,最大重传次数由 tcp_synack_retries 内核参数决定。

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

    第三次握手的 ACK 是对第二次握手的 SYN 的确认报文,所以当第三次握手丢失了,如果服务端那一方迟迟收不到这个确认报文,就会触发超时重传机制,重传 SYN-ACK 报文,直到收到第三次握手,或者达到最大重传次数

    如果达到最大重传次还没收到,那服务器端连接建立失败。但此时客户端认为自己已经连接成功了,因此开始向服务器端发送数据,但是服务器端此时没有在监听状态,因此服务器端接收到来自客户端发送来的数据时会发送 RST 报文给客户端,消除客户端单方面建立连接的状态。

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

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

    主要原因有两个方面:

    • 为了防止历史报文被下一个相同四元组的连接接收(主要方面);

      • 客户端和服务端的初始化序列号都是随机生成,能很大程度上避免历史报文被下一个相同四元组的连接接收,然后又引入时间戳的机制,从而完全避免了历史报文被接收的问题。
    • 为了安全性,防止黑客伪造的相同序列号的 TCP 报文被对方接收;

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

    MTU 与 MSS
    • MTU:一个网络包的最大长度,以太网中一般为 1500 字节;
    • MSS:除去 IP 和 TCP 头部之后,一个网络包所能容纳的 TCP 数据的最大长度;

    当 IP 层有一个超过 MTU 大小的数据(TCP 头部 + TCP 数据)要发送,那么 IP 层就要进行分片,把数据分片成若干片,保证每一个分片都小于 MTU。把一份 IP 数据报进行分片以后,由目标主机的 IP 层来进行重新组装后,再交给上一层 TCP 传输层。这看起来井然有序,但这存在隐患的,那么当如果一个 IP 分片丢失,整个 IP 报文的所有分片都得重传。因为 IP 层本身没有超时重传机制,它由传输层的 TCP 来负责超时和重传。

    所以,为了达到最佳的传输效能 TCP 协议在建立连接的时候通常要协商双方的 MSS 值,当 TCP 层发现数据超过 MSS 时,则就先会进行分片,当然由它形成的 IP 包的长度也就不会大于 MTU ,自然也就不用 IP 分片了。此时如果一个 TCP 分片丢失后,进行重发时也是以 MSS 为单位,而不用重传所有的分片,大大增加了重传的效率。

    TCP 连接断开 / 四次挥手(重要!)

    img
    • ① 客户端打算关闭连接,此时会发送一个 TCP 首部 FIN 标志位被置为 1 的报文,也即 FIN 报文(表示请求释放连接),FIN报文中会指定一个序列号,之后客户端进入 FIN_WAIT_1 状态(即半关闭阶段,并且停止向服务端发送通信数据)

    • ② 服务端收到该报文后,就向客户端发送 ACK 应答报文(表示接收到客户端释放连接的请求),接着服务端进入 CLOSED_WAIT 状态(等待关闭)。

      客户端收到服务器发送过来的 TCP 报文后,确认服务器已经收到了客户端连接释放的请求,随后客户端结束 FIN-WAIT-1阶段,进入 FIN_WAIT_2 状态。

    • ③ 服务器也打算断开连接,等待服务端处理完数据后,也向客户端发送 FIN 报文,之后服务端进入 LAST_ACK 状态(最后确认),并且停止向客户端发送数据。

    • ④ 客户端收到服务端的 FIN 报文后,确认了服务器已经做好释放连接的准备,回一个 ACK 应答报文,于是结束 FIN-WAIT-2 阶段,进入 TIME_WAIT 状态

    • 服务器收到了 ACK 应答报文后,就进入了 CLOSED 状态,至此服务端已经完成连接的关闭。

      客户端在经过 2MSL 一段时间后,自动进入 CLOSED 状态,至此客户端也完成连接的关闭。

    每个方向都需要一个 FIN 和一个 ACK,因此通常被称为四次挥手。这里一点需要注意是:主动关闭连接的,才有 TIME_WAIT 状态。

    为什么挥手需要四次?

    回顾下四次挥手双方发 FIN 包的过程:

    • 关闭连接时,客户端向服务端发送 FIN 时,仅仅表示客户端不再发送数据了但是还能接收数据。
    • 服务器收到客户端的 FIN 报文时,先回一个 ACK 应答报文,而服务端可能还有数据需要处理和发送,等服务端不再发送数据时,才发送 FIN 报文给客户端来表示同意现在关闭连接。

    综上所述,服务端通常需要等待完成数据的发送和处理,所以服务端的 ACKFIN 一般都会分开发送,也就是说是 FIN 释放连接报文和 ACK 确认接收报文是分别在两次握手中传输的,从而导致比三次握手多了一次。

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

    如果第一次挥手丢失了,那么客户端迟迟收不到被动方的 ACK 的话,会触发超时重传机制,重传 FIN 报文,重发次数由 tcp_orphan_retries 参数控制。当客户端重传 FIN 报文的次数超过 tcp_orphan_retries 后,就不再发送 FIN 报文,直接进入到 close 状态。

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

    因为ACK 报文是不会重传的,所以如果服务端的第二次挥手丢失了,客户端就会触发超时重传机制,重传 FIN 报文,直到收到服务端的第二次挥手,或者达到最大的重传次数。

    这里提一下,当客户端收到第二次挥手,也就是收到服务端发送的 ACK 报文后,客户端就会处于 FIN_WAIT2 状态,在这个状态需要等服务端发送第三次挥手,也就是服务端的 FIN 报文。对于 close 函数关闭的连接,由于无法再发送和接收数据,所以FIN_WAIT2 状态不可以持续太久,而 tcp_fin_timeout 控制了这个状态下连接的持续时长,默认值是 60 秒。这意味着对于调用 close 关闭的连接,如果在 60 秒后还没有收到 FIN 报文,客户端(主动关闭方)的连接就会直接关闭。

    但是注意,如果主动关闭方使用 shutdown 函数关闭连接且指定只关闭发送方向,而接收方向并没有关闭,那么意味着主动关闭方还是可以接收数据的。如果主动关闭方一直没收到第三次挥手,那么主动关闭方的连接将会一直处于 FIN_WAIT2 状态(tcp_fin_timeout 无法控制 shutdown 关闭的连接)。

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

    当服务端(被动关闭方)收到客户端(主动关闭方)的 FIN 报文后,内核会自动回复 ACK。而服务端等待客户端返回 ACK 来确认连接关闭。如果迟迟收不到这个 ACK,服务端就会重发 FIN 报文,重发次数仍然由 tcp_orphan_retries参数控制,与客户端重发 FIN 报文的重传次数控制方式是一样的。

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

    服务端(被动关闭方)没有收到 ACK 报文前,还是处于 LAST_ACK 状态。如果第四次挥手的 ACK 报文没有到达服务端,服务端就会重发 FIN 报文,重发次数仍然由 tcp_orphan_retries 参数控制。

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

    当客户端发出最后的 ACK 确认报文时,并不能确定服务器端能够收到该段报文。所以客户端在发送完 ACK 确认报文之后,会设置一个时长为 2 MSL 的计时器。为的是确认服务器能否接收到客户端发出的 ACK 确认报文。MSL指一段 TCP 报文在传输过程中的最大生命周期。2 MSL 即是服务器端发出 FIN 报文和客户端发出的 ACK 确认报文所能保持有效的最大时长。

    若服务器在 1 MSL 内没有收到客户端发出的 ACK 确认报文,再次向客户端发出 FIN 报文。如果客户端在 2 MSL 内收到了服务器再次发来的 FIN 报文,说明服务器由于一些原因并没有收到客户端发出的 ACK 确认报文。客户端将再次向服务器发出 ACK 确认报文,并重新开始 2 MSL 的计时。若客户端在 2MSL 内没有再次收到服务器发送的 FIN 报文,则说明服务器正常接收到客户端 ACK 确认报文,客户端可以进入 CLOSE 阶段,即完成四次挥手。

    注意区分MSL,RTT,TTL

    MSL报文最大生存时间,TTL生存时间(跳数),RTT客户到服务器往返所花时间

    TTL生存时间(跳数): 存储了一个ip数据报可以经过的最大路由数,每经过一个处理他的路由器此值就减1,当此值为0则数据报将被丢弃,同时发送ICMP报文通知源主机。

    TTL与MSL是有关系的但不是简单的相等的关系,MSL要大于等于TTL。

    RTT:受网络传输拥塞程序的变化而变化。表示从发送端发送数据开始,到发送端收到来自接收端的确认(接收端收到数据后便立即发送确认),总共经历的时延。一般认为单向时延=传输时延t1+传播时延t2+排队时延t3

    t1是数据从进入节点到传输媒体所需要的时间,通常等于数据块长度/信道带宽
    t2是信号在信道中需要传播一定距离而花费的时间,等于信道长度/传播速率
    t3可笼统归纳为随机噪声,由途径的每一跳设备及收发两端负荷情况及吞吐排队情况决定(包含互联网设备和传输设备时延)

    TIME-WAIT 的状态和意义 / 为什么需要 TIME_WAIT 状态?

    主动发起关闭连接的一方,才会有 TIME-WAIT 状态。

    需要 TIME-WAIT 状态,主要是两个原因:

    • 防止历史连接中的数据,被后面相同四元组的连接错误的接收;
      • TIME-WAIT 发生在第四次挥手,当客户端向服务端发送 ACK 确认报文后进入该状态,若取消该状态,即客户端在收到服务端的 FIN 报文后立即关闭连接,此时服务端相应的端口并没有关闭,若客户端在相同的端口立即建立新的连接,则有可能接收到上一次连接中残留的数据包,可能会导致不可预料的异常出现。
      • 为了防止历史连接中的数据,被后面相同四元组的连接错误的接收,因此 TCP 设计了 TIME_WAIT 状态,状态会持续 2MSL 时长,这个时间足以让两个方向上的数据包都被丢弃,使得原来连接的数据包在网络中都自然消失,再出现的数据包一定都是新建立连接所产生的。
    • 保证「被动关闭连接」的一方,能被正确的关闭;
      • 假设客户端最后一次发送的 ACK 包在传输的时候丢失了,由于 TCP 协议的超时重传机制,服务端将重发 FIN 报文,若客户端并没有维持 TIME-WAIT 状态而直接关闭的话,当收到服务端重新发送的 FIN 包时,客户端就会用 RST 包来响应服务端,服务端收到这个 RST 并将其解释为一个错误,然而其实只是正常的关闭连接过程,并没有出现异常情况。
      • 为了防止这种情况出现,客户端必须等待足够长的时间确保对端收到 ACK,如果对端没有收到 ACK,那么就会触发 TCP 重传机制,服务端会重新发送一个 FIN,这样一去一来刚好两个 MSL 的时间。

    TIME_WAIT 过多有什么危害?/ TIME_WAIT 状态会导致什么问题,怎么解决?

    • 第一是内存资源占用;

    • 第二是对端口资源的占用,一个 TCP 连接至少消耗「发起连接方」的一个本地端口;

      • 如果「发起连接方」的 TIME_WAIT 状态过多,占满了所有端口资源,则会导致无法创建新连接。

    客户端(发起连接方)受端口资源限制

    • 客户端TIME_WAIT过多,就会导致端口资源被占用,因为端口就 65536 个,被占满就会导致无法创建新的连接。

    服务端(被动连接方)受系统资源限制

    • 由于一个四元组表示 TCP 连接,理论上服务端可以建立很多连接,因为服务端只监听一个端口,不会因为 TCP 连接过多而导致端口资源受限。但是 TCP 连接过多,会占用系统资源,比如文件描述符、内存资源、CPU 资源、线程资源等。

    CLOSE-WAIT的状态和意义

    在服务器收到客户端关闭连接的请求并告诉客户端自己已经成功收到了该请求之后,服务器进入了 CLOSE-WAIT 状态,然而此时有可能服务端还有一些数据没有传输完成,因此不能立即关闭连接,而 CLOSE-WAIT 状态就是为了保证服务器在关闭连接之前将待发送的数据发送完成。

  • 相关阅读:
    AI大模型低成本快速定制法宝:RAG和向量数据库
    PMP每日一练 | 考试不迷路-11.23(包含敏捷+多选)
    类型体系与基本数据类型(第五节)
    7-58 修理牧场——优先队列
    springboot实现监听
    leetcode622-设计循环队列
    10.1作业
    【大数据】Spark使用大全:下载安装、RDD操作、JAVA编程、SQL
    Apollo 7.0自动驾驶开发笔记22——Orin上运行Apollo(3)目标检测增加多路摄像头
    基于Spring Boot+Unipp的校园志愿者小程序(图形化分析)
  • 原文地址:https://blog.csdn.net/h0327x/article/details/125501522