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

TCP 三次握手与四次挥手

一、TCP 协议的核心特性:为什么需要三次握手与四次挥手?

在深入细节前,我们先明确 TCP 的定位:TCP 是面向连接、可靠传输、基于字节流的传输层协议,与 “无连接、不可靠” 的 UDP 形成鲜明对比。这些特性决定了 TCP 必须通过特定机制实现 “连接管理”:

  • 面向连接:通信前必须先建立连接,通信后需断开连接(类似打电话 “拨号 - 通话 - 挂电话”);
  • 可靠传输:通过 “确认机制、重传机制、滑动窗口” 等确保数据不丢失、不重复、按序到达;
  • 字节流:数据以连续字节的形式传输,而非 UDP 的 “数据包” 形式。

而 “三次握手” 是 TCP 建立可靠连接的核心机制,“四次挥手” 是断开连接的标准流程 —— 二者共同保证了 TCP 通信的 “可靠性” 和 “资源高效利用”。

二、TCP 三次握手:如何建立可靠连接?

“三次握手”(Three-Way Handshake)是客户端与服务器之间通过交换 3 个 TCP 报文段,确认双方 “发送能力” 和 “接收能力” 均正常,最终建立可靠连接的过程。

1. 核心前提:TCP 报文段的关键字段

理解三次握手前,需先掌握 TCP 报文段中 3 个核心控制字段(位于 TCP 头部),它们是握手过程的 “信号旗”:

  • SYN(Synchronize):同步序列编号,用于发起连接请求,标识 “我要建立连接,并发送初始序列号”;
  • ACK(Acknowledgment):确认编号,用于响应对方请求,标识 “我已收到你的数据,下一次请发送这个编号之后的数据”;
  • FIN(Finish):结束标志,用于请求断开连接(四次挥手时使用);
  • Seq(Sequence Number):序列号,TCP 将数据按字节编号,Seq 标识当前报文段的第一个字节的编号(确保数据按序传输);
  • Ack(Acknowledgment Number):确认号,标识 “期望收到的下一个字节的编号”,即 “已收到的最大序列号 + 1”。

2. 三次握手完整流程

假设场景:客户端(如浏览器)要与服务器(如 Web 服务器)建立 TCP 连接,进而传输 HTTP 请求。

第一步:客户端发送 “连接请求”(SYN 报文)
  • 发起方:客户端
  • 报文类型:SYN 报文(仅 SYN 标志位为 1)
  • 关键字段
    • Seq = x(x 是客户端生成的随机初始序列号,如 100);
    • SYN = 1(表示 “我请求建立连接,并同步我的初始序列号 x”);
  • 目的:客户端向服务器表明 “我有发送数据的能力,想和你建立连接,请确认你的接收能力”。

此时客户端状态从CLOSED(关闭)变为SYN-SENT(已发送 SYN 请求)。

第二步:服务器响应 “连接确认”(SYN+ACK 报文)
  • 发起方:服务器
  • 报文类型:SYN+ACK 报文(SYN 和 ACK 标志位均为 1)
  • 关键字段
    • Seq = y(y 是服务器生成的随机初始序列号,如 200);
    • SYN = 1(服务器同步自己的初始序列号 y,同时请求建立连接);
    • Ack = x + 1(确认已收到客户端的 Seq=x,下一次期望收到 x+1 及之后的数据);
    • ACK = 1(表示 “我已收到你的连接请求,我的接收能力正常,同时我也向你发起连接请求”);
  • 目的:服务器向客户端反馈 “我已收到你的请求(接收能力正常),我也有发送能力,想和你建立连接,请确认你的接收能力”。

此时服务器状态从LISTEN(监听)变为SYN-RCVD(已收到 SYN 请求,等待客户端确认)。

第三步:客户端发送 “最终确认”(ACK 报文)
  • 发起方:客户端
  • 报文类型:ACK 报文(仅 ACK 标志位为 1)
  • 关键字段
    • Seq = x + 1(客户端的下一个序列号,因第一步已发送 1 个字节的 SYN 报文,Seq 递增 1);
    • Ack = y + 1(确认已收到服务器的 Seq=y,下一次期望收到 y+1 及之后的数据);
    • ACK = 1(表示 “我已收到你的连接请求,我的接收能力正常,连接可以正式建立”);
  • 目的:客户端向服务器确认 “我已收到你的响应(接收能力正常),连接可以正式建立,我们可以开始传输数据了”。

