TCP三次握手的智慧:为什么不是两次或四次?

前一段时间我一个之前的同事,他问我知道TCP的三次握手吗?其实说实话我并不能说的那么明白,我问他你的事怎么回答的,他说光说了个三次握手的名字,然后面试官问为什么要握手三次,少握一次或者多握一次又问题吗?

那么我们今天就来分析分析这个问题?

一、三次握手

客户端和服务端两个端初次见面时的场景:

  1. 客户端发起请求 :主动说"你好!"(发送SYN报文
  2. 服务端确认并回应 :回答"收到了,你也好呀!"(发送SYN-ACK‌报文)
  3. 客户端最终确认 :回应"收到你说的:你要呀信息了"(发送ACK‌报文)

至此,双方均确认了彼此的发送能力和接收能力,可靠的双向通道建立完成。

关键点 :ACK(Acknowledgment)的本质是告知服务端"我收到了你之前发的某个数据"。第2步中服务端的SYN-ACK,既是对客户端SYN的确认(ACK),也是自己发起连接的请求(SYN)。

二、如果是两次握手的话

假设简化为两次握手(客户端SYN -> 服务端SYN-ACK即建立连接):

场景再现

  1. 客户端发送SYN(seq=100),假如说网络拥堵延迟
  2. 客户端超时重传SYN(seq=200),这次顺利到达,服务端响应SYN-ACK(ack=201),连接建立,正常通信后关闭。
  3. 此时,第一个迟到的SYN(seq=100)终于到达服务端!
  4. 服务端认为这是新请求,响应SYN-ACK(ack=101)并认为连接已建立
  5. 客户端收到此SYN-ACK,发现确认的序列号是101(它期望的是201或更新),会立刻回复RST(复位)包拒绝。
  6. 但服务端在收到RST之前,连接状态已是"已建立"。那么此时问题就来了:
    • 资源占用:服务端为该响应超时的连接分配了内存等资源。
    • 状态不一致 :服务端认为连接有效,客户端认为不存在。若服务端此时发送数据,客户端会以RST回应,但资源已被无意义占用

三次握手是如何解决的 :在第3步,客户端收到SYN-ACK后,能根据序列号判断这个连接请求是否过时 。如果是过时的请求,客户端不会发送ACK,服务端也就不会建立连接。服务端仅在收到有效的ACK后才真正建立连接状态

三、两次握手不行,那多握手一次应该可以吧?

这时候我们可能会想,两次不行,那就握四次,双向确认嘛,多好的然后就有了:

  • 客户端:SYN(我要连你)
  • 服务端:ACK(知道你连我)-> 确认了客户端的发送能力
  • 服务端:SYN(我也要连你)
  • 客户端:ACK(知道你也连我)-> 确认了服务端的发送能力

这样有什么问题吗?人家本来服务端的ACK和SYN可以放在一个报文里面发送,非要拆成两个

  1. 增加延迟:多了一次网络往返。
  2. 浪费资源:多处理一个报文。

四、所以三次握手是刚刚好的

  • 状态同步 :三次握手是通信双方可靠地同步初始序列号 的过程。初始序列号(ISN)用于标识字节流顺序,防止旧报文干扰新连接。双方必须确认对方知晓并认可自己的初始序列号。 三次设计是在效率、可靠性和资源安全之间找到的最小充分解,确保双方对连接状态达成唯一、一致的共识。

好了如果以后再面试中碰到这类问题,这样回答的话应该是可以的了!

相关推荐
用户3074596982072 小时前
门面(Facade)—— 静态语法的“动态伪装术”
后端·php
辜月十2 小时前
CentOS7 离线安装字体
后端
superman超哥2 小时前
仓颉语言中流式I/O的设计模式深度剖析
开发语言·后端·设计模式·仓颉
豆浆whisky2 小时前
Go内存管理最佳实践:提升性能的Do‘s与Don‘ts|Go语言进阶(17)
开发语言·后端·golang
Kay_Liang2 小时前
Spring中@Controller与@RestController核心解析
java·开发语言·spring boot·后端·spring·mvc·注解
weixin_497845542 小时前
Windows系统Rust安装慢的问题
开发语言·后端·rust
IT_陈寒3 小时前
React性能优化:10个90%开发者不知道的useEffect正确使用姿势
前端·人工智能·后端
Apifox3 小时前
如何在 Apifox 中使用 OpenAPI 的 discriminator?
前端·后端·测试
yuuki2332333 小时前
【数据结构】双向链表的实现
c语言·数据结构·后端
朝新_3 小时前
【SpringBoot】玩转 Spring Boot 日志:级别划分、持久化、格式配置及 Lombok 简化使用
java·spring boot·笔记·后端·spring·javaee