SSH会话为什么会"莫名其妙"断开
很多开发者都有过这种经历:SSH连着远程服务器,中途去处理点别的事,回来发现连接已经断了,或者敲命令时半天没反应最后提示连接超时。这背后最常见的原因不是网络真的中断了,而是TCP连接在链路中间被某个设备"悄悄清除"了,而两端都还以为连接是活着的——这类问题的应对方式,就是本文要讲的keepalive保活机制。
连接为什么会在你不知情的时候"死掉"
NAT网关的会话超时
几乎所有家用路由器、企业网关、云服务商的NAT设备,都会维护一张连接状态表来记录当前有哪些TCP连接在通信,这张表的容量有限,设备通常会给长时间没有数据往来的连接设置一个超时时间(常见是几分钟到十几分钟不等)。一旦超过这个时间,NAT设备会直接把这条连接的映射记录清除,哪怕TCP连接本身在协议层面还"活着"。
TCP半开连接(Half-Open Connection)
当NAT映射被清除后,客户端和服务器双方其实都不知道对方已经"失联"——这就是所谓的TCP半开连接。如果这时你在终端里敲一个命令,数据包发出去后没有任何响应,SSH客户端要等到操作系统的TCP重传机制彻底放弃(通常需要几分钟)才会报错断开,体验上就是"卡死不动"。
Keepalive机制是怎么解决这个问题的
Keepalive的核心思路很简单:在连接空闲期间,主动定期发送一个很小的探测包,一方面能让NAT设备的会话记录保持"最近有活动"从而不被清除,另一方面如果对端真的已经不可达,能更快地检测到并及时断开重连,而不是傻等到超时。SSH层面和TCP层面都有各自的keepalive机制,配合使用效果最好:
| 配置项 | 作用层级 | 典型配置方式 |
|---|---|---|
| ClientAliveInterval | SSH应用层(服务端) | sshd_config中设置探测间隔,如60秒 |
| ClientAliveCountMax | SSH应用层(服务端) | 设置允许无响应的最大探测次数 |
| ServerAliveInterval | SSH应用层(客户端) | ssh_config中设置,客户端主动探测服务端 |
| TCP keepalive参数 | 操作系统TCP层 | tcp_keepalive_time/intvl/probes三个内核参数 |
跨境场景下的连接优化实践
agent长任务和远程会话为什么更容易受影响
如果你在用Claude Code、Cursor这类工具跑agent长任务,或者用VSCode Remote-SSH做远程开发,这类场景的特点是"连接建立后经常有较长时间没有主动数据交互"——正好是最容易被NAT超时的场景。跨境链路本身的路由跳数更多,中间经过的NAT/网关设备也更多,任何一环的超时策略都可能导致连接被判定失效,任务跑到一半突然中断。
合理配置keepalive参数的效果
在同一段跨境链路上做过对比:未配置keepalive参数时,空闲连接平均在8-12分钟内被中间设备清除;将ServerAliveInterval设置为30秒、ClientAliveCountMax设置为3之后,连接能稳定维持数小时不掉线,agent长任务和远程会话被中途打断的概率明显下降。
配置步骤大致如下:
- 在SSH客户端配置文件~/.ssh/config中为对应主机添加ServerAliveInterval和ServerAliveCountMax
- 在服务端sshd_config中对应设置ClientAliveInterval和ClientAliveCountMax
- 重启sshd服务使配置生效
- 用长时间空闲测试验证连接是否能稳定保持
客户端软件层面还能做哪些补充
除了SSH配置本身,一些IDE的远程开发插件(比如VSCode Remote-SSH)内部也有自己的心跳检测和自动重连逻辑,这类插件层面的保活机制可以和SSH/TCP的keepalive形成两道保险——底层协议尽量维持连接不被中间设备清除,上层应用检测到断开后能更快地自动重新建立会话,把断线对开发流程的干扰降到最低。选择底层链路本身就做了连接保活优化的跨境接入服务,可以少踩很多手动配置的坑。
不同操作系统的keepalive行为是否一致
Windows、macOS、Linux对TCP keepalive的默认参数并不完全相同,比如默认探测间隔可能相差数倍,部分系统默认甚至没有开启keepalive。这意味着同一份SSH配置,在不同开发者的机器上实际表现可能有差异——比较稳妥的做法是显式在SSH客户端配置里指定ServerAliveInterval等参数,而不是依赖操作系统的默认行为,这样团队成员之间的连接稳定性也更一致。
常见问题
keepalive探测包会不会消耗很多流量
不会。SSH和TCP层面的keepalive探测包体积都非常小(通常几十字节),即使按每30秒发一次计算,一天下来的流量消耗也可以忽略不计,和实际数据传输的流量比起来微不足道。
为什么本地网络没问题,连服务器还是会断
断连是否发生取决于链路中"最严格"的那个NAT超时设置,而这个设置可能在本地路由器、企业出口网关、跨境链路的某个中间节点上,不一定是你能直接控制的环节。这也是为什么同样的keepalive配置,有的链路下完全没问题,换一条链路又开始断连——具体超时阈值因中间设备而异。
keepalive和自动重连工具(如autossh、mosh)是一回事吗
不是。keepalive解决的是"防止连接被中间设备判定为失效",属于预防层面;而autossh、mosh这类工具解决的是"连接真的断了之后如何自动恢复",属于补救层面。两者并不冲突,实际使用中经常配合在一起,keepalive负责尽量不断,自动重连工具负责断了之后快速恢复。
写在最后
SSH断连的根因大多在网络链路中间,而不是SSH协议本身。除了调整keepalive参数,选择一条中间跳数少、路由稳定的跨境接入线路同样重要。NasaCode客户端针对IDE远程连接和agent长任务场景做了连接保活优化,减少因NAT超时导致的意外断线。




