为什么是三次握手?
成本转移
主要原因是 “将连接异常的处理成本转接到客户端上”
Why?
在CS模式(Client and Server)中,客户端是主动发起连接请求的一方,服务器端是等待接收连接请求的一方。因为服务器一般会同时维护多个连接,处理来自不同客户端的数据,如果连接失败的成本让服务端承担,会导致服务器性能下降,浪费服务器资源,所以连接失败的成本最好转接到客户端上。
网络,千变万化,你服务器无法保证每次(🤝🏻第一次握手)收到的SYN报文是真正需要建立连接的,如以下情况:
- 历史连接:假设client曾经向server发送过一个
SYN
报文(旧SYN
),但由于网络阻塞,client迟迟收不到来自server的ACK
,因此client超时重传,向server发送一个新的SYN
报文(注意,此时旧SYN
可能还在网络中向server进发),旧SYN
可能会比新SYN
先到达。
🤝🏻第二次握手是server和client说 “我收到你的连接请求(ACK),我们来连接吧(SYN)!”。client收到来自server的SYN+ACK
报文后,会确认server请求建立的连接是否与client期望建立的连接相同(client通过收到的SYN+ACK
报文中的ACK Number
和当前上下文信息对比,进行验证,随后在第三次握手中,告知server验证的结果)
🤝🏻第三次握手,client告知server,二者期望建立的连接是否为同一个。
-
YES!client向server发送
ACK
报文,表示 “没问题,我们建立连接吧!”(此时client已经建立了连接,它认为连接建立成功了,ACK
可以携带数据发回server。server若收到最后的ACK
报文,即into ESTABLISHED状态,连接建立成功)。如果server若没有收到“第三次握手”的ACK,自然不会建立连接。 此时client和server对于连接建立的认知不一致:-
client认为连接建立成功了,它在本地也创建了连接资源(socket、序列号、窗口等)
-
server处于SYN_RCVD迟迟没有收到
ACK
(超时重传SYN+ACK
后还收不到的话),结束,最后并没有建立连接,没有因为连接异常产生资源浪费。这两个点,正体现了**“将连接异常的处理成本转接到客户端上”**。后续client若向server发送报文,server收到后,发现该报文不是本地连接发来的(对比报文中的端口号和本地连接的端口号),随即向client发送
RST
报文,告诉它“你还没和我建立连接呢!请建立连接后再给我发送数据”
-
-
NO!client向server发送
RST
报文,表示 “异常!请终止这个连接的建立!”
得出结论,在三次握手的情况下:
- 服务端有一个中间状态(SYN_RCVD),可以给客户端来阻止历史连接在服务端上的建立
- 最后一次应答报文由客户端发出,连接失败的成本由客户端承担
如果是两次握手连接,server不管收到任何SYN
报文,都直接建立连接,没有确认该报文的合法性,久而久之会造成服务器严重的造成严重的空间泄漏。
所以,为什么是三次握手?
- 为什么不是两次握手?三次握手刚好能满足:将连接异常的处理成本转接到客户端上,两次握手不行。
- 为什么不是四次握手?三次握手就已经能够通过最少的次数建立可靠的连接,所以不需要使用更多的握手次数。(第二次握手可以拆成两次,那最后就是四次握手,但是第二步和第三步可以优化成一步,所以就成了三次握手)
同步双方初始序列号
另外一个原因,通信双方在握手建立连接时,需要 “同步双方初始序列号”(初始序列化是双方各自随机生成的,要想保证后续通信数据的可靠性,必须提前通知对方)
- client向server发送携带 初始序列号(client_isn) 的
SYN
报文后,server应该向client回复一个ACK
报文,表示双方同步了client的初始序列号client_isn - 同理,server向client发送携带 初始序列号(server_isn) 的
SYN
报文后,client也应该向server回复一个ACK
报文,表示双方同步了server的初始序列号server_isn,client也应该向server回复一个ACK
报文,表示双方同步了server的初始序列号server_isn
这样一来一回,就同步了双方的初始序列号。理论上需要四次握手,但是第二、第三步可以合成一步(SYN+ACK
),最后得出了三次握手。