安卓 Termux 一切到后台 SSH 就断?mosh 加两个隐藏坑,一次修到位

URL
date
Aug 9, 2026
slug
termux-mosh-android-macos-persistent-shell
summary
安卓杀后台+TCP脆弱导致Termux SSH频繁断连:保活三开关+mosh+tmux,附zsh PATH与macOS防火墙两个坑
status
Published
tags
termux
mosh
ssh
macos
android
type
Post
notion image
notion image
notion image
在安卓手机上用 Termux SSH 连自己的 Mac 或服务器,是很多折腾党的日常。但你大概率遇到过这个场景:SSH 连得好好的,切出去回个微信,再切回来 - 会话已经断了,跑了一半的下载任务也没了。
client_loop: send disconnect: Broken pipe
Connection to your-host closed.
这篇讲清楚三件事:为什么会断、怎么修到基本不再烦你、以及两个 macOS 上常见但容易被忽略的坑(zsh PATH 和应用防火墙)。全程可复现。

一、为什么切出 App 就断:不是网络,是安卓在杀进程

先说结论:"切出 App 秒断"这个特定症状,不是网络问题,是安卓的电源管理在杀 Termux 的后台进程(切网/断网导致的断连是另一类,下文分开处理)。
用十岁小孩能懂的话说:安卓觉得"你都不看这个 App 了,它还占着电干嘛",于是把 Termux 和它养的所有子进程(包括你的 ssh)直接掐死。SSH 是 TCP 长连接,客户端进程一死,连接自然就断。
更麻烦的是这里有两层杀手:
  1. 电池优化:经典的后台限制,白名单能解决
  1. Phantom Process Killer:Android 12 新增的机制,专杀 App 的子进程(ssh 正是 Termux 的子进程),电池白名单管不住它
所以只做"允许后台运行"是不够的,两层都要处理。
另外先把问题拆干净 - 你遇到的断连其实是两类,解法不同、不能互相替代:
  • A 类:客户端进程被杀(切 App 后安卓杀 Termux)→ 靠本节的保活三开关
  • B 类:进程还活着,但 TCP 连接断了(切 WiFi/5G、电梯断网、NAT 超时)→ 靠下一节的 mosh
Broken pipe 两类都可能触发,判别方法:切回 Termux 看 shell 提示符还在不在 - 提示符都没了是 A 类,提示符在但连接死了是 B 类。

二、基础保活:三个开关

1. Termux wake lock

在 Termux 里跑:
termux-wake-lock
通知栏出现 "Termux wake lock held" 就生效了。

2. 电池白名单

设置 → 应用 → Termux → 电池 → 不受限制。国产 ROM(小米/华为/OPPO 等)还要在「自启动管理/后台管理」里额外加白名单。

3. 关掉 Phantom Process Killer(Android 12+)

如果做完前两步、切 App 后 Termux 里的进程还是被杀(A 类症状复发),再上这一步。需要电脑 adb:
adb shell settings put global settings_enable_monitor_phantom_procs false
注意:这是全局系统设置,具体行为随 Android 小版本和厂商 ROM 有差异,系统大版本更新后可能被重置。想恢复默认就把 false 换成 true 再跑一次。

三、根治方案:换 mosh

保活解决 A 类,但 B 类它管不了:WiFi 切 5G、电梯里断网几秒,TCP 连接照样死。B 类的根治是把 SSH 换成 mosh。
mosh(mobile shell)是 MIT 出的移动优先远程终端:
  • UDP + 状态同步:不维护"连接",只同步终端状态。切网、换 IP、断网几分钟,回来会话原封不动
  • 本地回显:打字不用等服务器回包,高延迟网络下手感天差地别
  • 首次握手和认证走 SSH,之后的 UDP 流量用 mosh 自己的 AES 加密协议(注意它不支持 SSH 的端口转发等高级功能)
边界要讲清楚:mosh 抗的是网络中断。如果 mosh 客户端进程本身被安卓杀了(A 类),新起的 mosh 恢复不了旧会话 - 所以上一节的保活和这一节的 mosh 是互补关系,不是二选一,远端现场的最后一道保险是下文的 tmux。
安装两边都要装:
# 手机 Termux 侧
pkg install mosh

# Mac 侧(服务器是 Linux 就 apt install mosh)
brew install mosh
然后把连接命令从 ssh user@host 换成:
mosh user@host
理论上到此结束。但如果你的服务器是 macOS,大概率会撞上下面两个坑 - 这才是本文的重点。

四、坑 1:明明 brew 装了,却报 command not found: mosh-server

