TCP三次握手&四次挥手

TCP 三次握手

  • 客户端发送 SYN 包请求连接,服务端返回 SYN+ACK 确认并请求连接,客户端再返回 ACK 确认,双方进入 ESTABLISHED 状态。
graph LR
    A[客户端] -->|1、SYN=1, Seq=x| B[服务端]
    B -->|2、SYN=1, ACK=1, Seq=y, Ack=x+1| A
    A -->|3、ACK=1, Seq=x+1, Ack=y+1| B
    A -->|4、ESTABLISHED| C[连接建立完成]
    B -->|4、ESTABLISHED| C

作用:建立可靠的双向连接,确保通信双方确认彼此的接收和发送能力。 过程

  1. 第一次握手(客户端 → 服务端): 客户端发送带有 SYN=1,Seq=x 的报文,进入 SYN_SENT 状态,请求建立连接。
  2. 第二次握手(服务端 → 客户端): 服务端收到后,发送 SYN=1,ACK=1,Seq=y,Ack=x+1 的报文,进入 SYN_RCVD 状态,确认客户端请求并发起自身连接请求。
  3. 第三次握手(客户端 → 服务端): 客户端发送 ACK=1,Seq=x+1,Ack=y+1 的报文,进入 ESTABLISHED 状态。服务端收到后也进入 ESTABLISHED 状态,连接建立完成。

TCP 四次挥手

  • 主动关闭方先发送 FIN 包请求关闭,被动关闭方返回 ACK 确认(此时仍可发送剩余数据)。
  • 被动关闭方数据发送完毕后,再发送 FIN 包请求关闭,主动关闭方返回 ACK 确认,并进入 TIME_WAIT 状态(等待 2MSL 确保最后一个 ACK 到达),被动关闭方收到后立即关闭。
graph LR
    D[主动关闭方] -->|1、FIN=1, Seq=m| E[被动关闭方]
    E -->|2、ACK=1, Seq=n, Ack=m+1| D
    E -->|3、FIN=1, ACK=1, Seq=p, Ack=m+1| D
    D -->|4、ACK=1, Seq=m+1, Ack=p+1| E
    D -->|5、TIME_WAIT| F[等待超时后关闭]
    E -->|5、CLOSED| G[立即关闭]

作用:终止连接,确保双向数据传输都已完成,资源可安全释放。 过程

  1. 第一次挥手(主动关闭方 → 被动关闭方): 主动关闭方发送 FIN=1,Seq=m 的报文,进入 FIN_WAIT_1 状态,请求关闭连接。
  2. 第二次挥手(被动关闭方 → 主动关闭方): 被动关闭方收到后,发送 ACK=1,Seq=n,Ack=m+1 的报文,进入 CLOSE_WAIT 状态,确认收到关闭请求(此时被动关闭方仍可发送剩余数据)。
  3. 第三次挥手(被动关闭方 → 主动关闭方): 被动关闭方数据发送完毕后,发送 FIN=1,ACK=1,Seq=p,Ack=m+1 的报文,进入 LAST_ACK 状态,请求关闭自身到主动方的连接。
  4. 第四次挥手(主动关闭方 → 被动关闭方): 主动关闭方收到后,发送 ACK=1,Seq=m+1,Ack=p+1 的报文,进入 TIME_WAIT 状态。被动关闭方收到后进入 CLOSED 状态,连接彻底关闭。

关键点总结

  • 三次握手原因:防止失效的连接请求报文段被误认为是有效请求,避免资源浪费。
  • 四次挥手原因:被动关闭方可能有未发送完的数据,需分两步确认(先确认收到关闭请求,再发送自身关闭请求)。
  • TIME_WAIT 作用:确保最后一次 ACK 报文成功到达,避免因网络延迟导致被动关闭方重发 FIN 报文。

TIME_WAIT状态:原理与解决

1. 原理

