借助AI再次理解三次握手和四次挥手

文章分为核心前置知识、三次握手、四次挥手以及相关思考和问题,感兴趣的话可以全部读一读

一、核心前置知识:TCP 报文的"密码"

在看握手和挥手之前,必须先认得 TCP 报文头部的几个控制位(Flags)。它们就像是旗语,只有 0(关)和 1(开)两种状态:

1. SYN (Synchronize) "同步"信号灯

这盏灯专门用来建立连接。它亮起是为了告诉对方:"我想和你对一下暗号(同步初始序列号 seq)。"

  • SYN = 1 (亮): "我想和你建立新连接"。 整个 TCP 通信生命周期中,只有在三次握手的前两次(客户端发起请求、服务端同意请求并反向请求)这盏灯才会亮起。
  • SYN = 0 (灭): "这是一封普通的、处于通信中的信"。 只要连接一旦建立成功(第三次握手及以后),这盏灯就会永久熄灭,再也不会亮起。

2. ACK (Acknowledge) ------ "收到确认"信号灯

这盏灯是 TCP 可靠传输的灵魂 ,用来给对方报平安。

  • ACK = 1 (亮): "我这封信里的确认号 ack 是有效的,我收到了你之前发的数据。" TCP 规定,除了客户端发出的第一封信(第一次握手)以外,后面所有人发出的所有报文,ACK 都必须是 1。只要连接成了,大家说话都得带着"收到"的标志。
  • ACK = 0 (灭): "这封信里的确认号 ack 无效,忽略它。" 这种情况极其罕见,只有在第一次握手时,客户端单方面发起连接,此时它还没收到服务端的任何回应,所以 ack 没意义,此时 ACK = 0。

3. FIN (Finish) ------ "再见/结束"信号灯

这盏灯专门用来拆除连接。它亮起代表一方要开始"撂电话"了。

  • FIN = 1 (亮): "我的应用数据已经全部发完了,我要关闭我这边的发送通道了。" 在四次挥手时,客户端想断开会发一封 FIN=1 的信,服务端想断开也会发一封 FIN=1 的信。它亮起就意味着"再见"。
  • FIN = 0 (灭): "我还在正常传输数据呢,没打算断开。" 在平时的正常通信、以及建立连接的三次握手期间,这盏灯都是灭的。

如果把 TCP 通信比作两个人在通过挂号信写一本连载小说,那么 seq 和 ack 就是书页里的"字符计数器"。因为 TCP 是全双工(双向)通信,所以每个人手里都有一个独立的计数器。

4. seq(序号):当前发送的"起点位置"

TCP 极其严谨,它不按"第几条消息"来算账,而是按"字节(Byte)"来算账。

  • 只要连接一建立,TCP 就会为你要发送的每一个字节的数据,在心里编一个号。
  • seq 的含义:就是告诉对方,"这封信里装的数据,是从我这边全局数据流的第几个字节开始的"。

5. ack(确认号):期望下次收到的"起点位置"