此时客户端状态从SYN-SENT变为ESTABLISHED(已建立连接);服务器收到 ACK 报文后,状态也从SYN-RCVD变为ESTABLISHED

至此,TCP 三次握手完成,双方进入 “已连接” 状态,可开始传输应用层数据(如 HTTP 请求、文件数据等)。

3. 为什么是 “三次” 而不是两次或四次?

这是 TCP 三次握手最经典的问题,核心答案是 “确保双方发送能力和接收能力均正常,同时避免‘无效连接请求’浪费资源”:

  • 两次握手不可行:若仅两次握手,服务器发送 SYN+ACK 后就认为连接已建立,但客户端可能因网络延迟未收到该报文,不会发送 ACK。此时服务器会一直等待客户端的数据,浪费端口和内存资源(“半连接队列” 溢出风险);
  • 四次握手没必要:三次握手已能完整确认双方的 “发送 + 接收” 能力(客户端确认服务器:能发能收;服务器确认客户端:能发能收),第四次握手属于冗余操作,会增加网络延迟。

三、TCP 四次挥手:如何断开连接?

当数据传输完成后,TCP 需要通过 “四次挥手”(Four-Way Wavehand)断开连接,确保双方都已完成数据接收,避免数据丢失。与三次握手不同,四次挥手是 “双向断开”—— 客户端和服务器需分别向对方发起断开请求并确认。

1. 四次挥手完整流程

假设场景:客户端与服务器已完成数据传输(如浏览器已接收完网页数据),需断开 TCP 连接。

第一步:客户端发送 “断开请求”(FIN 报文)
  • 发起方:客户端
  • 报文类型:FIN 报文(仅 FIN 标志位为 1)
  • 关键字段
    • Seq = u(u 是客户端已发送的最后一个字节的序列号 + 1,如客户端已发送到 1000 字节,Seq=1001);
    • FIN = 1(表示 “我已完成数据发送,想断开我的发送连接”);
  • 目的:客户端向服务器表明 “我这边没有数据要发送了,你可以准备接收我的最后数据,但你仍可以向我发送数据”。

此时客户端状态从ESTABLISHED变为FIN-WAIT-1(等待服务器确认断开)。

第二步:服务器响应 “断开确认”(ACK 报文)
  • 发起方:服务器
  • 报文类型:ACK 报文(仅 ACK 标志位为 1)
  • 关键字段
    • Seq = v(v 是服务器已发送的最后一个字节的序列号 + 1,如服务器已发送到 2000 字节,Seq=2001);
    • Ack = u + 1(确认已收到客户端的 FIN 报文,下一次期望收到 u+1 及之后的数据,实际客户端已无数据发送);
    • ACK = 1(表示 “我已收到你的断开请求,我会继续向你发送剩余数据,请你等待”);
  • 目的:服务器向客户端反馈 “我知道你要断开了,但我可能还有数据没发完,请你先别关闭接收通道,等我发完数据再断开”。

此时服务器状态从ESTABLISHED变为CLOSE-WAIT(等待自身数据发送完成后,再发起断开请求);客户端收到 ACK 后,状态从FIN-WAIT-1变为FIN-WAIT-2(等待服务器的 FIN 报文)。

第三步:服务器发送 “断开请求”(FIN 报文)
  • 发起方:服务器
  • 报文类型:FIN 报文(仅 FIN 标志位为 1)
  • 关键字段
    • Seq = w(w 是服务器已发送的最后一个字节的序列号 + 1,若服务器在第二步后发送了 500 字节数据,Seq=2001+500=2501);
    • FIN = 1(表示 “我已完成所有数据发送,想断开我的发送连接”);
    • Ack = u + 1(再次确认已收到客户端的 FIN 报文,与第二步的 Ack 一致);
  • 目的:服务器向客户端表明 “我这边也没有数据要发送了,我们可以彻底断开连接了”。

