开发实录

测试机连上以后,我们又做了个文件互传助手

「把这个链接发到测试机上」

Android 配套 App 做出来以后,测试机已经可以扫配对码,把流量切到电脑上的 DevPeek。接口调到一半,我们又碰到另一件很小、但一天会发生很多次的事:

「把这个链接发到测试机上,我用真机打开。」

自己的手机上,这句话通常意味着发到「文件传输助手」或者自己的聊天窗口,再从手机点开。但团队测试机一般不会装这些聊天、通讯软件,也不应该登录某位同事的个人账号。

链接短一点还能手敲。遇到带一长串 Query 的活动页、临时环境地址,输一次就很容易漏字符。临时生成二维码、打开一个局域网页面、接数据线跑 adb push,都能解决,但为了传几行文字,每次都要先搭一座桥。

反方向更麻烦:测试机刚截的异常页面要传回电脑,手机里的日志和 JSON 要拿到桌面看,录屏要交给开发复现。没有通讯软件时,往往又得插线、开临时文件服务,或者先想办法把文件传到另一台手机。

上一篇 解决的是测试机换人时反复改系统代理。做到配对之后,一个很自然的问题就冒出来了:手机和电脑既然已经知道彼此,能不能顺手把这些信息也直接传过去?

我们想做的不是另一个聊天软件

互传助手不是 Android App 最初的目标。扫码切代理这条主链做完以后,手机与电脑已经有了配对关系、设备名和可达地址,我们只是顺着这些现成条件,多解决一个很窄的场景:当前测试机与当前配对电脑之间,临时交换联调资料。

电脑上的 DevPeek「手机」面板和 Android App 里都有一个对话式入口。文字可以双向发送;图片、视频和普通文件也可以从任意一端选取。收到内容后:

  • 文字可长按复制,适合 URL、JSON 片段和临时参数;
  • 图片可以直接预览,也可以复制后粘到其它应用;
  • 视频可以在会话里打开播放,不必先另存再找播放器;
  • APK、HAR、日志等普通文件显示文件名、大小和传输进度,完成后可另存;
  • 传输失败可以重试,断开后仍能查看之前的会话记录。

单文件当前限制为 2 GB。这是临时互传,不做文件夹同步、云端备份,也不自动同步整块剪贴板。APK 在这里也只是一个普通文件,传到手机后仍要按 Android 的安装流程确认,不是远程安装器。

界面做成聊天样式,并不是想继续长成聊天工具,只是「谁发的、发了什么、传完没有」用左右气泡最容易看清。测试机在几台电脑之间切换时,会话也按电脑分开,不会把上一位同事发来的内容混进当前窗口。

互传不能走抓包端口

第一反应可能是:手机本来就连着 DevPeek 的 HTTP 代理,文件也从 8888 传不就行了?

真这么做,会把两件事搅在一起。

8888 是 MITM 抓包链路。目标 App 的 HTTP(S) 经过这里,才会进入列表、SSL 解密、Mock 和 参数转换。互传的截图或安装包如果也从这里经过,大文件会被当成普通抓包请求处理,既污染列表,又给代理引擎增加一份完全没必要的正文。

还有更麻烦的一层:Android 配套 App 自己就在 VpnService 里声明了 HTTP 代理。如果互传请求继续遵循这份系统代理,它会先被送到 8888,再尝试访问电脑上的互传服务。路径绕回自己建立的 VPN,稍不注意就会形成环路。

所以互传没有复用抓包端口,而是在旁边开了一条 Assist 通道

  • 抓包代理继续监听自己的端口,默认是 8888;
  • Assist 服务优先使用 17891,端口被占用时自动寻找下一个可用端口;
  • 实际 Assist 端口跟着配对信息发给手机,用户不用再填一次;
  • Assist 连不上,不影响 VPN 抓包;两条链路各自失败、各自恢复。

二进制文件不会进入抓包引擎,也不会出现在 抓包列表 里。互传是配对后的附加能力,不是一次「自己抓自己」的 HTTP 请求。

手机必须绕回真实 Wi-Fi