ack 则是用来向对方"催更"或"报平安"的。

  • ack 的含义:告诉对方,"到第 X-1 个字节为止的数据我全都稳稳收到了!你下一封信,请从第 X 个字节开始发给我。"

    • 这里为什么是 x - 1? ==注意这里是数据传输时的ack,传输过程中是有一个实际的len数据长度的==

      python 复制代码
      """
      如果对方这次的起点 seq = 101,数据长度 len = 100。
         对方发过来的字节编号范围:从 101 开始,往后数 100 个,最后一个字节的编号是 200。
         公式计算 ack:seq + len = $101 + 100 =$ 201(这就是 $X$)。
      代入大白话:
         "到第 $201 - 1 = 200$ 个字节为止的数据我全都收到了!你下一封信,请从第 201 个字节开始发。"
      
      核心逻辑
         因为 TCP 算的是"下一发数据的起始位置"。
         ack 的数值($X$)代表的是尚未收到的、排在最前面的那一个空位。既然它是第一个没收到的,那它紧挨着的上一个位置($X - 1$),自然就是最后一个已稳稳收到的。
      """
    • 三次握手时的 ack = x + 1, ==注意这里的1是默认长度,不要和上面的x - 1联想到一起,不一样的概念== 先了解下这个+1,具体三次握手过程我们看后面

      python 复制代码
      """
      核心特殊规则:没有数据,强行 +1
      	在正常的通信中,ack = seq + len(数据长度)。
      	
      	但在三次握手(和四次挥手)时,报文里是没有应用层数据的,也就是说 len = 0。
      
      如果按照常规公式算:ack = seq + 0 = seq,那对方下一次发过来的起点还是 seq,这就原地打转了。
      
      为了解决这个问题,TCP 规定:只要控制位里的 SYN(同步)或者 FIN(结束)标志灯亮起(为 1),这个报文就算没有任何数据,也必须硬性、强行占掉 1 个字节的编号位置!
      
      因此,三次握手时的计算公式变成了:ack = 对方的 seq + 1。
      """
  • 所以,ack 的数值永远是:对方刚发过来的 seq + 对方刚发过来的数据长度。

二、上述知识了解后,我们来看看三次握手

1. 第一次握手:客户端发起连接

  • 做什么:客户端随机生成一个初始序号 seq = x,把控制位 SYN 设为 1,向服务端发送首个报文。为什么没有 ack? 因为这是第一封信,客户端还没有收到过服务端的任何回应,ACK 灯是灭的(0),此时的 ack 没有任何意义(通常为 0)。
  • 状态变化:客户端由 CLOSED(关闭)状态进入 SYN_SENT(同步已发送) 状态。
  • 此时底层:这个报文不能携带任何应用层数据(如 HTTP 请求),但它会消耗掉一个序号(所以下一步对方回的 ack 会是 x + 1)。

2. 第二次握手:服务端响应并反向请求

  • 做什么:服务端收到 SYN 报文后,同意建立连接。它需要做两件事:

    • 确认客户端的请求:把 ACK 设为 1,并将确认号设为 ack = x + 1。这里的 ack 计算公式:对方的 seq + 强行 1。大白话就是,"客户端,到第 x 个字节为止的'建立连接暗号'我全都稳稳收到了!你的诚意我懂了。接下来你再跟我说话(第三回合),请从第 x + 1 个字节开始发给我"

    • 自己也发起连接请求:随机生成自己的初始序号 seq = y,把 SYN 设为 1。 这两个动作合并成一个 SYN+ACK 报文发回。

  • 状态变化:服务端由 LISTEN(监听)进入 SYN_RCVD(同步已接收) 状态。

3. 第三次握手:客户端最后确认

  • 做什么:客户端收到服务端的 SYN+ACK 报文。对服务端的连接请求进行确认:把 ACK 设为 1,确认号设为 ack = y + 1,自己的序号变为 seq = x + 1。发送给服务端。这里的 ack 根据公式:对方的 seq + 强行 1。大白话:"服务端,你发过来的连接请求我也收到了,到第 y 个字节为止的暗号我全拿到了!你放心吧。咱们现在连接建好了,你接下来如果要给我发真正的网页数据,请从第 y + 1 个字节开始发给我。"

  • 状态变化:客户端发送后立刻进入 ESTABLISHED(已建立连接) 状态。服务端收到该报文后,也进入 ESTABLISHED 状态。

  • 此时底层:第三次握手的报文可以携带具体的应用层数据了。