此时服务器状态从CLOSE-WAIT变为LAST-ACK(等待客户端的最终确认)。

第四步:客户端发送 “最终确认”(ACK 报文)
  • 发起方:客户端
  • 报文类型:ACK 报文(仅 ACK 标志位为 1)
  • 关键字段
    • Seq = u + 1(客户端的下一个序列号,因第一步已发送 1 个字节的 FIN 报文,Seq 递增 1);
    • Ack = w + 1(确认已收到服务器的 FIN 报文,下一次期望收到 w+1 及之后的数据,实际服务器已无数据发送);
    • ACK = 1(表示 “我已收到你的断开请求,连接可以彻底断开了”);
  • 目的:客户端向服务器确认 “我知道你也断开了,连接可以正式关闭”。

此时客户端状态从FIN-WAIT-2变为TIME-WAIT(等待 2MSL 时间,确保服务器收到 ACK);服务器收到 ACK 后,状态从LAST-ACK变为CLOSED(已关闭连接)。

客户端等待 2MSL(MSL:Maximum Segment Lifetime,报文最大生存时间,默认 2 分钟)后,若未收到服务器的重传 FIN 报文,确认服务器已收到 ACK,状态从TIME-WAIT变为CLOSED。至此,TCP 四次挥手完成,连接彻底断开。

2. 为什么是 “四次” 而不是三次?

核心原因是TCP 的 “双向通信” 特性——TCP 连接是 “全双工”(双方可同时发送数据),断开连接时需分别关闭 “客户端→服务器” 和 “服务器→客户端” 两个方向的通道:

  • 第一次挥手:客户端关闭 “发送通道”,告诉服务器 “我不发了”;
  • 第二次挥手:服务器确认 “收到关闭请求”,但 “发送通道” 仍开(可能还有数据要发);
  • 第三次挥手:服务器发完数据后,关闭 “发送通道”,告诉客户端 “我也不发了”;
  • 第四次挥手:客户端确认 “收到关闭请求”,关闭 “接收通道”。

若改为三次挥手,服务器需在第二次挥手时同时发送 FIN+ACK(类似三次握手的 SYN+ACK),但此时服务器可能仍有数据未发送,强行关闭会导致数据丢失 —— 因此四次挥手是 “优雅断开” 的必要设计。

3. 四次挥手的关键问题与实战分析

(1)“TIME-WAIT” 状态的作用

客户端在第四次挥手后会进入TIME-WAIT状态,等待 2MSL 时间,这是 TCP 设计的关键,主要解决两个问题:

  • 确保服务器收到最终 ACK:若第四次挥手的 ACK 报文丢失,服务器会重传 FIN 报文,客户端在 TIME-WAIT 期间可收到并重发 ACK,避免服务器因未收到 ACK 而一直处于LAST-ACK状态;
  • 避免 “旧连接” 干扰新连接:2MSL 是报文在网络中最大生存时间,等待 2MSL 可确保网络中所有该连接的旧报文都已过期,避免新连接使用相同端口时,收到旧连接的残留报文。

注意:若服务器频繁出现大量TIME-WAIT连接(如高并发短连接场景),可能导致端口耗尽。可通过调整 Linux 参数优化(如net.ipv4.tcp_tw_reuse允许端口复用,net.ipv4.tcp_tw_recycle快速回收 TIME-WAIT 连接)。

(2)“CLOSE-WAIT” 状态堆积的问题

服务器在第二次挥手后会进入CLOSE-WAIT状态,若该状态连接大量堆积,通常是应用层代码未正确关闭 TCP 连接(如服务器处理完请求后,未调用close()函数释放连接)。

排查方案

  • 使用netstat -an | grep CLOSE-WAIT查看堆积的连接;
  • 检查应用层代码,确保请求处理完成后主动关闭连接(如 Java 中的socket.close()、Python 中的socket.close());
  • 调整 TCP 超时参数(如net.ipv4.tcp_keepalive_time,开启 TCP 保活机制,检测死连接并关闭)。

四、总结

