scrcpy 设备连接完全指南:设备选择、无线 TCP/IP 连接与源码实现剖析
【免费下载链接】scrcpyDisplay and control your Android device项目地址: https://gitcode.com/GitHub_Trending/sc/scrcpy
本文基于 scrcpy 官方文档 Connection 与客户端源码,系统讲解多设备环境下的设备选择机制(--serial/--select-usb/--select-tcpip/--tcpip)、ANDROID_SERIAL环境变量、无线 TCP/IP 连接的自动与手动两种模式,并结合 app/src/adb/adb.c 与 app/src/server.c 中的实际调用链,剖析这些选项在底层是如何解析adb devices -l输出、判定设备类型、自动完成 USB 到 TCP/IP 切换的。读完后你可以独立完成多设备指定、免数据线投屏,并能读懂 scrcpy 连接阶段的日志输出。
一、设备选择(Selection)
scrcpy 通过adb枚举设备。当adb devices恰好只列出一台设备时,scrcpy 会自动选中它,无需任何参数。
当连接了多台设备时,必须用以下 4 种方式之一指定目标设备:
1. 通过序列号(serial)选择
scrcpy --serial=0123456789abcdef scrcpy -s 0123456789abcdef # 短选项 # 通过 TCP/IP 连接时,serial 就是 ip:port(行为与 adb 一致) scrcpy --serial=192.168.1.1:55552. 指定 USB 设备(要求恰好只有一台)
scrcpy --select-usb scrcpy -d # 短选项3. 指定 TCP/IP 设备(要求恰好只有一台)
scrcpy --select-tcpip scrcpy -e # 短选项4. 直接连接一个已在监听 TCP/IP 的设备
scrcpy --tcpip=192.168.1.1:5555 scrcpy --tcpip=192.168.1.1 # 默认端口为 5555此外,序列号也可以通过环境变量ANDROID_SERIAL提供(adb工具同样使用该变量),三种常见 Shell 下的写法:
# in bash export ANDROID_SERIAL=0123456789abcdef scrcpy:: in cmd set ANDROID_SERIAL=0123456789abcdef scrcpy# in PowerShell $env:ANDROID_SERIAL = '0123456789abcdef' scrcpy二、源码剖析:scrcpy 是如何完成设备选择的
上面 4 种选择方式在源码中统一收敛为「选择器(selector)」机制,核心实现在 sc_adb_select_device():
- 枚举设备:sc_adb_list_devices() 执行
adb devices -l,将输出解析为设备列表(serial、state、model)。解析逻辑由 app/src/adb/adb_parser.c 实现,并有对应的单元测试 test_adb_parser.c 覆盖。 - 按选择器过滤:每种命令行选项对应 sc_adb_device_selector 的一个类型——
SC_ADB_DEVICE_SELECT_ALL(无参数)、SC_ADB_DEVICE_SELECT_SERIAL(-s)、SC_ADB_DEVICE_SELECT_USB(-d)、SC_ADB_DEVICE_SELECT_TCPIP(-e),过滤规则见 sc_adb_accept_device()。 - 设备类型判定:sc_adb_device_get_type() 的规则非常简洁——serial 以
emulator-开头判为模拟器;serial 中含:判为 TCP/IP 设备(因为ip:port与真实 USB serial 可以靠冒号区分);其余判为 USB 设备。因此-e实际会同时选中「TCP/IP 设备和模拟器」,这与 adb 的-e语义一致(源码注释中亦有说明)。 - 数量校验与状态检查:匹配到 0 台或多台都会报错并打印全部设备列表(日志中带
-->标记的即候选设备);匹配到 1 台后还会检查其状态(sc_adb_device_check_state(),adb.c#L584-L604)——只有device状态可用,unauthorized会提示在设备上确认授权弹窗。
两个值得注意的匹配细节(adb.c#L513-L534):
- IP 前缀匹配:如果设备 serial 是
192.168.1.1:5555,而你用--serial=192.168.1.1(不带端口)匹配,scrcpy 会截取 IP 部分做前缀比较——因此--serial=IP可匹配该 IP 的任意 adb 端口,这也是--tcpip=192.168.1.1能自动补默认端口的基础。 ANDROID_SERIAL的读取时机:在 run_server() 中,当未显式给出-s/-d/-e时,scrcpy 会调用getenv("ANDROID_SERIAL");若设置了该变量,则隐式按 serial 选择器过滤,等价于scrcpy -s $ANDROID_SERIAL。
命令行选项的定义见 app/src/cli.c(-d/--select-usb)与 app/src/cli.c(-e/--select-tcpip),--tcpip选项在 app/src/cli.c#L904-L906。注意三者的互斥关系:在需要「先选设备」的路径上,req_serial、select_usb、select_tcpip三者至多只能设置一个(见 server.c#L967-L971 的断言)。
三、TCP/IP(无线)连接
scrcpy 依赖adb与设备通信,而adb本身支持通过 TCP/IP 连接设备。前提是设备与电脑处于同一网络。
3.1 自动模式(--tcpip)
--tcpip有两个用法:
用法 A:不带参数(设备上 adb TCP/IP 模式未开启,或不知道 IP)——先用 USB 连上设备,然后:
scrcpy --tcpip # 不带参数scrcpy 会自动:探测设备 IP 和 adb 端口 → 必要时开启 TCP/IP 模式 → 连接设备 → 再启动投屏。
用法 B:带地址(设备已在监听 adb 端口,通常是 5555):
scrcpy --tcpip=192.168.1.1 # 默认端口为 5555 scrcpy --tcpip=192.168.1.1:5555强制重连:在地址前加+前缀,会先断开旧连接再连接:
scrcpy --tcpip=+192.168.1.13.2 自动模式背后的调用链
整个流程集中在 app/src/server.c 的run_server()中,按是否带地址分两条路径:
路径一:--tcpip(无地址)→ sc_server_configure_tcpip_unknown_address()
- 先用 USB 选出一台设备(第二节的 selector 机制);若该设备已经是 TCP/IP 类型则直接复用。
- sc_server_switch_to_tcpip() 完成「USB → TCP/IP」切换:
- sc_adb_get_device_ip() 执行
adb -s <serial> shell ip route并解析出设备 IP; - get_adb_tcp_port() 读取设备属性
service.adb.tcp.port判断 TCP/IP 模式是否已开启; - 若未开启,调用 sc_adb_tcpip()(即
adb tcpip 5555,5555定义在 server.c#L23 的SC_ADB_PORT_DEFAULT),然后由 wait_tcpip_mode_enabled()轮询等待:每 250ms 重读一次service.adb.tcp.port,最多 40 次(约 10 秒),直到属性值等于 5555——这一步解释了「adb tcpip执行后 adbd 需要重启、存在短暂延迟」这一现象。
- sc_adb_get_device_ip() 执行
路径二:--tcpip=ip[:port](已知地址)→ sc_server_configure_tcpip_known_address()
- 地址中无端口时自动补上默认端口 5555;
+前缀会被 run_server() 检测出来,先执行一次adb disconnect(静默模式,连接不存在时不报错)再连接;- 随后调用 sc_server_connect_to_tcpip() 执行
adb connect ip:port。
一个容易踩坑的底层细节:adb connect无论成败退出码都是 0,所以 sc_adb_connect() 不得不读取其 stdout,只有输出以connected或already connected开头才判定成功;否则把 adb 的报错回显到 stderr。这就是连接失败时你会在终端看到类似failed to connect to '192.168.1.1:5555'输出的原因。
3.3 手动模式
也可以不借助--tcpip,直接用adb手动完成:
用 USB 把设备连到电脑;
让设备与电脑连入同一个 Wi-Fi网络;
获取设备 IP:在「设置 → 关于手机 → 状态信息」中查看,或执行:
adb shell ip route | awk '{print $9}'在设备上启用 adb TCP/IP 模式:
adb tcpip 5555;拔掉 USB 线;
连接设备:
adb connect DEVICE_IP:5555(DEVICE_IP换成上一步查到的 IP);像平常一样运行
scrcpy;用完后执行
adb disconnect。
自 Android 11 起,系统还内置了「无线调试」功能,可以完全跳过物理 USB 连接这一步,直接通过配对码完成无线 adb 连接(参见 ADB 官方文档中 wireless debugging 一节)。
四、自动启动(Autostart)
scrcpy 作者维护了一个小工具AutoAdb,可以在检测到新 Android 设备连接的瞬间自动执行任意命令。用它实现「设备一插上就启动 scrcpy」:
autoadb scrcpy -s '{}'其中{}是 AutoAdb 提供的占位符,会被替换为新连接设备的 serial,等价于scrcpy -s <新设备serial>,天然适配多设备环境。
五、常见问题速查
| 现象 | 原因与处理 |
|---|---|
Multiple (N) ADB devices报错 | 多台设备时未指定选择器;用-s/-d/-e指定,或设置ANDROID_SERIAL |
Device is unauthorized | 设备端未确认调试授权弹窗,在设备上点击允许即可(报错信息会引导查看 FAQ.md) |
Could not find any ADB device over USB/TCP/IP | -d/-e要求「恰好一台」,当前一台都没有;确认adb devices输出 |
adb tcpip后一段时间才能连上 | adbd 重启需要时间;自动模式内置了最多约 10 秒的轮询等待(40 次 × 250ms),手动模式建议稍等数秒再adb connect |
| 换了 Wi-Fi 后连接失败 | 设备与电脑必须同网段;重新查 IP 并adb connect |
适用范围说明:以上行为均基于当前仓库源码验证——默认端口 5555 由 SC_ADB_PORT_DEFAULT 决定;
--tcpip=+ip的强制重连、ANDROID_SERIAL兜底等细节均与文档 doc/connection.md 描述一致。若你使用 portable 版 scrcpy,adb 二进制随包附带,其查找逻辑见 sc_adb_init()。
【免费下载链接】scrcpyDisplay and control your Android device项目地址: https://gitcode.com/GitHub_Trending/sc/scrcpy
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考