TCP握手为什么是三次?两次行不行?

我们都知道TCP建立连接需要三次握手,而对于这个常见的面试题,我想记录一下相对比较好记也比较合适的答案!

一、首先来看一下TCP握手过程图

图源TCP三次握手、四次挥手及状态转换图 - wj_hubei - 博客园 (cnblogs.com)

我们想一下,三次握手每次都干了什么?

一次握手【客户端发起连接,发送客户端的SYN(一种很小的数据包,SYN攻击会包含很多这种数据包)和初始化序列号seq1】:如果服务端成功接收到,那就可以说明服务端知道了客户端的发送能力正常,服务端的接收能力正常,主语是服务端

二次握手【服务端发送确认收到ACK报文(数值是客户端的初始化序列化seq值+1,可以保证回应客户端这次请求,而不会因为客户端迟迟没有收到服务端的确认报文导致客户端重传报文使得服务端回应混乱)SYN和服务端的初始化序列号seq2】:如果客户端成功收到服务端回应的ACK报文和初始化序列号seq2,可以说明客户端知道了客户端的接受能力正常,服务端的发送能力正常,主语是客户端

三次握手【客户端发送确认收到服务端数据包的ACK报文】:如果服务端成功收到客户端发送的确认收到ACK报文,那就说明服务端知道了客户端的接收能力正常,主语是服务端

TCP连接建立完成!双方都知道了对方可以接收和发送数据,所以可以开始全双工通信

现在我们来想一下,假如我们二次握手就结束了TCP连接,那服务端不就还不知道客户端能否正常接收就认为可以发送数据吗?如果仅改成两次握手,现在我们来看这样一种情况:C端即Client/客户端,S端即Server/服务端,C端向S端发起连接请求,S端返回确认ACK报文,发出返回ACK报文后S端其实不管客户端能否正常接收都认为TCP连接已经建立成功可以发送数据(因为假如是两次握手协议那就是不需要客户端发送确认ACK报文就认为已经建立TCP连接),那设想此时客户端无法正常接收呢?,或者说S端发送的ACK报文和初始化序列号数据包丢失呢?这些情况S端都没办法知道,并且S端已经认为可以发送数据,那S端就会开始发送数据,而此时C端如果不能正常接收,那这些发出的数据只是徒劳的占用链路带宽,而没有起到数据交互作用。这就是两次握手的弊端,也是三次握手的必然性。

我们来说一下上面这种情况具体是如何被三次握手解决的

由于还需要第三次握手,上述问题可以分两种情况:

1、S端发送的确认ACK报文和初始化序列号数据包丢失:S端迟迟收不到C端发来的确认收到ACK报文,经过一定时间后就会超时重传这个数据包,重传的ACK报文是一致的,初始化序列号是随机生成的另一个值,超时重传的时间是指数次增长的,下一次超时重传的时间是本次超时重传时间的两倍,当重传次数或时间超过设定的阈值后,就不再重传而是断开连接。超时重传机制可以解决数据包丢失的问题。

2、C端接收能力异常:显然连接是无法建立的,超时重传到一定时间也会关闭,也就避免了不必要的S端数据发送导致占用带宽

好了,文章的主体部分基本上就到这里。

下面讲一些补充内容

二、从双方的状态来看

刚开始C端是closed状态,S端是listen监听状态

第一次握手:C端向S端发送SYN报文和初始化序列号ISN(c)请求连接,C端就会转变为SYN_SENT状态

第二次握手:S端收到C端的请求连接报文后,就会以自身的SYN报文+初始化序列号ISN(s)+ACK作为应答,其中ACK=ISN(c)+1,此时S端转变为SYN_RCVD状态

第三次握手:C端收到S端的应答报文后,返回确认收到ACK报文,ACK=ISN(s)+1,表示收到了S端的SYN报文,此时转变为established状态

S端在确认收到ACK报文后,便会转变为established已建立连接状态!

三、三次握手的作用

  1. 确认双方接收能力、发送能力是否正常
  2. 指定自身的初始化序列号,为之后的可靠传输做准备

四、什么是半连接队列和全连接队列?

半连接队列 就是说服务端第一次收到客户端的SYN后,处于SYN_RCVD状态,此时双方还没有完全建立起连接,服务端会把这种状态下的请求连接放到一个特定的队列里,也就是半连接队列,可以暂存未完成三次握手的连接请求。

半连接队列的作用:

  1. 防止 SYN 攻击:SYN 攻击是攻击者发送大量伪造的连接请求(SYN 包),但不完成后续的握手过程,从而占用服务器资源。通过限制半连接队列的长度,可以一定程度上减轻 SYN 攻击对服务器的影响,因为半连接队列容量有限,攻击者无法无限制地占用队列空间。
  2. 防止资源耗尽:半连接队列可以限制同时等待三次握手完成的连接数量。如果没有半连接队列,服务器就不得不立即接受连接请求并处理三次握手,可能会导致服务器资源耗尽,进而无法处理更多的连接请求。

相应的,全连接队列 就是TCP完成三次握手建立连接之后存放请求连接的队列

完!

ps:文中内容是笔者自己的思考和想法,没办法保证绝对的正确,毕竟计网还是有点偏理论,偏抽象。如有错误,还望指正!!!

相关推荐
爱勇宝1 小时前
第 1 章:别把“需求文档”当成真正的需求
前端·后端·程序员
IT_陈寒6 小时前
闭包陷阱让我加了两天班,JavaScript你真行
前端·人工智能·后端
易协同低代码7 小时前
通达OA核心类库TD类深度解析
后端
Gopher_HBo7 小时前
Go语言学习笔记(十八)Gin处理Session
后端
谭光志8 小时前
工具塞满上下文窗口怎么办?深度拆解 AI Agent Tool Search 按需加载实现原理
前端·后端·ai编程
她说..8 小时前
Java 默认值设置方式
java·开发语言·后端·springboot
foggyprojects8 小时前
从0开始,一句话启动AI DataAgent
后端·数据分析·ai编程
郡杰8 小时前
一些基础和问题解决
后端
陈随易8 小时前
前端项目部署只要30秒
前端·后端·程序员
YIAN8 小时前
从零手写文件读取 MCP 服务:一文吃透 Model Context Protocol 全链路通信原理
前端·后端·mcp