biaobro 2015-08-20
WebRTC(Web实时通信)的创建主要是为了视频和音频通信,但它也有在两个浏览器之间传递二进制数据的API。这为创建更多的点对点Web应用程序带来了许多机会,而且已经有许多有趣的应用程序是使用它创建的,如 WebTorrent 、 UberConference 。
Zohaib Rauf 是一名软件工程师,他正在学习 Elixir语言 。为了更好地理解WebRTC,他使用它创建了一个P2P文件分享应用程序,目标是实现点对点的文件分享,而不需要任何中间人。文件发送者添加文件,并将唯一URL分享给接收者。接收者访问唯一URL就可以看到分享的文件并下载,如下图所示:
在该应用程序中,Zohaib使用 Phoenix框架 实现了一个用于初次握手的代理。近日,他发表了一篇 博文 ,简要介绍了该代理的设计决策过程。
为了创建点对点的连接,发起者会创建一个包含其网络信息和其它媒体相关信息的握手请求。接收者在收到请求后会创建应答和它自己的申请并发送回发起 者。发起者接收到应答后完成初次握手。至此,发起者和接收者就可以进行点对点通信了。在这个过程中,需要有一种方式交换握手请求信息,这就是代理的用途所 在了。
代理的实现要满足如下两个要求:
每个对等节点在接入Zohaib的Web应用时都会获得一个唯一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关闭或崩溃,那么该进程也会终止并解除注册。在这种方式中,代理不需要自己维护映射。
有了代理,就可以交换握手请求了。例如,发送者将一个像 http://epicshare.zohaib.me/?peer_id=20dd48ca-fdcf-41c9-9a3b-f192f77650f9
这样URL的分享,其中包含对等节点的ID。接收者打开该URL后会获得一个唯一ID,并像上文描述的那样将自己注册到代理。那样,两个对等节点就都通过Web Socket连接到代理了。下一步,它们就可以发送和接收握手请求了,过程如下所示:
感兴趣的读者可以 试用 Zohaib的程序,或者下载 源代码 。
另外,从 Hacker News网友的讨论 中得知, 使用WebRTC在两个浏览器之间交换请求/应答可以不借助任何服务器 。
部署WebRTC 或 SIP p2p 方案时经常会遇到p2p 无法穿透的环境,这时就是TunServer 的用武之地了。添加完成后,就可以在webrtc 里面使用stun 和tun server 了。