TCP 三次握手与四次挥手是网络通信的 “基石”,其设计充分体现了 TCP 协议的 “可靠性” 和 “严谨性”:

  • 三次握手:通过 3 次报文交换,确认双方发送 / 接收能力正常,建立可靠连接,避免无效连接浪费资源;
  • 四次挥手:通过 4 次报文交换,双向关闭连接,确保双方数据都已传输完成,避免数据丢失;
  • 关键状态SYN-SENTSYN-RCVD(握手),FIN-WAIT-1CLOSE-WAITTIME-WAIT(挥手),这些状态是排查网络问题的重要依据。

文章转载自:

http://S9CfuVDk.pdkht.cn
http://3Ni7wrNy.pdkht.cn
http://ewyjjoK1.pdkht.cn
http://ZKYbiUWl.pdkht.cn
http://V9NxfcYO.pdkht.cn
http://MxgPbojK.pdkht.cn
http://35PFU9K7.pdkht.cn
http://fEiEl4Vq.pdkht.cn
http://rnzDWnGN.pdkht.cn
http://koyE0498.pdkht.cn
http://EvKbyiw2.pdkht.cn
http://7jrAQBww.pdkht.cn
http://QvNehn8B.pdkht.cn
http://J9UCKRbC.pdkht.cn
http://uswqz6JU.pdkht.cn
http://T8n8eij8.pdkht.cn
http://B8ttJ0qN.pdkht.cn
http://DEnGV6oM.pdkht.cn
http://sRT6ikjd.pdkht.cn
http://DzFwX1gE.pdkht.cn
http://f1w1xLHd.pdkht.cn
http://8Ikl4rM0.pdkht.cn
http://nS8OS8Z1.pdkht.cn
http://afWWbL5y.pdkht.cn
http://LkSfL2vA.pdkht.cn
http://9dle2mwb.pdkht.cn
http://IBCcSt8h.pdkht.cn
http://G6jFd9Xi.pdkht.cn
http://ZRIY6W1P.pdkht.cn
http://w1Q5RNHJ.pdkht.cn
http://www.dtcms.com/a/379013.html

相关文章:

  • 【iOS】UIViewController生命周期
  • 硬件开发(7)—IMX6ULL裸机—led进阶、SDK使用(蜂鸣器拓展)、BSP工程目录
  • 人工智能学习:Transformer结构中的编码器层(Encoder Layer)
  • RISCV中PLIC和AIA的KVM中断处理
  • 掌握梯度提升:构建强大的机器学习模型介绍
  • 全球智能电网AI加速卡市场规模到2031年将达20216百万美元
  • springbook3整合Swagger
  • LMS 算法:抗量子时代的「安全签名工具」
  • CUDA中thrust::device_vector使用详解
  • Python学习-day8 元组tuple
  • 2025主流大模型核心信息
  • skywalking定位慢接口调用链路的使用笔记
  • LeetCode刷题记录----739.每日温度(Medium)
  • eNSP华为无线网测试卷:AC+AP,旁挂+直连
  • 开源多模态OpenFlamingo横空出世,基于Flamingo架构实现图像文本自由对话,重塑人机交互未来
  • 光路科技将携工控四大产品亮相工博会,展示工业自动化新成果
  • matlab实现相控超声波成像仿真
  • 【C】Linux 内核“第一宏”:container_of
  • Dinky 是一个开箱即用的一站式实时计算平台
  • Vue3内置组件Teleport/Suspense
  • Python打印格式化完全指南:掌握分隔符与行结尾符的高级应用
  • 实体不相互完全裁剪,请检查您的输入
  • 分数阶傅里叶变换(FRFT)的MATLAB实现
  • ARM (6) - I.MX6ULL 汇编点灯迁移至 C 语言 + SDK 移植与 BSP 工程搭建
  • unsloth微调gemma3图文代码简析
  • 【ECharts ✨】ECharts 自适应图表布局:适配不同屏幕尺寸,提升用户体验!
  • wpf依赖注入驱动的 MVVM实现(含免费源代码demo)
  • Python的f格式
  • 技术视界 | 末端执行器:机器人的“手”,如何赋予机器以生命?
  • 从零开始使用 axum-server 构建 HTTP/HTTPS 服务