"Broken pipe" / "client_loop: send disconnect" 是什么意思
你的 SSH 会话死了,因为底下的 TCP 连接没了——典型是闲置一段时间之后。packet_write_wait: Broken pipe 和 client_loop: send disconnect: Broken pipe 是同一个故事的两个角度:客户端往一条已经不存在的连接里发数据。这不是认证或服务器问题,是连接存活问题,三个经典元凶:NAT 路由器静默丢弃闲置连接、网络在你脚下变了(Wi-Fi ↔ 蜂窝)、以及任一端激进的闲置超时配置。
三个元凶
| # | 元凶 | 规律 |
|---|---|---|
| 1 | NAT/路由器闲置超时 | 安静 N 分钟后死;一直打字就没事 |
| 2 | 网络在脚下变了 | 走出 Wi-Fi 范围 / 切网络时死 |
| 3 | 配置的闲置断开 | 服务器的 ClientAliveInterval/CountMax 在关安静会话 |
修法 1——心跳保活(标准解)
保活发送微小的心跳包,让 NAT 表和闲置计时器永远看不到"安静"的连接。客户端(~/.ssh/config):
Host *
ServerAliveInterval 60
ServerAliveCountMax 3
# = 每 60 秒 ping 服务器一次;丢 3 次就放弃 或服务器端(/etc/ssh/sshd_config),覆盖包括移动 App 在内的所有客户端:
ClientAliveInterval 60
ClientAliveCountMax 3 两者有其一通常就能整类消灭闲置掉线。移动客户端在前台时一般会发自己的保活——服务器端设置是无论什么客户端都生效的双保险。
修法 2——接受移动网络会漫游,提前安排
手机从 Wi-Fi 跳到蜂窝时,TCP 连接的地址变了——普通 SSH 不可能挺过去;无论保活怎么设,会话都会断。你的选项:
- mosh——为漫游而生(UDP,不绑地址)。需要装在服务器上;没有隧道/SFTP。见 Mosh vs SSH。
- 稳定地址上的快速重连——移动端的务实答案:走 Tailscale,服务器在每个网络上都是同一个地址,重连就是一下点击、什么都不用重填。TermAI 内置 Tailscale,重开会话回到原处。
- 服务器上的 tmux/screen——无论连接怎样,你的工作活着:
tmux attach重新挂上,跑着的进程还在。和上面任一选项完美配合。
修法 3——长任务本来就不该指望你的会话
如果掉线杀死了一个长任务,持久的习惯是压根别把任务绑在你的终端上:跑在 tmux 里,或 nohup long_command &。在手机上这一点加倍重要——长等待中操作系统可能挂起 App。任务分离地启动,放心关 App,稍后回来看。
常见问题
为什么我的 SSH 会话闲几分钟就死?
NAT 路由器或闲置计时器丢弃了安静的连接。客户端设 ServerAliveInterval 60 或服务器设 ClientAliveInterval 60。
SSH 能挺过 Wi-Fi 切蜂窝吗?
普通 SSH 不能——地址变了。用 mosh(挺得过漫游),或稳定的 Tailscale 地址做一键即时重连,服务器上配 tmux 保住工作。
怎么不让长任务跟着连接一起死?
分离运行:tmux/screen 里,或 nohup。任务从此比任何掉线都活得久。
快速事实
- 含义:TCP 连接死了(通常被闲置丢弃),不是认证/服务器的错
- 标准解:保活——客户端
ServerAliveInterval 60或服务器ClientAliveInterval 60 - 网络漫游:普通 SSH 挺不过——mosh,或稳定地址 + 快速重连
- 保住工作:tmux/nohup,让任务比会话长寿
Free on iOS and Android. 5 AI requests/day on the free tier, plus unlimited SSH/SFTP and built-in Tailscale.