python 复制代码
"""
三次握手时的 ack,其本质就是"给对方的连接请求(SYN)打一个回执":

	服务端回的 ack = 101,是对客户端 SYN (seq=100) 的签收证明。

	客户端回的 ack = 501,是对服务端 SYN (seq=500) 的签收证明。

正因为双方都拿到了对方明确写着 +1 的 ack 回执,彼此才敢百分之百地确信:"对方不仅听到了我的声音,而且我们的序列号已经完美对齐了,接下来可以放心大胆地传文件了!"
"""
python 复制代码
"""
深度思考:为什么"两次"不行?"四次"不多余吗?
	为什么不能是两次?(为了防止已失效的连接请求突然传到服务端,引起错误)

		假设场景:客户端发了第一个 SYN,但在网络堵车了。客户端以为丢包了,于是重发了第二个 SYN。这次网络很顺,两次握手成功,传完数据,连接关闭了。

		问题来了:这时候,那条堵车的第一个 SYN 终于开到了服务端。如果只要两次握手,服务端就会认为客户端又发起了一个新连接,于是进入 ESTABLISHED 状态在原地苦苦等待。但客户端实际上根本没想发数据,服务端的资源就被白白浪费了。

		有了第三次握手:服务端收到迟到的 SYN 即使回了 SYN+ACK,客户端发现自己没发过这个请求,就不会理它,服务端迟迟收不到第三次 ACK,就会知道这个连接是无效的。
"""

三、四次挥手

TCP 的连接是双向全双工的,断开时必须两端分别独立关闭。

1. 第一次挥手:客户端主动关闭发送

  • 做什么:客户端不想发数据了,发送一个 FIN = 1 的报文,随机生成序号 seq = u。

  • 状态变化:客户端进入 FIN_WAIT_1(终止等待1) 状态。

  • 注意:此时客户端进入"半关闭"状态,它不能再发送应用数据,但如果服务端发来数据,客户端依然可以接收。

2. 第二次挥手:服务端确认,进入半关闭

  • 做什么:服务端收到 FIN 报文,发送一个 ACK = 1 进行确认,确认号 ack = u + 1,带上自己的当前序号 seq = v。

  • 状态变化:

    • 服务端进入 CLOSE_WAIT(关闭等待) 状态。这时候应用层会被通知:对方要断开了。

    • 客户端收到这个 ACK 后,进入 FIN_WAIT_2(终止等待2) 状态。

  • 此时底层:此时 TCP 连接处于半关闭(Half-Close)。客户端到服务端的这条路断了,但服务端如果还有数据没发完,可以继续往客户端发。

3. 第三次挥手:服务端数据发完,申请关闭

  • 做什么:服务端把最后缓存的数据全部发送完毕后,也想关闭连接了。于是向客户端发送 FIN = 1, ACK = 1 的报文,由于中间可能发了些数据,它的当前序号变成了 seq = w,确认号依然保持 ack = u + 1。

  • 状态变化:服务端进入 LAST_ACK(最后确认) 状态。

4. 第四次挥手:客户端最终确认

  • 做什么:客户端收到服务端的 FIN 报文,必须最后回复一个 ACK = 1 确认,ack = w + 1,seq = u + 1。

  • 状态变化:

    • 服务端收到该 ACK 后,直接进入 CLOSED 状态,服务端断开完成。

    • 客户端发送后,进入 TIME_WAIT(时间等待) 状态。在这个状态死等 2MSL(Maximum Segment Lifetime,报文最大生存时间)之后,如果没有异常,才最终进入 CLOSED 状态。

四、思考和问题:

1. 为什么第四次挥手后,客户端必须在 TIME_WAIT 状态等 2MSL?

  • 为了保证最后一次 ACK 能够安全送达服务端

    • 网络是不可靠的。假设客户端发出的第四次 ACK 在路上丢包了,服务端在 LAST_ACK 状态下迟迟收不到确认,就会认为自己的第三次 FIN 丢了,于是重发 FIN。 如果客户端发完 ACK 就直接 CLOSED 了,当服务端的重发 FIN 到达时,客户端已经找不到对应的连接了,就会回复一个 RST(复位重置)报文,这会导致服务端报错(通常是 Connection reset by peer),无法优雅地正常关闭。 客户端等 2MSL(一去一回的最长时间),就能保证如果服务端没收到,能在该时间内重发,客户端也能在这等它并重新补发 ACK。
  • 防止"已失效的连接请求报文"出现在新连接中

    • 等待 2MSL 的时间,足以让本次连接在网络中产生的所有残留报文(比如在某个路由器里堵车延迟的报文)全部死掉并消失。这样当下次你再用相同的 IP 和端口建立新连接时,就绝不会收到上一次连接遗留下来的脏数据

