TCP的三次握手
本文讲解TCP三次握手:一个为建立可靠连接而设计的双向确认过程。客户端发起请求(SYN),服务器响应确认和请求(SYN-ACK),最后客户端再次确认(ACK)。此过程确保双方收发正常,并防止失效连接请求。
我们来详细、通俗地讲解一下TCP的三次握手(Three-way Handshake)原理。
一、一句话比喻:打电话
想象一下你和朋友打电话的过程,这个过程和三次握手非常相似:
第一次握手(你打给朋友):
- 你:“喂,能听到我说话吗?”
- 你的目的:确认朋友能收到你的信息。
第二次握手(朋友回应你):
- 朋友:“我听到了。你能听到我说话吗?”
- 朋友的目的:1. 告诉你他听到了(回应你的请求)。 2. 问你是否能听到他说话(发起自己的请求)。
第三次握手(你再次回应朋友):
- 你:“我也能听到你。好了,我们可以开始聊天了。”
- 你的目的:告诉朋友你收到了他的信息,现在双方都确认通信线路是通畅的。
经过这三步,双方都确认了自己和对方的发送和接收能力都是正常的,然后才能开始真正地通话。TCP三次握手就是为了在网络中建立这样一个可靠的双向通信信道。
二、三次握手的核心目的
TCP是一种可靠的、面向连接的传输协议。在正式传输数据之前,必须先建立一个可靠的连接。三次握手的核心目的主要有三个:
- 确认双方的收发能力:确保客户端(Client)和服务器(Server)都能正常发送和接收数据。
- 同步初始序列号(ISN, Initial Sequence Number):TCP会为每个发送的字节分配一个序列号(Sequence Number, SEQ),以保证数据的有序和不重复。握手过程就是为了协商和同步双方的初始序列号。
- 防止已失效的连接请求:避免因为网络延迟等原因,导致早已失效的连接请求突然到达服务器,从而错误地建立连接,浪费服务器资源。
三、技术细节:三次握手的详细过程
在TCP协议中,数据包的头部有几个非常重要的标志位(Flags),在握手过程中主要用到两个:
- SYN (Synchronize):同步标志位。当
SYN=1时,表示这是一个请求建立连接的报文。 - ACK (Acknowledge):确认标志位。当
ACK=1时,表示这是一个确认报文,此时确认号(Acknowledgement Number)字段才有效。
此外,还有两个关键的数字:
- 序列号 (Sequence Number, seq):我方发送的数据的初始编号。
- 确认号 (Acknowledgement Number, ack):我方期望下次收到的、对方数据的编号。
下面是三次握手的具体步骤(假设客户端主动发起连接):
第一次握手(Client -> Server)
- 状态:客户端处于
CLOSED状态,服务器处于LISTEN状态。 - 客户端操作:
- 客户端随机选择一个初始序列号
seq = x。 - 将TCP报文的标志位
SYN设置为1。 - 将报文发送给服务器。
- 客户端随机选择一个初始序列号
- 报文内容:
SYN = 1,seq = x - 客户端状态变化:发送后,客户端进入
SYN_SENT(同步已发送)状态,等待服务器确认。
通俗解释:客户端对服务器说:“你好,我想和你建立连接,我的初始序列号是 x,你收到了吗?”
第二次握手(Server -> Client)
- 服务器操作:
- 服务器收到客户端的SYN报文后,也随机选择一个自己的初始序列号
seq = y。 - 为了确认已收到客户端的报文,服务器将确认号设置为
ack = x + 1。(表示“我收到了你的序列号x,我希望你下一个发来的数据从x+1开始”)。 - 将TCP报文的标志位
SYN和ACK都设置为1。 - 将报文发送给客户端。
- 服务器收到客户端的SYN报文后,也随机选择一个自己的初始序列号
- 报文内容:
SYN = 1,ACK = 1,seq = y,ack = x + 1 - 服务器状态变化:发送后,服务器进入
SYN_RCVD(同步已接收)状态。
通俗解释:服务器对客户端说:“我收到你的请求了(确认号是x+1)。我也想和你建立连接,我的初始序列号是 y,你收到了吗?”
第三次握手(Client -> Server)
- 客户端操作:
- 客户端收到服务器的
SYN+ACK报文后,检查确认号ack是否为x + 1。如果正确,则表示到服务器的通路是正常的。 - 为了确认已收到服务器的报文,客户端将确认号设置为
ack = y + 1。(表示“我收到了你的序列号y,我希望你下一个发来的数据从y+1开始”)。 - 将TCP报文的标志位
ACK设置为1。 - 将自己的序列号设置为
seq = x + 1。 - 将报文发送给服务器。
- 客户端收到服务器的
- 报文内容:
ACK = 1,seq = x + 1,ack = y + 1 - 双方状态变化:
- 客户端发送后,进入
ESTABLISHED(连接已建立)状态,此时可以开始发送数据。 - 服务器收到客户端的ACK报文后,也进入
ESTABLISHED状态,连接建立成功。
- 客户端发送后,进入
通俗解释:客户端对服务器说:“我收到你的确认和请求了(确认号是y+1),现在我们可以开始通信了。”
至此,三次握手完成,双方都进入了 ESTABLISHED 状态,可以进行双向数据传输。
四、为什么是三次,而不是两次或四次?
这是一个经典的面试问题。
为什么不能是两次握手?
主要原因是为了防止已失效的连接请求报文突然又传送到了服务器。
- 场景:客户端发送了一个SYN请求(我们称之为
SYN_old),但因为网络拥堵,这个请求很久都没有到达服务器。 - 客户端行为:客户端等了一段时间后,发现没有回应,于是重新发送了一个新的SYN请求(
SYN_new)。 - 正常流程:
SYN_new正常到达服务器,服务器回复,双方通过两次握手(假设)建立了连接,然后传输数据,最后关闭连接。 - 问题出现:此时,那个在网络中滞留的
SYN_old终于到达了服务器。- 如果是两次握手:服务器收到
SYN_old后,会误以为这是一个新的连接请求。它会立即向客户端发送确认报文,并分配资源,进入ESTABLISHED状态。但此时客户端实际上处于关闭状态,不会理会这个确认。结果就是,服务器单方面建立了一个“半开连接”(Half-Open Connection),并一直等待一个永远不会来的客户端数据,造成了资源浪费。 - 如果是三次握手:服务器收到
SYN_old后,会回复一个SYN+ACK报文。但客户端发现这个报文的序列号对不上(或者它根本就不想建立连接),就不会发送第三次握手的ACK。服务器收不到这个ACK,就知道客户端并不想建立连接,于是就不会进入ESTABLISHED状态,也就避免了资源浪费。
- 如果是两次握手:服务器收到
简单来说,第三次握手是让服务器确认“客户端确实收到了我的回复,并且真的准备好了建立连接”。
为什么不需要是四次握手?
因为服务器在第二次握手中,将自己的 SYN 请求和对客户端的 ACK 确认,合并在一个报文中发送了 (SYN+ACK)。这完全可以满足需求,并且比分开发送(先发ACK,再发SYN)更高效。所以,没有必要进行四次握手。
总结
TCP三次握手是一个精心设计的过程,它通过三次信息交换,确保了通信双方都具备可靠的收发能力,并同步了各自的初始序列号,最终建立起一个全双工的、可靠的通信通道。它既保证了连接的可靠性,又兼顾了效率。