为什么ssh长时间空闲后,终端就卡死了

·

问题原因

  • SSH 连接在长时间空闲后“卡死”(表现为终端无响应、输入无回显、Ctrl+C 无效等),\
  • 通常不是 SSH 协议本身“卡”了,而是连接被中间网络设备(如 NAT 网关、防火墙)静默丢弃所致。

具体分析

  • TCP 连接超时被中间设备丢弃
    • 多数 NAT 路由器或云服务商(如 AWS、阿里云)的防火墙/安全组默认 TCP 空闲超时时间为 5–15 分钟(例如 AWS 默认 350 秒)。
    • 若 SSH 会话长时间无数据包交换(即使你在终端看着 htop 不动也算“空闲”),中间设备会悄悄丢弃连接状态,但不向两端发送 RST/FIN。
    • 此时:
      • 客户端仍以为连接 alive,输入无响应(包发出去但被丢,无 ACK);
      • 服务端也无感知,直到尝试写数据才发现对端已不可达(但可能延迟很久)。
  • 服务端或客户端的 TCP keepalive 默认关闭或间隔太长
    • Linux 默认的 TCP keepalive 是 net.ipv4.tcp_keepalive_time = 7200 秒(2 小时),远晚于中间设备的超时。
    • 所以 keepalive 包来不及发送,连接已被丢弃。
  • 终端控制字符阻塞(较少见)
    • 如误按 Ctrl+S(XOFF 流控),导致终端本地“卡住”,但此情况可通过 Ctrl+Q 解除,且不影响 SSH 底层连接。

解决方案(推荐组合使用)

启用 SSH 客户端保活(最常用)

在 客户端 的 ~/.ssh/config 中为对应主机配置:

Host your-server  # 或 * 表示全局
    HostName example.com
    User peter
    ServerAliveInterval 60      # 每60秒客户端发一个空包探测
    ServerAliveCountMax 3       # 连续3次无响应则断开
    TCPKeepAlive yes            # 同时启用底层 TCP keepalive(可选)

✅ 效果:每 60 秒发一次 SSH_MSG_CHANNEL_REQUEST "[email protected]",穿透 NAT 设备,防止被丢弃。

服务端保活(需有服务器权限)

在服务端 /etc/ssh/sshd_config 中添加:

ClientAliveInterval 60
ClientAliveCountMax 3

重启 sshd:sudo systemctl restart sshd

⚠️ 注意:若中间设备超时 < 60 秒(如某些企业防火墙为 30 秒),可设为 30。

系统级 TCP keepalive 调优(全局生效)

临时调整(重启失效):

# Debian/Ubuntu
sudo sysctl -w net.ipv4.tcp_keepalive_time=300
sudo sysctl -w net.ipv4.tcp_keepalive_intvl=60
sudo sysctl -w net.ipv4.tcp_keepalive_probes=3
# 永久生效:写入 /etc/sysctl.d/99-tcp-keepalive.conf

诊断技巧

  • 检查是否真“卡”:另开终端 ping your-server。若 ping 通但 SSH 卡住 → 确认为 SSH 连接被丢弃。
  • 查看 SSH 日志(服务端):journalctl -u ssh -f
  • 抓包验证:sudo tcpdump -i any host your-server and port 22,看是否有 keepalive 包。