2. 什么是 已失效的连接请求报文

假设你在家里用电脑连接服务器,整个故事分为前世和今生:

  • 前世(上一次连接):

    • 客户端发送了一个连接请求(SYN 报文,假设它的序列号 seq = 100)。
    • 结果这个报文走到半路,网络大堵车,它被卡在某个慢吞吞的路由器缓存里了。
    • 客户端等了半天没回应,以为丢包了,于是重新发了一个 SYN(seq = 200)。
    • 这次很顺利,三次握手成功,数据传完了,双方四次挥手正常断开。
    • 重点来了: 那个卡在半路的第一个 SYN(seq = 100)依然活着,它在网络里继续晃悠。这个报文,就叫做"已失效的连接请求报文"。
  • 今生(紧接着发起的新连接)如果挥手后不等待,会发生什么恐怖的事?

    • 上一次连接断开后,你马上又用相同的 IP 和相同的端口号向同一个服务器发起了新连接。

    • 就在新连接刚刚建立好,双方准备传数据的时候,那个在网络里堵车很久的、前世遗留下来的 SYN(seq = 100)突然满血复活,终于开到了服务器!

    • 如果四次挥手时客户端没有 TIME_WAIT 的限制,而是发完最后一个 ACK 立刻彻底关闭,那么:

      • 服务器看到这个延迟大半天的 SYN,误以为是客户端又发起了一次全新的请求。

      • 服务器就会一本正经地回复 SYN + ACK。

      • 此时客户端会莫名其妙地收到一个来自前世的确认报文,导致整个 TCP 的状态机彻底陷入混乱,甚至可能把前世没发完的旧数据,混进今生的新数据里,导致传输的文件损坏!

3. 四次挥手时的 TIME_WAIT 是如何解决这个问题的?

为了消灭这种"跨时空污染",TCP 规定:在四次挥手的最后一步,客户端发送完最后一个 ACK 后,连接并没有真正关闭,而是把端口锁死,进入 TIME_WAIT 状态,死等 2MSL 时间。

  • MSL(Maximum Segment Lifetime):是一个报文在网络里能活下来的最大时间。如果超过这个时间,网络里的路由器就会无情地把它丢弃。

    • 在现代 Linux 操作系统中,默认的 MSL 通常被硬编码设置为 30 秒(所以 2MSL 就是 60 秒)。
    • 在老旧的系统或标准的 TCP 规范(RFC 793)中,官方建议的 MSL 甚至是 2 分钟(2MSL 就是 4 分钟)。
  • 为什么要等 2 倍(2MSL)? 因为 1 个 MSL 保证客户端发出的最后一个 ACK 能到达对方;万一这个 ACK 丢了,服务端重发的 FIN 传回来,又需要 1 个 MSL。这一来一回刚好是 2MSL。

4. 2MSL 真正等的是谁?

  • TIME_WAIT 的 2MSL 时间,是从第四次挥手(客户端发送最后一个 ACK)的那一刻才开始倒计时的。==注意这里2msl只是从第四次挥手那一刻开始倒计时,并不只是为了熬死第四次发出去后失效的报文,而是为了熬死所有属于这次连接的、活着的报文==
  • 它等的是在挥手前后这一小段时间内,网络里可能刚刚产生的、还在赶路的、属于本次连接的所有残余报文。
  • 也就是说,TCP 强制全员在原地罚站 2MSL,就是为了保证此时此刻网络中所有属于这次连接的、活着的报文,全部超过 1个 MSL 的寿命而死绝。