TIME_WAIT主动关闭方在发送最后一个ACK后的状态,持续时间为2MSL(通常是1-4分钟,取决于系统配置)。
作用

  • 确保被动关闭方收到最终ACK:如果ACK丢失,被动关闭方会重发FINTIME_WAIT状态的主动方可以再次发送ACK,避免被动方一直处于LAST_ACK等待。
  • 防止旧报文干扰新连接:2MSL足够让网络中所有属于该连接的旧报文段过期,避免其被误认为是新连接的报文(新连接可能复用相同的源IP、端口和目的IP、端口)。

2. 问题场景

当主动关闭方(如高频创建短连接的客户端)频繁关闭连接时,会积累大量TIME_WAIT状态的连接,可能导致:

  • 端口耗尽:每个TIME_WAIT连接占用一个本地端口,端口数量有限(默认65535),新连接可能因无端口可用而失败。
  • 系统资源消耗:过多TIME_WAIT连接会占用内存和文件描述符。

3. 解决方法

(1)内核参数调优(Linux)

通过调整TCP内核参数减少TIME_WAIT的影响:

  • net.ipv4.tcp_tw_reuse:允许重用处于TIME_WAIT状态的端口(需满足一定条件,如新连接的timestamp大于旧连接),适用于客户端(主动关闭方)。
    echo 1 > /proc/sys/net/ipv4/tcp_tw_reuse
    
  • net.ipv4.tcp_max_tw_buckets:限制TIME_WAIT连接的最大数量,超过后会被强制清理(可能影响稳定性,需谨慎)。
    echo 10000 > /proc/sys/net/ipv4/tcp_max_tw_buckets  # 默认通常是180000
    
  • (不推荐)net.ipv4.tcp_tw_recycle:开启TIME_WAIT快速回收,但在NAT网络(如多客户端通过同一网关访问服务器)中会导致连接失败(已在Linux 4.12+移除)。
(2)应用层优化
  • 使用长连接:通过复用连接(如HTTP Keep-Alive、TCP长连接)减少连接创建/关闭频率,从根源减少TIME_WAIT
  • 调整主动关闭方:让服务器作为主动关闭方(需业务允许),服务器通常有更充足的资源和更易调优的内核参数。

CLOSE_WAIT状态:原理与解决

1. 原理

CLOSE_WAIT被动关闭方在收到主动方的FIN并发送ACK后的状态,直到被动方自身发送FIN前一直保持。
本质:被动关闭方的应用层未及时调用close()(或类似关闭接口),导致内核无法发送FIN,连接卡在CLOSE_WAIT

2. 问题场景

CLOSE_WAIT状态的持续时间完全由应用层决定:如果应用程序存在bug(如忘记关闭连接、异常处理中未释放连接),CLOSE_WAIT连接会持续积累,最终耗尽系统文件描述符,导致新连接无法建立。

3. 解决方法

CLOSE_WAIT的核心问题在应用层,需从代码和监控入手:

  • 修复代码逻辑

    • 确保连接不再使用时,显式调用关闭接口(如socket.close()conn.Close())。
    • 异常场景(如超时、错误)中必须释放连接,避免资源泄漏(可使用try-finallydefer机制)。
  • 设置连接超时

    • 对长期空闲的连接,通过超时机制(如SO_TIMEOUT、应用层心跳检测)主动关闭,避免永久滞留。
  • 监控与告警

    • 通过netstat -an | grep CLOSE_WAIT | wc -lss -ant | grep CLOSE-WAIT | wc -l监控数量。
    • CLOSE_WAIT超过阈值(如数百)时触发告警,及时排查代码问题。

总结

状态 所属角色 持续时间 核心原因 解决思路
TIME_WAIT 主动关闭方 固定2MSL 确保报文可靠与避免旧连接干扰 内核调优、长连接复用
CLOSE_WAIT 被动关闭方 由应用层决定 应用层未及时关闭连接 修复代码、超时控制、监控告警

TIME_WAIT是TCP设计的必要状态,少量存在是正常的;CLOSE_WAIT则几乎总是异常,需优先排查应用程序逻辑。