第一次连,直接失败:
zsh:1: command not found: mosh-server
Connection to your-host closed.
/data/data/com.termux/files/usr/bin/mosh-bin: Did not find mosh server startup message.
明明 brew install mosh 成功了,mosh-server 也真实存在于 /opt/homebrew/bin/,为什么找不到?
根因:mosh 客户端是先 SSH 到服务器、再在远端启动 `mosh-server`。而这种非交互 SSH 场景下,zsh 不加载 `.zprofile`,Homebrew 的 PATH 根本不在环境里。
验证很简单,SSH 非交互跑一句:
ssh your-host 'echo $PATH'
# 输出: /usr/bin:/bin:/usr/sbin:/sbin   <- 没有 /opt/homebrew/bin
zsh 的启动文件加载规则是:
Homebrew 默认把 PATH 写在 .zprofile,所以交互登录一切正常,mosh 的非交互启动路径就瞎了。
修法:把 PATH 加进 .zshenv(唯一在非交互场景也会加载的文件)。Apple Silicon 的 brew 前缀是 /opt/homebrew,Intel Mac 是 /usr/local,用 brew --prefix 通吃:
echo "export PATH=\"$(brew --prefix)/bin:\$PATH\"" >> ~/.zshenv
验证修复:
ssh your-host 'command -v mosh-server'
# 输出: /opt/homebrew/bin/mosh-server   <- 修好了
顺带一提:这个坑不止咬 mosh。所有"经由非交互 SSH 调用 brew 装的工具"的场景(rsync 调远端 rsync、scp 后处理、远程脚本)都会踩,.zshenv 一次修,同类全部受益。

五、坑 2:macOS 防火墙静默拦 UDP

PATH 修好后再连,可能出现第二种症状:连接看似建立了,但终端黑屏卡住。
同一局域网/同一 VPN 内,最常见的元凶是 macOS 应用防火墙:它默认拦截未知程序的传入连接,而 mosh 恰恰需要服务器在 UDP 60000-61000 端口收包。SSH(TCP 22,由系统 sshd 处理)不受影响,所以你从来没注意过它。(如果是跨公网/路由器 NAT/云服务器安全组,UDP 还可能被这些层拦,需要相应放行 - 本文场景是内网直连,只涉及 macOS 这一层。)
修法有两种:
方案 A(最简单):连接的瞬间,Mac 屏幕上会弹窗「是否允许 mosh-server 接受传入连接?」- 点允许即可,一次生效。实测这就是最顺滑的路径,前提是你人在 Mac 前或能看到屏幕。
方案 B(无人值守):命令行加白名单,需要 sudo:
MS=$(realpath /opt/homebrew/bin/mosh-server)
sudo /usr/libexec/ApplicationFirewall/socketfilterfw --add "$MS"
sudo /usr/libexec/ApplicationFirewall/socketfilterfw --unblockapp "$MS"
注意 realpath:防火墙白名单登记的是真实二进制路径,而 brew 的 mosh-server 是指向 Cellar 版本目录的软链。这意味着 brew 升级 mosh 后真实路径会变、老规则失效 - 升级后如果又开始黑屏,重跑上面两行(或再点一次弹窗允许)即可。

六、最后一块拼图:服务端 tmux

mosh 解决"网络断连",tmux 兜住"客户端进程死亡":手机端 Termux 真被杀了、或者你想换台设备接管同一个现场,远端的 tmux 会话还在:
mosh user@host -- tmux new -A -s main
这一条命令的意思是:mosh 连上后自动附着到名为 main 的 tmux 会话(不存在则创建)。从此手机、平板、电脑任何设备连上来,看到的都是同一个现场。
边界同样讲清楚:tmux 抗的是客户端死亡,服务器重启它也活不了 - 跨重启的任务续跑要靠任务自身的断点续传或 systemd/launchd 之类的服务化,不在本文范围。

七、完整清单(TL;DR)

手机侧(一次性):
  1. termux-wake-lock + 电池白名单
  1. Android 12+:adb shell settings put global settings_enable_monitor_phantom_procs false
  1. pkg install mosh
服务器侧(macOS,一次性):
  1. brew install mosh
  1. echo "export PATH=\"$(brew --prefix)/bin:\$PATH\"" >> ~/.zshenv - 关键,否则非交互 SSH 找不到 mosh-server
  1. 首次连接时在 Mac 弹窗点「允许」(或 socketfilterfw 加白名单)
日常使用:
mosh user@host -- tmux new -A -s main
之后切 App、切网络、锁屏 - 只要保活生效、UDP 可达,回来时你的会话还在原地等你;就算 Termux 真被杀了,重连一条命令,tmux 里的现场也一个字不少。

八、排错速查表


© Ying Bun 2021 - 2026