5. 从第四次挥手后有多个丢失的报文 2MSL 够用吗?重传机制和最大重传次数限制

绝对够用。不管丢了多少个报文,2MSL 都是理论和实际上的"终极安全边界"。 这里的核心秘密在于:2MSL 的倒计时并不是一成不变的,它是可以被"无限续期(刷新)"的。

  • 场景推演:如果第四次挥手的 ACK 丢了 假设客户端发出第四次挥手的 ACK,并且网络里还有其他残留报文在乱飞:

    • 客户端: 发出最后一个 ACK,开始 2MSL 倒计时(假设一共要等 2 分钟)。

    • 网络中: 这个 ACK 走到半路丢了。

    • 服务端: 因为没收到 ACK,触发了超时重传机制。服务端会认为:"我刚才发的第三次挥手 FIN 是不是丢了?" 于是服务端重新发送了一个 FIN。

    • 客户端: 还在 2MSL 的"原地罚站"状态中。突然,它又收到了服务端重传的 FIN。

    • 【关键机制触发】: 客户端只要在 TIME_WAIT 期间收到任何来自对方的旧报文(比如重传的 FIN),它就会重新发送一次 ACK,并且把 2MSL 的定时器直接清零,重新开始倒计时 2 分钟

  • 为什么说不管丢多少个报文都够用? 通过上面的重传机制,你会发现 2MSL 的真正威力在于:

    • 只要网络里还有"活着的旧报文"折腾: 服务端就会因为收不到确认而不断重传 FIN。客户端每收到一次 FIN,就会续命(刷新)一次 2MSL。

    • 直到什么时候才停止? 直到某一时刻,客户端发出的 ACK 终于成功送达服务端,服务端彻底关闭(CLOSED),不再重传了;或者服务端重传达到最大次数强制断开。

    • 最后的平静: 当网络里最后一个重传的 FIN 报文传到客户端,或者在路上死掉之后,网络里就再也没有任何新的报文产生了。

    • 2MSL 开始最后的清空: 客户端以这"最后一个报文出现的时间"为起点,孤独地等待完整的 2MSL。

      • 1 个 MSL: 保证客户端最后补发的那个 ACK 要么到了,要么死在路上了。

      • 第 2 个 MSL: 保证服务端如果没收到、再次重传的 FIN(如果还有的话)在通往客户端的路上彻底死掉。

    • 当这最后的 2MSL 顺利走完,意味着全网所有可能在赶路的、重传的、迷路的、错乱的报文,已经全部超过寿命上限,无一幸免,全部死绝了。

  • 总结

    • 2MSL 够用,不是因为时间长,而是因为 2MSL 永远是以"网络中最后一点风吹草动"为起点开始计算的。它是一座永远能帮网络"清空历史、重头再来"的终极安全防御塔。

6. 为什么是2MSL 不是1MSL?

