当前位置: 首页 > article >正文

TCP 三次握手与四次挥手

TCP 三次握手与四次挥手知识总结

一、TCP 连接与断开的核心机制

1. 三次握手(建立连接)

目的:
建立客户端与服务端之间的双向传输通道,确保双方都能确认对方的接收和发送能力,为后续的数据传输奠定可靠基础。

流程:

  1. 客户端发送 SYN
    客户端发送 SYN 报文,请求建立连接,并包含初始序列号(SEQ),此时客户端进入 SYN_SENT 状态。

  2. 服务端回应 SYN-ACK
    服务端收到 SYN 后,回应 SYN-ACK,其中 ACK 为客户端 SYN 的确认,ACK 号为客户端 SEQ+1。服务端也发送自己的 SYN,包含自己的初始序列号,此时服务端进入 SYN_RCVD 状态。

  3. 客户端发送 ACK
    客户端收到 SYN-ACK 后,发送 ACK 报文,确认号为服务端的 SEQ+1,双方进入 ESTABLISHED 状态,连接建立成功。

关键状态:

  • LISTEN(监听状态): 服务端调用 listen() 后进入该状态,等待客户端连接。
  • ESTABLISHED(已建立连接): 连接建立后,客户端和服务端可以进行数据传输。

2. 四次挥手(断开连接)

目的:
安全拆除双向传输通道,确保双方都能完成数据的发送和接收,避免数据丢失。

流程:
(假设主动关闭方为 A,被动方为 B)

  1. A 发送 FIN
    A 发送 FIN,表示没有数据要发送,进入 FIN_WAIT_1 状态。

  2. B 回复 ACK
    B 回复 ACK,确认收到 A 的关闭请求,进入 CLOSE_WAIT 状态。

  3. B 发送 FIN
    B 完成数据发送后,发送 FIN,请求关闭 B 到 A 的通道,进入 LAST_ACK 状态。

  4. A 回复 ACK
    A 回复 ACK,确认 B 的关闭请求,进入 TIME_WAIT 状态,等待一段时间后才能真正关闭连接。

关键状态:

  • TIME_WAIT: 主动关闭方等待残留报文消失,持续 2MSL 时间(通常 1-2 分钟)。
  • CLOSE_WAIT: 被动关闭方等待应用层处理剩余数据。如果 CLOSE_WAIT 状态持续过长,可能是程序未正确关闭连接。

二、Socket 状态与命令工具

1. Socket 状态

  • LISTEN: 服务端调用 listen() 后进入监听状态,等待客户端的连接请求。
  • ESTABLISHED: 连接已建立,可以进行数据传输。
  • TIME_WAIT: 主动关闭方等待残留报文消失的状态。
  • CLOSE_WAIT: 被动关闭方等待应用层处理剩余数据。

2. 查看 Socket 状态

命令:
netstat -na | grep <端口号>
该命令列出特定端口的连接状态。例如,查看端口 8080 的连接状态:
netstat -na | grep 8080

关键列说明:

  • 本地地址(Local Address): 本地主机的 IP 和端口号。
  • 外网地址(Foreign Address): 远程主机的 IP 和端口号。
  • 状态(State): 连接的当前状态,如 LISTEN、ESTABLISHED 等。

三、编程中的关键细节

1. 端口分配

  • 服务端端口: 需要固定,使用 bind() 指定端口(如 HTTP 协议使用 80 端口)。
  • 客户端端口: 由系统随机分配,通常从可用端口范围中选择未被占用的端口。

2. 高并发处理

backlog(已连接队列大小):
指定 listen() 的第二个参数,影响全连接队列的大小,默认值通常为 128,但可以根据实际需要增大。

示例:

listen(sockfd, 1024);  // 设置队列长度为 1024

3. TIME_WAIT 问题与解决

问题:
主动关闭方在 TIME_WAIT 状态期间无法重用端口,可能导致高并发场景中的端口资源浪费。

解决方案:
设置 SO_REUSEADDR 属性来允许端口复用:

int opt = 1;
setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));

四、常见问题与面试要点

1. 为什么挥手需要四次?

  • 全双工协议: TCP 需要独立关闭每一方向的传输通道。
  • 数据发送完成: 被动关闭方可能需要继续发送数据,所以不能立即关闭连接,必须等到完成数据发送后再发送 FIN

