无线 ADB 到底怎么连上手机?从发现设备到恢复投屏
mDNS 找入口,TCP 建通道,TLS 保护通信:顺着一次无线投屏,把这些名字串起来
手机提供入口,电脑找到并连接它,查看器再把连接变成画面。无线投屏背后的几个协议,各管其中一段。
🎯 拔掉 USB 之后,连接是怎么来的?
我原来用 USB 把 Android 手机的画面显示在 Mac 上。切成无线后,又陆续遇到几个问题:为什么一下就连好了?手机重启后谁来连接谁?查看器怎么出现了同一台手机的两个端口?
整个过程其实分成了两步:先利用已有的 USB 调试授权,建立传统无线 ADB 连接;后来通过这条已经可用的无线连接,启用系统原生无线调试。确认新的加密连接可用后,再关闭旧的 5555 入口。
没有 USB 也能完成后半段,是因为当时已有一条能操作手机的无线通道,电脑也已经被手机信任。 这不等于任何电脑只要知道地址,就能直接接管手机;首次使用仍需按系统要求完成授权或配对。
先认清通信两端的角色:
| 角色 | 在哪里 | 负责什么 |
|---|---|---|
| ADB client | Mac | 接收查看器或终端发出的操作请求 |
| ADB server | Mac 后台 | 管理设备连接,把请求送到指定手机 |
| adbd | 手机 | 接收调试请求,提供设备侧服务 |
这里最容易叫混:ADB server 在电脑,adbd 在手机。 电脑端的后台服务通常由 adb 工具按需启动,多个程序可以共用它。Android 官方文档介绍了这套分工。
查看器 / adb 命令
↓
Mac 上的 ADB server
↓ USB 或网络
手机上的 adbd
无线化改变了中间的传输方式;设备发现、身份认证和画面传输,则需要分别看。
🧭 mDNS:电脑怎么知道手机在哪里?
传统连接可以记住一个固定的 手机 IP:5555。但系统无线调试通常分配动态端口,手机 IP 也可能变化。一直拨旧地址,显然不够。
这时需要服务发现:电脑要知道,目标设备现在提供什么服务,入口在哪里。
mDNS 是 Multicast DNS,即组播 DNS。它让同一局域网中的设备通过 UDP 5353 端口查询和回答名字、地址等信息;配合 DNS-SD,就能发布和查找具体服务。macOS 的 Bonjour 支持这套机制。mDNS 标准
用日常语言描述,过程像这样:
Mac:附近谁提供 ADB 无线调试?
↓ 局域网查询
手机:我提供,服务名是这个,连接入口在这里。
↓
Mac:找到目标,取得它当前的地址和端口。
手机服务上线时也可以主动公告。因此无论是手机晚开机,还是 Mac 晚启动,都有发现对方的机会,前提是服务正常发布、局域网允许这些组播消息通过。
Android 原生无线调试发布的连接服务类型是 _adb-tls-connect._tcp。这里的 _tcp 表示发现之后应通过 TCP 连接这个服务,发现消息本身仍走 mDNS。AOSP 无线 ADB 设计
它确实有点像蓝牙的“发现附近设备”,但通信依靠局域网。更关键的是,发现入口不等于获得使用权限:服务公告提供线索,后面还要建立连接、完成认证。
🔐 TCP 和 TLS:谁先连接,连上后怎么聊?
找到入口后,Mac 主动连接,手机监听并接受连接。手机开机、发布服务,不代表它主动向查看器建立了这条 TCP 连接。
查看器通过 adb 客户端提出连接请求,Mac 上的 ADB server 再调用操作系统完成 TCP 建连:
Mac 的 ADB server 手机 adbd
│ │ 已监听端口
│ ── SYN:请求连接 ───────────→ │
│ ←─ SYN + ACK:回应并确认 ──── │
│ ── ACK:确认 ───────────────→ │
│ │
│ TCP 通道建立 │
│ ←── TLS 握手与身份认证 ─────→ │
│ ←════ 加密的 ADB 通信 ══════→ │
握手由两边操作系统处理,TCP 提供可靠、有序的双向字节流。TCP 标准
TLS,就是 HTTPS 用的那一层吗?
对。TLS 提供加密、完整性保护,并参与身份认证。熟悉 HTTPS 的话,可以这样对照:
HTTPS(HTTP/1.1、HTTP/2): HTTP → TLS → TCP
原生无线 ADB: ADB → TLS → TCP
保护通信的技术相同,上面承载的协议不同。传统 ADB TCP 模式已有调试密钥认证,但不提供这条原生无线调试通道的 TLS 加密;认证谁能连接,与保护传输内容,是两件事。
这次新连接成功复用了手机已有的电脑调试授权,没有重新插线。换一台电脑、撤销授权或清除手机数据后,就需要重新核对配对和授权条件,不能照搬本次结果。
连上以后,是不是电脑问一次、手机答一次?
取决于具体操作。读取一个属性,接近“发命令、返回结果”;交互式 shell 则会持续传输入和输出。投屏建立视频通道后,手机可以持续发送视频数据,电脑不用每一帧都问“有新图了吗”。
谁先发起连接,不决定之后谁才能说话。 ADB 可以在连接上承载多条逻辑数据流。ADB 协议说明
🔄 手机重启后,为什么要让两端各做一件事?
重启会结束旧 TCP 连接和投屏会话。即使授权记录还在,也必须重新建立通信。
这次测试的设备还有一个具体行为:重启后,系统会关闭原生无线调试。因此我利用设备已有的 root 权限,加了一个一次性的开机辅助:等待系统启动完成、Wi-Fi 拿到地址后,再启用系统无线调试。
它没有替换 adbd,也没有自己实现另一套广播。实际提供 TLS 监听和 mDNS 服务的,仍是 Android 原生组件。辅助只在开机时运行一次,不会常驻反复打开用户后来手动关闭的开关。
root 用来解决这台设备的开机自动启用问题,并不是使用系统无线调试的通用前提。
把手机端和电脑端放到一起,恢复过程就清楚了:
手机 Mac / 查看器
│ │
│ 系统启动,Wi-Fi 就绪 │ 发现旧连接断开
│ 开启原生无线调试 │
│ 发布当前服务入口 ─────────────────────→ │ 找到已记住的设备
│ │
│ ←──────── 新的 TCP+TLS 连接 ────────── │
│ ←──────── 核对身份、建立投屏会话 ────── │
│ ───────── 编码视频数据 ───────────────→ │ 解码并显示新画面
所以,“手机自动开启”和“电脑自动连接”分别负责链路两端。只做前者,没有人来连;只做后者,手机没提供入口,重试也不会成功。
这条链路已经做过实际重启验证:手机分配了不同的 TLS 端口,查看器没有依赖手动连接命令,就找到新入口并显示了新画面。随后关闭传统 5555 监听,避免同一台手机在列表中出现两个连接入口。
⚙️ 自动重连:找新入口,同时控制重试节奏
只记住地址,端口变了就找不到;只做发现,也不能保证每次连接都立即成功。查看器因此把两种能力结合起来:
| 遇到的情况 | 查看器的处理 |
|---|---|
| 暂时没有发现新入口 | 对保存的地址进行退避重连 |
| 发现匹配设备的新入口 | 尝试连接,核对序列号后再更新选择 |
| 同一广播被多个接口重复列出 | 去重;跳过尚未解析出的无效地址 |
| 发现多个无法明确区分的匹配入口 | 暂不自动切换,避免选错设备 |
重试先快后慢:立即尝试,连续失败后依次等待约 1 → 2 → 4 → 8 → 16 → 30 秒,带少量随机抖动,等待不超过 30 秒。这样既能较快处理短暂抖动,也减少长期离线时的无效拨号。
但重试不会无限继续:连续离线 10 分钟后停止,也可以提前点击“停止重连”;点击“继续重连”会立即再试,并重新计时。 停止期间不主动连接或查询发现结果,已有连接和画面不会因此被断开。
这还带来一个排查上的提醒:某个工具的发现列表为空,不足以单独证明手机没发布服务。此次就出现过 ADB 的 mDNS 列表为空,而 macOS Bonjour 仍能发现服务的情况。需要区分手机是否发布、网络是否传递,以及客户端是否正确发现。
📝 连接恢复,最后要验收到哪一步?
发现服务、连上 ADB、恢复投屏,是三个不同的结果:
| 验收点 | 已经证明什么 |
|---|---|
| 能发现服务 | 取得了候选入口,尚不代表认证成功 |
| ADB 设备在线,命令可执行 | 调试通道恢复,尚不代表视频流正常 |
| 当前会话收到并解码新帧 | 查看器确实恢复了画面 |
scrcpy 会在手机上采集并编码屏幕,通过 ADB 隧道传送视频数据,再由电脑解码显示。它不是不断执行截图命令刷新图片。scrcpy 开发文档
因此,连接在线但画面失败时,仍可能需要单独重试投屏会话;留着的最后一帧也只能算历史画面。
再把所有名字串起来:mDNS 帮电脑找到手机,TCP 建立双向通道,TLS 保护 ADB 通信,scrcpy 把屏幕变成可传输的视频。 重启以后,沿着这条顺序逐段恢复,才能从“设备上线”走到“画面回来了”。