端口分开还不够。Android 上的互传连接还要明确告诉系统:这条 Socket 不走当前 VPN。

App 建立 Assist 客户端时做了两层处理:

  1. 强制 NO_PROXY,不读取 VpnService 注入的 HTTP 代理;
  2. UnderlyingNetwork.bind() 把 Socket 绑定到手机当前的真实网络,通常就是扫码时使用的 Wi-Fi。

最终路径不是:

手机 → VpnService → 8888 抓包代理 → Assist

而是:

手机 → 真实 Wi-Fi → 电脑 Assist 端口

这条路径避免了 TUN 环路,也让互传文件不进入抓包列表。手机仍然可以同时把浏览器流量送到 DevPeek:一个 App 里有两条连接,一条为目标流量服务,一条为手机与电脑之间的信息交换服务。

这也是为什么互传要求手机和电脑处于可直接访问的局域网。它不经过云端服务器,也不会为了发一张截图先上传公网再下载回来。

消息走 WebSocket,文件走 HTTP

文字和文件不适合硬塞进同一种消息。

互传建立后,手机先通过 WebSocket 向桌面发送 hello,带上设备 ID、机型和平台。桌面回一条 hello.ok,双方才把这台设备标为可互传。之后,文字以及文件状态都走这条长连接:

text
file.offer
file.progress
file.done
file.fail

文件本体则走 HTTP。发送方先用 file.offer 告诉对方文件名、大小和 MIME 类型,再向 /assist/files/{id} 上传;接收方拿到完成消息后,从同一路径下载。这样 WebSocket 只承担轻量信令,大文件可以流式读写、显示进度,也能单独校验和保存。

连接断开时,正在传的项目会标记为失败,不会一直停在「传送中」。Android 端随后按退避间隔尝试恢复 Assist 连接;恢复不了,也不影响已经建立的抓包 VPN。历史消息和已接收文件会留在本机,下次打开还能查看。

我们没有为了「协议统一」把几百 MB 的视频编码进 JSON,也没有让文件传输反过来绑住代理核心。工具内部多一条通道,换来的是抓包和互传互不拖累。

本地直传,也不等于可以忽略安全边界

不要求测试机安装聊天软件、登录个人账号,也意味着临时资料不用先上传到第三方服务;但「局域网直传」不自动等于「端到端加密」。

当前 Assist 使用局域网内的 ws://http://,不提供公网中继,也没有面向不可信网络设计的账号认证和端到端加密。它遵循的仍是配对场景:手机与电脑在同一个可信局域网,配对码不要发到公网,Assist 端口也不要暴露到公共网络。

传 Token、Cookie、生产数据或用户隐私之前,仍要先判断这些内容是否应该离开原设备。需要长期保存、权限控制或审计的文件,应该回到团队正式的协作与存储系统。DevPeek 的互传助手解决的是眼前这一次搬运,不替代这些系统。

它与桌面的 局域网协作 也有区别:局域网协作面向两台 DevPeek,发送抓包请求和调试录制;互传助手面向 Android 测试机与当前电脑,发送文字和普通文件。

配对之后,少绕一个中转站

我们做 Android 配套 App,最初只是为了让测试机换人时不用反复修改系统代理。互传助手是这条主链完成后捎带实现的:既然配对信息和局域网连接已经有了,就再少折腾一次数据线、临时二维码或文件服务。

以前,把 URL 放到没有通讯软件的测试机上,要手敲、扫临时二维码或者插线;把截图拿回电脑,又要反着走一遍。现在测试机既然已经和桌面建立了关系,就让文字和文件沿着这段局域网连接直接过去。

不要求云盘,不要求联系人,也不要求在公共测试机上登录谁的个人账号。只是从电脑到手机,再从手机回来。

相关文档


配套 Android App 与互传助手仍在随桌面 1.4.x 打磨,目前不上架。电脑侧可从 DevPeek 官网 下载;如果你经常在手机和电脑之间搬 URL、截图、日志或安装包,欢迎到 GitHub Discussions 说说最常传的是什么。