开始的想法很简单,就是能通过 wifi 传 mp3 到手机上播放,有单曲循环、顺序播放、目录循环什么的就行了。客户端应不需装任何软件,就用浏览器传文件。
但市面上好像没有这种应用,不是要数据线拷文件,就是要在线播放它提供的歌曲,胡里花哨的一大堆不需要的功能,还几十、上百兆的。
那我就着手自己写一个吧,首先要一个 android http server,请参考 [用一个安卓 app 实验全栈开发] ,这个有了之后又要加入文件管理功能,比如:创建新文件夹、移动、删除、重命名等。把这个简单的文件管理界面加上后,再加上播放器相关的:单曲播放、单曲循环、顺序播放、目录循环。因为这是用浏览器的<audio>
标签播放,所以局域网内的其它人也可以同时播放你手机上的音乐。
那不是可以当作文件服务器用吗?干嘛限制只能上传 mp3 ?所以要让它可以上传十几个 G 的蓝光电影,或几百兆的 office 安装包。别人通过浏览器可以下载(或在线播放)你手机上的文件。好了,这些又有了之后感觉不太过瘾。
既然整个 boost asio 都在上面,顺便也写个 socks5 代理吧,让别人可以通过你手机代理上网,如果你手机有 vpn 账号的话还是有点用的。
目前所有的一切还在局域网内,要让它上广域网,界面是 vuejs 写的,运行在安卓 webview 里,我觉得应该把 webrtc 加上,webrtc 需要个 signaling server,然后我就用 nodejs 写了个 signaling server 做成了 docker 镜像(包含 stun/turn server ),随便在哪个有公网 ip 的服务器上 run 起来,不需要域名。这个 app 里再加上个“服务器配置界面”,可以配置多个服务器,就像 wow 的不同世界服一样,但要让它同时与不同服务器的玩家交互,就要有个 id 来标识它,就用手机硬件序列号的 hash 做 id 吧。比如玩家 a 同时在 s1,s2 两台服务器上出现的话,玩家 b 也在这两台服务器,那么 a 与 b 只会建立一个 webrtc 连接,因为同一 id 的玩家已有连接的话就不会再建立了。
流程是这样的:玩家打开 app,就会同时建立 N 个 websocket 连接到他配置的服务器去,配置了几个就连几个,如果没有一个服务器的话,那他就完全在自己局域网内当文件服务器用,每个公网服务器会随机选最多 100 个其它玩家的 id 发给它——通过加密后 websocket 连接(就像 https ),然后这个安卓 app 会把该服务器当作 signaling server,与这些玩家建立直接连接——其后与这些玩家的交互就不再经过任何服务器了。所以我又加了几个界面:附近玩家列表(直连的),私聊界面,附近频道,世界频道,及个人信息配置界面:昵称、头像、签名,是否允许加好友,是否允许音频 /视频聊天,是否允许被当作代理(这个后面会解释)。
私聊界面就类似微信的,可以发语音、文字、图片、文件什么的,还可以申请语音 /视频聊天,及请求代理。有的手机浏览器版本太低,建立实时音频 /视频聊天会失败,我个人测试用一加 5t 与华为平板可以视频聊天,但是与 nexus 6p 则会失败,可能要 Android7 以上的 webview 才行吧。不过这个不重要了,因为可以发录音文件嘛,就像微信那样按下说话,松开发送那种——而且还可以在附近频道群发。如果有人发垃圾消息可以屏蔽他,屏蔽后会与他断开直连,并不再接收其世界频道消息;有些人也可以加为好友,好友列表里的玩家会被优先直连,所有玩家不需要注册,他的手机就是他的 id,除非换手机。
到目前为止还没什么有创意的。上面说了每个玩家手机上都有个 socks5 代理,如果让别的玩家做代理服务器上网呢?
这个问题困扰了我许久,最初想用 udp 打洞穿透,然后 tcp 走 udp,但据说有些手机网 ISP 会屏蔽 udp,而且这个内 /外网的 ip 建立连接还要像 ICE 那样处理,相当于是把 webrtc 的功能再实现一遍,看了几篇 rfc 文档,觉得这个不是短期能做的。开始是想让 C++层直接穿透直连,因为 tcp 的监听也是在 C++做的,都放在一起做感觉会简单点,那时把那个udt(不是 udp )库都已编译成安卓版了,这个是可靠传输的 udp C++库,试了下感觉它那个接口跟 asio 不太兼容,明明用 C++写的,还非得要搞成 BSD socket 那样 C 接口,就放弃了。况且即使这个能用也还要实现几个 rfc 文档。所以就整个放弃 C++层穿透了。
webview 里的 webrtc 都已经直连了为什么不用呢?开始考虑的是 C++服务是运行在 Android Service 里,而 Webview 是运行在 Activity 里,service 可以长期运行,activity 切换到后台其中的代码执行就会被中断。所以又去查了下什么情况下 webview 里的 websocket 不会被中断。这篇文章说了个方法,试了一下,遗憾的是它需要用户手工开启特殊权限,而且允许这个特殊权限的接口在以后的 android 版本里还可能会变化。
算了,就直接用 Activity 里的 webview 里的 webrtc 吧。我把本地 socks5 的监听端口号+100,开另一个 tcp 监听(我称作远程 socks5 代理端口),当这个端口有连接过来,C++层会通过本地 websocket 询问 webview 层“是否有附近玩家同意作为代理”,有的话则通过 webrtc 通道(直连通道)通知对端的 webview 再通过它本地的 websocket 通知它的 C++层建立与它自身 socks5 的 tcp 连接……。说起来太拗口了,图示如下:
最终这样实现了,自己测试了一下效果还行,如果打开网站去查 ip 的话,会显示别人手机的 ip——为了测这个还买了个 XX 云 1 核 /1G/1M 的服务器自己搭了个 vpn 用另一台手机连接测试。因为没什么人脉,都是自己搞。
后面还想把 bt 下载也加进去,是用 webtorrent ( js 库)呢?还是用 libtorrent ( C++库)?我是倾向用 webtorrent,因为用 js 写起来简单些,结果一测根本就不支持老一点手机的 webview,就像那个视频聊天一样。
算了,等以后大家换几代手机再说吧。回想当初不是只写个音乐播放器吗,先到这吧,有点扯远了。
默认加了一个测试服地址——1 核 1G1 兆的服务器。你也可以自己起服务,然后让自己的圈内人都配置这个地址
signaling server 的代码地址,就一百多行 nodejs 代码
这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。
V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。
V2EX is a community of developers, designers and creative people.