
这次我们来看一个 Linux 内核网络协议栈的核心机制TCP 状态机。对于任何需要深入理解网络连接建立、数据传输和终止过程的开发者或运维人员来说掌握 TCP 状态机是排查网络问题、进行性能调优的必备技能。它不是某个可以独立下载运行的软件而是内置于 Linux 内核中的一套复杂逻辑规则决定了每一个 TCP 连接从生到死的完整生命周期。本文将直接切入主题讲解 TCP 状态机的工作原理、各个状态的含义及其转换条件。我们会重点关注状态机在三次握手和四次挥手过程中的具体表现并结合 Linux 内核源码片段如net/ipv4/tcp.c和常用网络工具如netstat,ss的输出来进行验证。无论你是遇到TIME_WAIT过多导致端口耗尽还是想理解SYN_RECV、ESTABLISHED、FIN_WAIT_2等状态背后的故事这篇文章都能提供清晰的路径。本文适合有一定网络基础希望深入 Linux 内核网络子系统或解决实际网络连接问题的读者。1. 核心能力速览在深入细节之前我们先通过一个表格快速了解 TCP 状态机的关键特性这有助于你判断是否需要继续深入阅读以及如何将其应用于实践。能力项说明所属领域Linux 内核网络协议栈 / TCP/IP 协议族核心功能管理 TCP 连接的生命周期定义连接建立三次握手、数据传输、连接终止四次挥手过程中的状态流转规则。关键状态LISTEN,SYN_SENT,SYN_RECV,ESTABLISHED,FIN_WAIT_1,FIN_WAIT_2,TIME_WAIT,CLOSE_WAIT,LAST_ACK,CLOSING等。观察工具用户空间工具netstat -antp,ss -antp,lsof -i。内核空间/proc/net/tcp文件内核跟踪点tracepoint。影响范围决定服务器并发连接数、端口资源管理、连接超时与重传、网络服务稳定性。调优场景调整TIME_WAIT超时时间、tcp_max_syn_backlog、tcp_tw_reuse等内核参数以优化性能。学习门槛需要基础网络知识如 TCP 报文段、序列号、标志位。无需编程即可通过工具观察但深入理解需阅读内核源码。“部署”方式作为 Linux 内核核心功能默认启用。学习重点是理解其原理并学会观察与调优。2. 适用场景与使用边界理解 TCP 状态机并非为了“部署”它而是为了在以下场景中高效地解决问题服务器性能调优当服务器出现TIME_WAIT状态连接过多导致无法创建新连接时你需要理解该状态的成因和持续时间才能合理调整net.ipv4.tcp_tw_reuse、net.ipv4.tcp_fin_timeout等参数。网络问题诊断应用出现“Connection refused”、“Connection timeout”或“Cannot assign requested address”等错误时通过ss或netstat查看连接状态可以快速定位问题是发生在握手阶段SYN_SENT/SYN_RECV、传输阶段还是挥手阶段CLOSE_WAIT/LAST_ACK。高并发服务设计设计 Web 服务器、数据库连接池或 RPC 框架时需要预估各种状态下的连接数量对系统资源如端口、内存的占用避免达到系统限制。安全与监控识别异常连接状态例如大量的SYN_RECV状态可能是 SYN Flood 攻击的迹象监控ESTABLISHED连接数可以评估服务负载。使用边界与注意事项 TCP 状态机是标准协议实现其行为由 RFC 793 等规范定义Linux 内核在此基础上进行了增强和调优。学习时应注意理论结合实践状态图是理论模型实际内核实现可能因版本和配置参数有所不同。区分客户端与服务器视角同一个连接的两端可能处于不同状态如一端FIN_WAIT_2另一端CLOSE_WAIT分析问题时需明确视角。内核参数谨慎调整修改/etc/sysctl.conf中的 TCP 相关参数会影响整个系统在生产环境调整前应在测试环境充分验证。3. 环境准备与前置条件要跟随本文进行实践观察和分析你需要准备一个 Linux 环境。以下是一个通用清单操作系统任何主流的 Linux 发行版均可如 Ubuntu 20.04/22.04 LTS, CentOS 7/8, Rocky Linux, AlmaLinux 等。本文命令在这些系统上通用。工具链网络诊断工具net-tools包提供netstat和iproute2包提供ss通常已预装。若无可通过包管理器安装如apt install net-tools iproute2。进程查看工具lsof常用于查看进程打开的网络连接apt install lsof或yum install lsof。内核信息接口/proc/net/tcp文件默认存在用于查看内核层面的 TCP 连接详细信息。权限要求查看所有连接状态通常需要root权限或在普通用户下使用sudo。使用ss或netstat时-p选项显示进程也需要特权。测试服务可选为了主动创建连接进行观察可以安装一个简单的网络服务如nginxWeb服务器或使用ncnetcat工具创建临时监听端口。4. TCP 状态机全景与核心状态解读TCP 状态机可以用一个经典的状态转换图来表示。为了更直观地理解我们将其核心路径拆解为连接建立、数据传输和连接终止三个阶段。下图概括了主要状态及其在握手和挥手过程中的流转关系stateDiagram-v2 direction LR [*] -- CLOSED CLOSED -- LISTEN: 被动打开\n(服务端) CLOSED -- SYN_SENT: 主动打开\n(客户端) note right of LISTEN 服务端等待连接 end note LISTEN -- SYN_RCVD: 收到SYN SYN_SENT -- SYN_RCVD: 收到SYNACK SYN_RCVD -- ESTABLISHED: 收到ACK SYN_SENT -- ESTABLISHED: 收到SYNACK\n并回复ACK note left of ESTABLISHED 数据传输阶段 end note ESTABLISHED -- FIN_WAIT_1: 主动关闭\n发送FIN FIN_WAIT_1 -- FIN_WAIT_2: 收到ACK FIN_WAIT_2 -- TIME_WAIT: 收到对端FIN\n并回复ACK ESTABLISHED -- CLOSE_WAIT: 收到FIN\n并回复ACK CLOSE_WAIT -- LAST_ACK: 发送FIN LAST_ACK -- [*]: 收到ACK TIME_WAIT -- [*]: 2MSL超时下面我们对图中的关键状态进行详细解读CLOSED起始点与终点。表示当前没有活动的连接或连接已完全释放。LISTEN服务器端状态。表示套接字正在监听指定的端口等待客户端的连接请求SYN 报文。这是服务端调用listen()函数后的状态。SYN_SENT客户端状态。当客户端调用connect()发起主动连接发送了 SYN 报文后便进入此状态等待服务器的 SYN-ACK 应答。SYN_RECV(或 SYN_RECEIVED)服务器端状态。当服务器收到客户端的 SYN 报文并回复了 SYN-ACK 报文后进入此状态等待客户端的最终 ACK 确认。此状态下的连接通常位于内核的半连接队列中。ESTABLISHED连接已建立状态。这是数据传输阶段的状态。对于客户端是在收到 SYN-ACK 并发送 ACK 后进入对于服务器是在收到客户端的 ACK 后进入。此时双方可以自由传输数据。FIN_WAIT_1主动关闭方状态。当应用程序调用close()或shutdown(SHUT_WR)发送 FIN 报文后进入此状态等待对方的 ACK 确认或同时等待对方的 FIN 报文。FIN_WAIT_2主动关闭方状态。在 FIN_WAIT_1 状态下收到了对方对 FIN 的 ACK 确认后便进入 FIN_WAIT_2。此时主动关闭方等待对方发送 FIN 报文即对方也发起关闭。TIME_WAIT主动关闭方状态。这是最常被讨论的状态之一。当主动关闭方在 FIN_WAIT_2 状态下收到对方的 FIN 报文并回复 ACK 后进入 TIME_WAIT。该状态会持续2MSLMaximum Segment Lifetime报文段最大生存时间Linux 默认 60 秒的时间以确保网络中所有属于此连接的报文都已消失防止旧报文干扰新连接。CLOSE_WAIT被动关闭方状态。当一端收到对方的 FIN 报文表示对方已无数据发送并回复了 ACK 后便进入 CLOSE_WAIT。这表示本地应用程序尚未调用close()关闭连接。大量 CLOSE_WAIT 状态通常是应用程序未正确关闭连接如未释放 socket的迹象。LAST_ACK被动关闭方状态。当处于 CLOSE_WAIT 状态的应用程序调用close()发送自己的 FIN 报文后进入 LAST_ACK 状态等待对方对这个 FIN 报文的 ACK 确认。CLOSING一种比较罕见的状态。表示双方几乎同时发送 FIN 报文发起关闭此时双方都进入了 FIN_WAIT_1 状态并在收到对方的 FIN 后进入 CLOSING等待对方的 ACK。收到 ACK 后进入 TIME_WAIT。5. 实战观察使用工具查看 TCP 连接状态理论需要实践验证。我们使用 Linux 系统自带的工具来观察真实的 TCP 状态。5.1 使用ss命令推荐ss(socket statistics) 是iproute2包的一部分比传统的netstat更快、更高效显示的信息也更详细。查看所有 TCP 连接及其状态sudo ss -ant-a: 显示所有 sockets (包括监听的和已建立的)。-n: 以数字形式显示地址和端口不进行 DNS 解析和服务名查找。-t: 仅显示 TCP sockets。输出示例State Recv-Q Send-Q Local Address:Port Peer Address:Port LISTEN 0 128 *:22 *:* LISTEN 0 100 127.0.0.1:25 *:* ESTAB 0 0 192.168.1.10:22 192.168.1.5:54322 TIME-WAIT 0 0 192.168.1.10:80 203.0.113.1:41562 CLOSE-WAIT 1 0 192.168.1.10:443 198.51.100.2:36754第一列State就是 TCP 状态。注意ss使用的状态缩写可能与标准名称略有不同如ESTAB代表ESTABLISHED,TIME-WAIT代表TIME_WAIT。查看带有进程信息的 TCP 连接sudo ss -antp-p: 显示使用此 socket 的进程信息。这对于定位哪个应用程序持有大量特定状态的连接非常有用。5.2 使用netstat命令传统netstat功能类似但可能在某些新系统上默认未安装。查看所有 TCP 连接状态sudo netstat -ant输出格式与ss类似。统计各状态连接数快速诊断这是一个非常实用的命令可以一眼看出系统当前的连接状态分布。sudo netstat -ant | awk /^tcp/ {print $6} | sort | uniq -c | sort -rn或者使用ss:sudo ss -ant | awk NR1 {print $1} | sort | uniq -c | sort -rn输出示例1234 ESTABLISHED 89 TIME_WAIT 23 LISTEN 5 CLOSE_WAIT 1 SYN_RECV如果发现CLOSE_WAIT或SYN_RECV数量异常多就需要警惕并进一步排查。5.3 查看内核原始数据/proc/net/tcp/proc/net/tcp文件提供了内核中 TCP socket 表的原始视图信息最为详细但可读性较差。sudo cat /proc/net/tcp | head -20输出包含本地/远程地址端口十六进制、状态码、队列信息等。其中状态码需要对照内核定义如0A表示LISTEN,01表示ESTABLISHED。6. 深入原理三次握手与四次挥手的状态流转现在我们结合状态机图和工具观察详解两个核心过程。6.1 三次握手Connection Establishment假设客户端C主动连接服务器S。C - S: SYN客户端调用connect()发送一个 SYN 报文seqx。客户端状态由CLOSED变为SYN_SENT。S - C: SYN-ACK服务器在LISTEN状态下收到 SYN。服务器回复 SYN-ACK 报文seqy, ackx1。服务器状态由LISTEN变为SYN_RECV。此时连接进入服务器的“半连接队列”。C - S: ACK客户端收到 SYN-ACK。客户端发送 ACK 报文acky1。客户端状态由SYN_SENT变为ESTABLISHED。服务器收到 ACK。服务器状态由SYN_RECV变为ESTABLISHED。连接从“半连接队列”移入“全连接队列”等待accept()。观察点在握手完成前你可以在服务器上看到SYN_RECV状态的连接。如果大量连接卡在此状态可能遭遇 SYN Flood 攻击或net.ipv4.tcp_max_syn_backlog半连接队列大小设置过小。6.2 四次挥手Connection Termination假设客户端C先发起关闭。C - S: FIN客户端应用程序调用close()发送 FIN 报文sequ表示不再发送数据。客户端状态由ESTABLISHED变为FIN_WAIT_1。S - C: ACK服务器收到 FIN回复 ACK 报文acku1。服务器状态由ESTABLISHED变为CLOSE_WAIT。此时服务器可能还有数据要发送给客户端。客户端收到 ACK状态由FIN_WAIT_1变为FIN_WAIT_2。S - C: FIN服务器应用程序也调用close()发送 FIN 报文seqv, acku1。服务器状态由CLOSE_WAIT变为LAST_ACK。C - S: ACK客户端收到 FIN回复 ACK 报文ackv1。客户端状态由FIN_WAIT_2变为TIME_WAIT。服务器收到 ACK状态由LAST_ACK变为CLOSED。客户端在TIME_WAIT状态等待2MSL默认 60秒后状态变为CLOSED。关键状态解读CLOSE_WAIT被动关闭方服务器收到 FIN 后进入。如果此状态连接长期存在说明服务器端应用程序没有及时调用close()关闭 socket可能导致文件描述符泄漏。TIME_WAIT主动关闭方客户端最后停留的状态。它的存在有两个主要目的可靠地终止连接确保最后一个 ACK 能到达对方如果丢失对方会重传 FINTIME_WAIT下的客户端可以重发 ACK。让旧连接的报文在网络中消逝避免被之后复用的相同四元组源IP、源端口、目的IP、目的端口的新连接错误接收。 高并发短连接服务下TIME_WAIT状态连接过多会占用大量端口资源。可通过调整net.ipv4.tcp_tw_reuse和net.ipv4.tcp_tw_recycle注意tcp_tw_recycle在较新内核中已废弃不推荐使用等参数来优化。7. 内核参数调优与常见问题排查理解了状态机我们就可以有针对性地调整内核参数和排查问题。7.1 关键内核参数编辑/etc/sysctl.conf文件修改后执行sysctl -p生效。net.ipv4.tcp_tw_reuse(默认通常为0)含义允许将处于TIME_WAIT状态的 socket 重新用于新的 TCP 连接。这可以显著减少TIME_WAIT状态对端口资源的占用。建议对于客户端或需要主动发起大量短连接的服务器可以设置为1。注意从协议安全角度启用后需同时开启net.ipv4.tcp_timestamps1默认开启。net.ipv4.tcp_fin_timeout(默认 60)含义控制FIN_WAIT_2状态的超时时间秒。如果对端一直不发送 FIN连接在此状态停留的时间。建议对于需要快速释放资源的场景可以适当减小如设为30。net.ipv4.tcp_max_syn_backlog(默认值因系统而异)含义SYN 半连接队列的最大长度。当服务器收到 SYN 但未完成三次握手时连接存放于此队列。建议如果服务器面临高并发连接且观察到SYN_RECV状态连接被丢弃可通过netstat -s | grep -i listen查看SYNs to LISTEN的丢弃统计可以适当增大此值如2048或4096。net.core.somaxconn(默认通常为128)含义全连接队列已完成三次握手等待accept()的连接的最大长度。建议对于高并发服务如 Nginx需要将其调大如1024或更大并确保应用程序如nginx.conf中的backlog参数的设置不小于此值。7.2 常见问题排查表问题现象可能原因排查命令解决方案与建议服务器出现大量TIME_WAIT连接主动关闭连接的客户端或服务器过多高并发短连接场景常见。ss -ant | grep TIME-WAIT | wc -l1. 优化应用使用连接池。2. 调整内核参数net.ipv4.tcp_tw_reuse 1。3. 考虑让服务端主动关闭连接改变角色。服务器出现大量CLOSE_WAIT连接服务器端应用程序未正确关闭 socket未调用close()。ss -antp | grep CLOSE-WAIT查看对应进程。1. 修复应用程序代码确保 socket 被正确关闭。2. 检查是否有未处理的异常导致资源未释放。服务器出现大量SYN_RECV连接1. 正常高并发连接。2. 遭受 SYN Flood 攻击。3. 客户端未回复 ACK网络问题或恶意。netstat -n -p TCP | grep SYN_RECVnetstat -s | grep -i “SYNs to LISTEN”1. 检查net.ipv4.tcp_max_syn_backlog和net.core.somaxconn是否过小。2. 启用 SYN Cookie (net.ipv4.tcp_syncookies 1)。3. 结合防火墙或 DDoS 防护设备进行排查。连接建立失败Connection timeout或Cannot assign requested address1. 本地端口耗尽尤其是大量TIME_WAIT。2. 本地 IP 地址配置问题。ss -s查看 Total sockets 统计。cat /proc/sys/net/ipv4/ip_local_port_range1. 优化TIME_WAIT回收 (tcp_tw_reuse)。2. 适当扩大本地端口范围 (ip_local_port_range如1024 65535)。3. 减少短连接使用。服务响应变慢ESTABLISHED连接数异常高1. 正常业务流量增长。2. 客户端或下游服务异常未释放连接。3. 连接泄漏。ss -antp | grep ESTAB | wc -l查看应用日志和监控。1. 评估是否为正常负载。2. 检查应用连接池配置和泄漏情况。3. 设置合理的 keepalive 超时。8. 从内核源码看状态转换进阶对于希望深入理解的开发者可以查阅 Linux 内核源码。状态定义在/include/net/tcp_states.h中#define TCP_ESTABLISHED 1 /* 已建立连接 */ #define TCP_SYN_SENT 2 /* 已发送SYN */ #define TCP_SYN_RECV 3 /* 已收到SYN并发送SYN-ACK */ #define TCP_FIN_WAIT1 4 /* 已发送FIN等待ACK */ #define TCP_FIN_WAIT2 5 /* 已收到对FIN的ACK等待对方FIN */ #define TCP_TIME_WAIT 6 /* 收到对方FIN并回复ACK等待2MSL */ #define TCP_CLOSE 7 /* 未使用 */ #define TCP_CLOSE_WAIT 8 /* 收到FIN等待本地应用关闭 */ #define TCP_LAST_ACK 9 /* 已发送FIN等待ACK */ #define TCP_LISTEN 10 /* 正在监听 */ #define TCP_CLOSING 11 /* 双方同时尝试关闭 */状态转换的逻辑则分散在net/ipv4/tcp_input.c处理接收报文和net/ipv4/tcp_output.c处理发送报文等文件中。例如在tcp_rcv_state_process函数中根据当前状态和收到的 TCP 报文标志位决定下一个状态。9. 最佳实践与总结监控先行将ss -s的输出或各状态连接数纳入系统监控如 Prometheus Grafana建立基线便于及时发现异常。参数调优需测试任何内核参数的修改都应在测试环境充分验证评估其对稳定性、性能的影响。理解应用行为明确你的应用程序在连接生命周期中是主动关闭方还是被动关闭方这决定了你主要关注TIME_WAIT还是CLOSE_WAIT。善用工具链结合ss/netstat、lsof、tcpdump抓包分析和strace跟踪系统调用进行综合诊断。代码规范在应用程序中确保 socket 在使用完毕后被正确关闭避免CLOSE_WAIT泄漏。使用连接池管理高频短连接。TCP 状态机是 Linux 网络通信的基石之一。掌握它不仅能让你在出现网络问题时快速定位瓶颈是握手问题、挥手问题还是传输问题更能让你在设计高并发网络服务时做出更合理的架构和参数决策。从看懂ss命令的输出开始结合状态转换图你就能清晰地洞察每一个网络连接背后的故事。建议将本文中的命令和排查表收藏在下次遇到棘手的网络连接问题时它们将成为你最得力的工具。