等 1 个 MSL 只能保证"单向"的报文死绝;而等 2 个 MSL,才能保证"一去一回"双向的报文在全网彻底死绝。
因为第四次挥手时,客户端面临的不是一个平静的网络,它刚刚发出了最后一个 ACK,而服务端可能还在痴痴地等。为了搞懂为什么必须是 2 倍,我们来推演一下如果只等 1 个 MSL,网络会发生怎样惨烈的"连环撞车事件"

  • 致命推演:为什么 1 个 MSL 绝对不够? 假设我们把时间卡在第 0 秒:客户端向服务端发送了第四次挥手的最后一个 ACK。 此时,这个 ACK 在网络中最多能活 1 个 MSL。

  • 倒霉鬼场景:

    • 第 0 秒: 客户端发出最后的 ACK。

    • 第 0.9 个 MSL 的时候: 这个 ACK 运不好,走到半路丢包(死掉)了。

    • 服务端开始发飙: 服务端在左等右等了将近 1 个 MSL 之后,依然没有收到 ACK。由于服务端的超时重传机制,它判定自己刚才发的第三次挥手 FIN 丢了。于是,服务端在第 1 个 MSL 的瞬间,重新打包并发送了一个新的 FIN 报文。

    • 客户端如果只等 1 个 MSL: 就在服务端发出重传 FIN 的同一秒,客户端的"1个MSL定时器"到期了。客户端开心地想:"太好了,1个MSL到了,我自由了!" 于是客户端瞬间关闭了端口(CLOSED)。

    • 灾难发生了: 服务端在第 1 个 MSL 时发出的那个重传的 FIN,才刚刚出发啊! 这个全新的 FIN 报文在网络里又可以活 1 个 MSL。它晃晃悠悠地往客户端飞去。 此时,客户端已经关闭了,或者更糟------客户端在第 1.001 个 MSL 的时候,立刻用相同的 IP 和端口启动了一个新连接! 结果,在第 1.5 个 MSL 的时候,那个服务端重传的旧 FIN 突然拍在了客户端新连接的脸上。新连接瞬间风中凌乱,直接崩溃。

  • 2MSL 的精妙之处:闭环的时间对称性 为了堵住上面这个由于"超时重传"导致的致命时间差,计算机科学家设计了 2个 MSL。 这 2个 MSL 的分工极其明确,刚好对应一去一回的极限时间:

    • 第 1 个 MSL("去"的极限): 用来等待客户端发出的最后一个 ACK 走到服务端。如果这个 ACK 顺利走完 1 个 MSL 还没到,说明它在路上死了。
    • 第 2 个 MSL("回"的极限): 如果 ACK 死了,服务端会在最极限的时刻(大约第 1 个 MSL 结束时)触发重传,把旧的 FIN 再次拍出来。这个新出来的 FIN 飞回客户端,又最多需要 1 个 MSL 的时间。
  • 总结

    • 客户端在第四次挥手后老老实实原地罚站 2MSL,本质上是在用时间做一堵双向防火墙:

      • 如果网络好,ACK 在第 1 个 MSL 内到了,服务端安静关闭。剩下的 1 个 MSL 时间里,网络一片寂静,客户端安全退出。

      • 如果网络坏,ACK 丢了,服务端重传的 FIN 必然会在 2MSL 期间传到客户端。客户端一旦收到,就会立刻重发 ACK 并刷新(重置)2MSL 定时器,重新再等 2 分钟,直到网络彻底干净为止。

    • 所以,1 个 MSL 只能保证自己发的信息死透,2 个 MSL 才能保证对方因为没收到而产生的"报复性重传"也死透。 TCP 用这个天才的对称性设计,换来了互联网几十年的稳定。

7. 为什么这2MSL等待不在服务端 而在客户端?