2. SYN 泛洪攻击

攻击方式:
攻击者伪造大量源 IP 地址,发送大量 SYN 报文,消耗服务端资源。

防御方法:
启用 SYN Cookie 机制来防御该攻击。

3. TIME_WAIT 的意义

  • 确保最后的 ACK 被送达: 防止被动关闭方没有收到最后的 ACK 而重发 FIN
  • 等待残留报文消失: 确保旧连接的报文不会干扰新连接。

4. 常见面试问题

  • SYN、ACK、FIN、SEQ 序列号的含义
  • 半连接队列 vs 全连接队列
  • CLOSE_WAIT 过多的原因

五、实际应用示例

1. 服务端代码示例

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>

#define PORT 8080
#define BACKLOG 1024

int main() {
    int sockfd, new_sockfd;
    struct sockaddr_in addr, client_addr;
    socklen_t client_addr_len = sizeof(client_addr);
    int opt = 1;

    // 创建 socket
    sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if (sockfd == -1) {
        perror("socket creation failed");
        exit(EXIT_FAILURE);
    }

    // 设置端口复用
    if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) == -1) {
        perror("setsockopt failed");
        close(sockfd);
        exit(EXIT_FAILURE);
    }

    // 绑定端口
    memset(&addr, 0, sizeof(addr));
    addr.sin_family = AF_INET;
    addr.sin_addr.s_addr = INADDR_ANY;
    addr.sin_port = htons(PORT);
    if (bind(sockfd, (struct sockaddr*)&addr, sizeof(addr)) == -1) {
        perror("bind failed");
        close(sockfd);
        exit(EXIT_FAILURE);
    }

    // 监听连接
    if (listen(sockfd, BACKLOG) == -1) {
        perror("listen failed");
        close(sockfd);
        exit(EXIT_FAILURE);
    }

    printf("Server listening on port %d...\n", PORT);

    // 接受客户端连接
    new_sockfd = accept(sockfd, (struct sockaddr*)&client_addr, &client_addr_len);
    if (new_sockfd == -1) {
        perror("accept failed");
        close(sockfd);
        exit(EXIT_FAILURE);
    }

    printf("Client connected.\n");

    // 处理客户端请求
    close(new_sockfd);
    close(sockfd);

    return 0;
}

2. 客户端连接测试

使用 ab 工具测试并发连接:

ab -n 1000 -c 100 http://localhost:8080/

总结

理解三次握手和四次挥手是掌握 TCP 协议的基础,通过结合编程实践与网络状态分析工具(如 netstat),可以有效排查连接问题并优化高并发场景下的服务端性能。对常见网络攻击(如 SYN 泛洪攻击)和连接状态(如 TIME_WAITCLOSE_WAIT)的理解和处理是网络编程和系统运维中不可或缺的技能。


http://www.kler.cn/a/566406.html

相关文章:

  • Python测试框架Pytest的参数化
  • ChatGPT Deep Research:重塑智能研究的未来边界
  • C++ 实现 组合
  • Sparsely-Gated Mixture-of-Experts Layer (MoE)论文解读与Pytorch代码实现
  • nginx配置文件分解为多个子配置
  • Maven 插件的使用(二)
  • 安全模块设计:token服务、校验注解(开启token校验、开启签名校验、允许处理API日志)、获取当前用户信息的辅助类
  • 解析excel文件报错java.lang.NoSuchMethodException
  • FPGA 使用门控时钟
  • 8个Linux进程管理命令详解及示例(四):kill、pkill 和 killall 命令
  • 养生保健:为健康生活筑牢基石
  • 人类驾驶的人脑两种判断模式(反射和预判)-->自动驾驶两种AI模式
  • 深度学习笔记17-马铃薯病害识别(VGG-16复现)
  • 验证码识别:一文掌握手机验证码的自动化处理
  • 爬虫下载B站视频简单程序(仅供学习)
  • 【考研】复试相关上机题目
  • 【西瓜书《机器学习》四五六章内容通俗理解】
  • IPoIB源码深度解析:如何基于TCP/IP协议栈实现高性能InfiniBand通信
  • Ubuntu 下 nginx-1.24.0 源码分析 - ngx_list_t
  • python-leetcode-颜色分类