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在兩個浏覽器之間交換請求/應答可以不借助任何服務器 。