来看下这个图:
具体描述如下:
-
客户端发起连接:
-
客户端向服务器发送一个SYN(同步序列编号)报文,其中包含客户端选择的初始序列号(seq=x),并请求建立连接。
-
客户端进入SYN_SENT(同步已发送)状态,等待服务器的回应。
-
-
服务器响应连接请求:
-
服务器收到客户端的SYN报文后,会回复一个SYN-ACK(同步序列编号-确认)报文。
-
在SYN-ACK报文中,服务器会确认客户端的SYN(通过ack=x+1实现),并发送自己的初始序列号(seq=y)。
-
服务器同时处于SYN_RCVD(同步收到)状态,等待客户端的最终确认。
-
-
客户端确认服务器响应:
-
客户端收到服务器的SYN-ACK报文后,会发送一个ACK(确认)报文给服务器。
-
在ACK报文中,客户端会确认服务器的SYN(通过ack=y+1实现),并再次发送自己的序列号(虽然这个序列号在后续的数据传输中更为重要,但在三次握手中发送是为了完成握手过程)。
-
客户端和服务器在收到对方的最终确认后,都进入ESTABLISHED(已建立连接)状态,此时TCP连接已成功建立,双方可以开始传输数据。
-
扩展知识
为什么三次能阻止这个问题?
并且如果网络阻塞时间较长,发送方可能多次发送请求,且接收方还可能全部接受这些连接(它不清楚,以为都是有效的),这就造成了不必要的资源的浪费。
如果要避免这种情况发生,两次通信是不够的。发送方需要知晓接收方到底接受了哪个连接,如果接受的是老连接,那么发送方需要告知接收方,这个连接不对!也就是 RST 通知。如果对,那么就返回 ACK 告诉接收方 OK!这就使得一次握手至少需要 3 次。
这好像四次了?如果真的按一来一回就是四次,但是中间一步可以合并,就是接收方告知发送方收到初始序号的同时将自己的初始序号告诉发送方。
因此四次握手就可以减到三次了。
通俗理解后,我们再看下这张图:
-
发送方通过 SYN 控制消息并携带自己期望的初始序列号 SEQ 给接收方
-
接收方收到 SYN 消息之后,通过 ACK 控制消息以及 SEQ+1 来进行确认,并带上自己的 SEQ
-
发送方通过 ACK 控制消息以及接收方的 SEQ+1 来进行确认,并且还能够在第三次握手通信的同时,直接携带数据进行传输
为什么不是两次握手?
因为两次握手无法阻止历史连接的建立,使得资源的浪费,也不能正确地同步序列号。
为什么不是四次握手?
理论上 3 次以上的握手都行,但是 3 次就已经够用了,没必要选择更多的握手次数。
三次握手的具体内容:
1. 第一次握手
-
发起方:客户端
-
操作:客户端向服务器发送一个SYN(同步序列号)报文段,其中SYN标志位被设置为1,表示这是一个连接请求报文。同时,客户端会选择一个初始序列号seq=x,并将其发送给服务器。
-
状态变化:客户端发送SYN报文后进入SYN_SENT状态,等待服务器的确认。
2. 第二次握手
-
响应方:服务器
-
操作:服务器收到客户端的SYN报文后,会向客户端发送一个SYN+ACK(同步序列号+确认序号)报文段作为应答。在这个报文段中,SYN标志位也被设置为1,表示这也是一个连接请求报文(虽然实际上是应答报文,但服务器也需要发送自己的SYN报文以建立连接)。同时,服务器会确认客户端的SYN(通过ack=x+1来实现),并选择一个自己的初始序列号seq=y发送给客户端。
-
状态变化:服务器发送SYN+ACK报文后进入SYN_RCVD状态,等待客户端的确认。
3. 第三次握手
-
发起方:客户端
-
操作:客户端收到服务器的SYN+ACK报文后,会向服务器发送一个ACK(确认序号)报文段作为最后的确认。在这个报文段中,ACK标志位被设置为1,表示这是一个确认报文。客户端的确认序号ack=y+1,表示它已经收到了服务器的SYN报文,并准备基于这个序列号开始发送数据。同时,客户端会再次发送自己的序列号seq=x+1(虽然这个序列号在连接建立后主要用于数据的发送和确认,但在第三次握手中也会发送以完成握手过程)。
-
状态变化:客户端发送ACK报文后,如果服务器也成功收到了这个ACK报文,则双方都会进入ESTABLISHED(已建立连接)状态,表示TCP连接已经成功建立,可以开始传输数据了。