说说 TCP 的三次握手?

TCP 的三次握手(Three-Way Handshake)是建立 TCP 连接的核心过程,通过三次数据包交互,确保通信双方的发送和接收能力正常,并协商初始序列号(用于后续可靠传输)。这一机制是 TCP 实现“面向连接”和“可靠传输”的基础。

一、三次握手的本质:确认双方通信能力

TCP 是“面向连接”的协议,通信前必须先建立连接。三次握手的核心目的是:

  1. 让客户端和服务器互相确认对方的发送和接收能力正常(避免一方能发不能收,或能收不能发)。
  2. 协商初始序列号(ISN,Initial Sequence Number),为后续数据传输的可靠性(按序、去重、确认)奠定基础。

二、三次握手的详细过程

三次握手涉及三个关键数据包(均为 TCP 协议的控制报文,不携带业务数据),双方状态变化如下:

1. 第一次握手:客户端 → 服务器(发起连接请求)
  • 客户端行为
    客户端(如浏览器、Java 客户端)创建 Socket 时,向服务器发送 SYN 报文(同步报文段,SYN=1),并生成自己的初始序列号 seq = x(随机生成的 32 位整数)。
    客户端状态从 CLOSED 变为 SYN_SENT(等待服务器确认)。

  • 报文内容
    SYN=1, seq=x

2. 第二次握手:服务器 → 客户端(确认请求并发起同步)
  • 服务器行为
    服务器(如 Java ServerSocket)收到 SYN 报文后,确认“客户端能发,服务器能收”。
    服务器回复 SYN + ACK 报文

    • SYN=1:表示服务器也向客户端发起同步,生成自己的初始序列号 seq = y(随机生成)。
    • ACK=1:表示确认收到客户端的 SYN,确认号 ack = x + 1(期望下次收到客户端从 x+1 开始的数据)。
      服务器状态从 LISTEN 变为 SYN_RCVD(等待客户端确认)。
  • 报文内容
    SYN=1, ACK=1, seq=y, ack=x+1

3. 第三次握手:客户端 → 服务器(确认服务器同步)
  • 客户端行为
    客户端收到 SYN + ACK 报文后,确认“服务器能收、能发”。
    客户端回复 ACK 报文,确认号 ack = y + 1(期望下次收到服务器从 y+1 开始的数据),序列号 seq = x + 1(遵循 TCP 序列号递增规则)。
    客户端状态从 SYN_SENT 变为 ESTABLISHED(连接建立,可传输数据)。

  • 服务器行为
    服务器收到 ACK 报文后,确认“客户端能收”,状态从 SYN_RCVD 变为 ESTABLISHED

  • 报文内容
    ACK=1, seq=x+1, ack=y+1

三、为什么需要三次握手?(核心原因)

三次握手是为了避免“无效连接请求”导致服务器资源浪费,同时确保双方通信能力完整。

  • 若仅两次握手
    假设客户端发送的第一个 SYN 报文因网络延迟滞留,客户端超时后重新发送 SYN 并完成两次握手建立连接。通信结束后,滞留的 SYN 报文到达服务器,服务器会误认为是新的连接请求,再次建立连接并分配资源(如端口、缓冲区),但客户端实际不会使用该连接,导致服务器资源浪费。

  • 三次握手的必要性
    第三次握手让服务器确认“客户端已收到自己的 SYN + ACK”,确保客户端是“活跃”的,从而避免为无效请求分配资源。同时,三次交互完整确认了双方的“发送-接收”能力:

    • 第一次:服务器确认“客户端能发,服务器能收”。
    • 第二次:客户端确认“服务器能收、能发”。
    • 第三次:服务器确认“客户端能收”。

四、三次握手的关键细节

  1. 初始序列号(ISN)的随机性
    ISN 不是固定的 0 或 1,而是随机生成的(通常随时间递增),目的是防止序列号猜测攻击(攻击者伪造数据包时难以猜对正确序列号)。

  2. SYN 报文的消耗
    SYN 报文会消耗一个序列号(如客户端发送 seq=x 后,下一个序列号是 x+1),而 ACK 报文不消耗序列号(仅确认对方的序列号)。

  3. 半连接队列
    服务器在第二次握手后(状态 SYN_RCVD),会将连接放入“半连接队列”,直到收到第三次握手的 ACK 后,才移至“全连接队列”(ESTABLISHED 状态)。若半连接队列满,服务器可能丢弃新的 SYN 请求(导致客户端连接失败)。

五、Java 中三次握手的体现(代码与现象)

Java 中通过 SocketServerSocket 实现 TCP 连接,三次握手由底层操作系统自动完成,开发者无需手动处理,但可通过代码观察连接建立过程:

示例:Java 客户端与服务器建立 TCP 连接
// 服务器端
import java.net.ServerSocket;
import java.net.Socket;

public class HandshakeServer {
    public static void main(String[] args) throws Exception {
        // 1. 启动服务器,监听端口 8080(状态:LISTEN)
        try (ServerSocket serverSocket = new ServerSocket(8080)) {
            System.out.println("服务器启动,等待连接...");
            
            // 2. 阻塞等待客户端连接(三次握手在此过程中完成)
            Socket clientSocket = serverSocket.accept(); 
            System.out.println("客户端已连接:" + clientSocket.getInetAddress());
            
            // 3. 连接建立后(状态:ESTABLISHED),可进行数据传输
            // ...
        }
    }
}

// 客户端
import java.net.Socket;

public class HandshakeClient {
    public static void main(String[] args) throws Exception {
        // 1. 发起连接(触发三次握手)
        System.out.println("发起连接请求...");
        Socket socket = new Socket("localhost", 8080); // 三次握手完成后返回
        
        // 2. 连接建立(状态:ESTABLISHED)
        System.out.println("连接已建立,服务器地址:" + socket.getInetAddress());
        
        // ...
    }
}
执行现象:
  1. 服务器启动后调用 accept(),进入阻塞状态(等待客户端连接,对应 LISTEN 状态)。
  2. 客户端创建 Socket 时,底层自动发送第一次握手的 SYN 报文。
  3. 服务器收到 SYN 后,发送第二次握手的 SYN + ACK 报文。
  4. 客户端收到后,发送第三次握手的 ACK 报文。
  5. 三次握手完成,accept() 方法返回,双方进入 ESTABLISHED 状态,可开始通信。

六、总结

TCP 三次握手是建立可靠连接的必要过程,通过三次报文交互:

  1. 确认了客户端和服务器的发送/接收能力均正常。
  2. 协商了初始序列号,为后续数据的有序传输、确认和重传奠定基础。
  3. 避免了无效连接请求导致的服务器资源浪费。

在 Java 开发中,三次握手由 SocketServerSocket 底层自动实现,开发者只需关注连接建立后的业务逻辑,但理解其原理有助于排查连接超时、连接失败等网络问题(如半连接队列满、SYN 报文被防火墙拦截等)。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值