这里有两个最核心的底层设计考量:"谁主动,谁负责" 的原则,以及对 服务器资源的保护。

  • 核心铁律:谁主动关闭,2MSL 就在谁身上

    • 其实,TCP 协议并没有规定 TIME_WAIT 必须在"客户端"。它的准确规定是:谁先发起主动关闭(Active Close),2MSL 的等待就由谁来承担。

    • 在绝大多数网络场景下(比如你用浏览器访问网站),都是客户端传完数据、看完网页后,主动划掉页面或者断开连接。因为是客户端主动发起的断开,所以客户端承担了这 2MSL 的等待。

    • 如果网站服务器因为某些原因(比如超时、或者垃圾清理)主动把你的连接断开了,那这个 2MSL 的 TIME_WAIT 状态就会留给服务器。

  • 为什么要把这个负担"尽量"压在客户端身上?

    • 原因一:服务器的端口资源极其宝贵(怕端口耗尽)

      • 客户端的视角: 客户端作为一台个人电脑,同一时间可能也就建立几十个、几百个网络连接。让它拿出一个端口原地罚站 2 分钟,无伤大雅,用户根本感知不到。

      • 服务端的视角: 服务器(比如一台高并发的 Web 服务器)每秒钟可能要处理几万个用户的连接。如果每个用户离开后,服务器都要为这个连接默默死等 2 分钟,那么短时间内服务器上就会堆积几十万个处于 TIME_WAIT 状态的死连接。

      • 后果: 操作系统的端口号和文件描述符是有限的(通常一个 IP 最多 65535 个端口)。如果全部被 TIME_WAIT 占满了,服务器就会陷入"端口耗尽"的窘境,再也无法接收任何新用户的请求了。

    • 原因二:符合"保全大局"的设计哲学

      • 在网络架构中,有一个核心原则叫:"把负担和状态尽量推向网络的边缘(客户端),保证网络核心(服务端)的高效和轻量。"
      • 客户端崩了,只影响这一个小明;但如果服务器因为端口被死等的连接占满而崩了,全天下几万个小明、小红都无法访问网站了。所以,这个"负责收尾清空幽灵报文"的安全重任,自然要由客户端来扛。
  • 如果服务端不得不"主动关闭"怎么办? 在实际开发中,有时候服务器必须主动断开不听话的客户端(比如客户端恶意占用连接不发数据)。这时,服务器就会不可避免地进入 TIME_WAIT 状态。

    为了防止上面说的"服务器端口耗尽"导致瘫痪,现代网络编程和操作系统提供了几种高级外挂:

    • SO_REUSEADDR(端口复用): 这是后端开发中最常用的套接字选项。它告诉操作系统:"就算这个端口现在处于 TIME_WAIT 状态,只要新来的连接序列号对得上,允许新连接直接抢用这个端口!"(直接打破 2MSL 的死等限制)。
    • 发出 RST 报文直接闪退: 服务器如果想彻底甩开 2MSL 的纠缠,可以直接向客户端发送一个 RST(复位)报文,而不是走正常的四次挥手。这相当于粗暴地直接挂断电话,不触发四次挥手,也就完全不会产生 TIME_WAIT 状态。
  • 总结

    • 2MSL 留给客户端,是因为客户端往往是主动说再见的那个人。TCP 协议让"主动说再见的人负责留下来打扫战场",既能保证网络数据的绝对安全,又能完美保护服务器不被海量的死连接活活拖垮。
相关推荐
abcefg_h2 小时前
HTTP 协议版本演进:从 TCP 连接到 QUIC
网络·网络协议·http
liulilittle2 小时前
拥塞控制:公平性的不可能三角
网络·c++·网络协议·tcp/ip·计算机网络·tcp·通信
zzz_23687 小时前
【Spring】面试突击系列(六):Spring 工程实践与面试综合
java·spring·面试
牛油果子哥q7 小时前
【C++ this指针】C++ this指针深度精讲:this底层本质、存储位置、调用机制、const this指针、空指针调用、面试坑点与工程实战
开发语言·c++·面试
用户47949283569157 小时前
盛大集团面经(专升本毕业 9 个月,99%代码靠 AI 写)
面试
Elias不吃糖7 小时前
AI Resume Forge:基于 LangGraph 的 AI 简历优化与模拟面试平台
java·人工智能·面试·agent开发
程序员二叉8 小时前
【Java】String 全套高频面试题详解
java·开发语言·面试
8125035338 小时前
第 8 篇:IP 地址:互联网的门牌号
网络·网络协议·tcp/ip
小江的记录本8 小时前
【Spring全家桶】Spring AI核心原理、大模型集成、Prompt工程、RAG实现、AI Agent开发(附《思维导图》+《面试高频考点清单》)
java·人工智能·spring boot·后端·spring·面试·prompt