利用 Phoenix 代理进行 P2P WebRTC 开发

  • 谢丽

2015 年 8 月 19 日

话题:语言 & 开发架构

WebRTC(Web 实时通信)的创建主要是为了视频和音频通信,但它也有在两个浏览器之间传递二进制数据的 API。这为创建更多的点对点 Web 应用程序带来了许多机会,而且已经有许多有趣的应用程序是使用它创建的,如WebTorrentUberConference

Zohaib Rauf是一名软件工程师,他正在学习Elixir 语言。为了更好地理解 WebRTC,他使用它创建了一个 P2P 文件分享应用程序,目标是实现点对点的文件分享,而不需要任何中间人。文件发送者添加文件,并将唯一 URL 分享给接收者。接收者访问唯一 URL 就可以看到分享的文件并下载,如下图所示:

在该应用程序中,Zohaib 使用Phoenix 框架实现了一个用于初次握手的代理。近日,他发表了一篇博文,简要介绍了该代理的设计决策过程。

代理的作用

为了创建点对点的连接,发起者会创建一个包含其网络信息和其它媒体相关信息的握手请求。接收者在收到请求后会创建应答和它自己的申请并发送回发起者。发起者接收到应答后完成初次握手。至此,发起者和接收者就可以进行点对点通信了。在这个过程中,需要有一种方式交换握手请求信息,这就是代理的用途所在了。

代理的实现要满足如下两个要求:

  1. 它应该能够通过 WebSocket 使用对等节点的 ID 与其通信;
  2. 它需要维护已接入的对等节点的信息。

每个对等节点在接入 Zohaib 的 Web 应用时都会获得一个唯一 ID,并在收到唯一 ID 后将自己进行注册。

使用对等节点 ID 进行通信

对于第一点要求,Zohaib 尝试了两种方法。第一种方法是为对等节点 ID 和与它相关连的 Socket 维护一个通用的映射。当需要同某个对等节点通信时,就使用对等节点的唯一 ID 检索出 Socket,并将消息推送给它。第二种方法是每个对等节点连接到一个唯一的主题,当需要向那个节点发送消息时,只需将消息广播到那个主题。有且只有一个对等节点会注册到那个主题,所以,其它对等节点不会获得这条消息。Zohaib 采用了第二种方法,因为第一种方法需要一个Elixir 代理存储映射。这样,每个通信请求都需要向该代理发送消息获取 Socket,它会成为瓶颈,而且不可扩展。而在第二种方法中,当向 WebSocket 注册时,节点会连接到唯一的主题(比如peer:20dd48ca-fdcf-41c9-9a3b-f192f77650f9)。这时,就可以使用函数ApplicationName.Endpoint.broadcast(topic, event, payload)向该主题发送消息。

对等节点信息维护

对于第二点要求,Zohaib 同样尝试了两种方法。第一种方法是同上文描述的一样,保存一个通用的映射。但当 Socket 连接关闭或 Socket 进程结束时,代理需要自己维护映射关系。第二种方法是使用 Elixir/Erlang 的全局名称注册功能。这种方式可以将一个全局名称注册到对应的 PID。当进程崩溃或终止时,该名称会自动解除注册。而且,这种方法可以扩展到多个节点。因此,Zohaib 采用了第二种方法。当节点注册时,它会启动一个简单的 GenServer,后者维护与节点相关的信息,并为节点分配一个像peer_state:<UUID>这样的全局名称。该进程会链接到 Socket 进程,如果 Socket 关闭或崩溃,那么该进程也会终止并解除注册。在这种方式中,代理不需要自己维护映射。

交换 WinRTC 请求

有了代理,就可以交换握手请求了。例如,发送者将一个像http://epicshare.zohaib.me/?peer_id=20dd48ca-fdcf-41c9-9a3b-f192f77650f9这样 URL 的分享,其中包含对等节点的 ID。接收者打开该 URL 后会获得一个唯一 ID,并像上文描述的那样将自己注册到代理。那样,两个对等节点就都通过 Web Socket 连接到代理了。下一步,它们就可以发送和接收握手请求了,过程如下所示:

感兴趣的读者可以试用Zohaib 的程序,或者下载源代码

另外,从Hacker News 网友的讨论中得知,使用 WebRTC 在两个浏览器之间交换请求 / 应答可以不借助任何服务器


感谢魏星对本文的审校。

给 InfoQ 中文站投稿或者参与内容翻译工作,请邮件至editors@cn.infoq.com。也欢迎大家通过新浪微博(@InfoQ@丁晓昀),微信(微信号:InfoQChina)关注我们,并与我们的编辑和其他读者朋友交流(欢迎加入 InfoQ 读者交流群)。

语言 & 开发架构