{%hackmd SyQbZi4Mr %} 用 WebRTC 建立半分散式網路 === #### By [PastLeo](https://pastleo.me), a developer from [5xruby](https://5xruby.tw) --- ## WebRTC 是什麼? ### Web Real-Time Communication https://5xruby.tw/posts/webrtc/ --- ## 跟 WebSocket 的差別是? | WebSocket | WebRTC | |---------------|----------------| | tcp (http) | udp by default | | client-server | peer-to-peer | --- ### Web Real-Time Communication <img class='svg' src='https://static.pastleo.me/assets/1908141135.svg'> * Video/Audio conferencing * Data channel --- ### Web Real-Time Communication <img class='svg' src='https://static.pastleo.me/assets/1908141135.svg'> * ~~Video/Audio conferencing~~ * Data channel --- ## WebRTC 連線建立 <img class='svg' src='https://static.pastleo.me/assets/190817225726.svg'> --- ## WebRTC 連線建立 1. Signaling 2. Interactive Connectivity Establishment,ICE --- ### Signaling <img class='svg' src='https://static.pastleo.me/assets/190817225743.svg'> --- ### Signaling <img class='svg' src='https://static.pastleo.me/assets/190817225835.svg'> --- ### Signaling <img class='svg' src='https://static.pastleo.me/assets/190817225908.svg'> --- ### Signaling <img class='svg' src='https://static.pastleo.me/assets/190817225948.svg'> --- ### Signaling done <img class='svg' src='https://static.pastleo.me/assets/190817230015.svg'> --- ## ICE <img class='svg' src='https://static.pastleo.me/assets/190817230032.svg'> --- ## WebRTC connection ready! <img class='svg' src='https://static.pastleo.me/assets/1908141135.svg'> `iceConnectionState: "completed"` --- ## 如何傳送 signaling 以及 ICE? ```json {"type":"offer","sdp":"v=0\r\no=- 808320459780741931 2 IN IP4 127.0.0.1\r\ns=-\r\nt=0 0\r\na=group:BUNDLE 0\r\na=msid-semantic: WMS\r\nm=application 9 UDP/DTLS/SCTP webrtc-datachannel\r\nc=IN IP4 0.0.0.0\r\na=ice-ufrag:sPNA\r\na=ice-pwd:TW1+woGkN85ZdM0J9s1rDb/g\r\na=ice-options:trickle\r\na=fingerprint:sha-256 61:2F:11:89:BD:82:81:48:54:CD:6C:CE:31:87:F9:40:36:CB:BA:CA:BB:DA:C8:9C:40:38:9E:AD:3B:EC:69:0F\r\na=setup:actpass\r\na=mid:0\r\na=sctp-port:5000\r\na=max-message-size:262144\r\n"} ``` ```json {"type":"answer","sdp":"v=0\r\no=- 1665667491633561454 2 IN IP4 127.0.0.1\r\ns=-\r\nt=0 0\r\na=group:BUNDLE 0\r\na=msid-semantic: WMS\r\nm=application 9 UDP/DTLS/SCTP webrtc-datachannel\r\nc=IN IP4 0.0.0.0\r\nb=AS:30\r\na=ice-ufrag:mAPp\r\na=ice-pwd:LfCCIhM35ejCm+bC/D7I263C\r\na=ice-options:trickle\r\na=fingerprint:sha-256 4E:58:CB:0A:9F:EF:78:BA:C0:A3:AB:32:14:14:C0:69:1B:A9:18:AC:04:27:6F:4E:1B:90:35:4D:40:07:41:63\r\na=setup:active\r\na=mid:0\r\na=sctp-port:5000\r\na=max-message-size:262144\r\n"} ``` ```json {"candidate":"candidate:842163049 1 udp 1677729535 223.137.53.226 23733 typ srflx raddr 10.203.94.37 rport 38103 generation 0 ufrag HvAq network-cost 999","sdpMid":"0","sdpMLineIndex":0} ``` #### <span style="color: transparent;">_</span> --- ## 如何傳送 signaling 以及 ICE? ```json {"type":"offer","sdp":"v=0\r\no=- 808320459780741931 2 IN IP4 127.0.0.1\r\ns=-\r\nt=0 0\r\na=group:BUNDLE 0\r\na=msid-semantic: WMS\r\nm=application 9 UDP/DTLS/SCTP webrtc-datachannel\r\nc=IN IP4 0.0.0.0\r\na=ice-ufrag:sPNA\r\na=ice-pwd:TW1+woGkN85ZdM0J9s1rDb/g\r\na=ice-options:trickle\r\na=fingerprint:sha-256 61:2F:11:89:BD:82:81:48:54:CD:6C:CE:31:87:F9:40:36:CB:BA:CA:BB:DA:C8:9C:40:38:9E:AD:3B:EC:69:0F\r\na=setup:actpass\r\na=mid:0\r\na=sctp-port:5000\r\na=max-message-size:262144\r\n"} ``` ```json {"type":"answer","sdp":"v=0\r\no=- 1665667491633561454 2 IN IP4 127.0.0.1\r\ns=-\r\nt=0 0\r\na=group:BUNDLE 0\r\na=msid-semantic: WMS\r\nm=application 9 UDP/DTLS/SCTP webrtc-datachannel\r\nc=IN IP4 0.0.0.0\r\nb=AS:30\r\na=ice-ufrag:mAPp\r\na=ice-pwd:LfCCIhM35ejCm+bC/D7I263C\r\na=ice-options:trickle\r\na=fingerprint:sha-256 4E:58:CB:0A:9F:EF:78:BA:C0:A3:AB:32:14:14:C0:69:1B:A9:18:AC:04:27:6F:4E:1B:90:35:4D:40:07:41:63\r\na=setup:active\r\na=mid:0\r\na=sctp-port:5000\r\na=max-message-size:262144\r\n"} ``` ```json {"candidate":"candidate:842163049 1 udp 1677729535 223.137.53.226 23733 typ srflx raddr 10.203.94.37 rport 38103 generation 0 ufrag HvAq network-cost 999","sdpMid":"0","sdpMLineIndex":0} ``` #### ...開發者要自己想辦法 --- #### My "hello world" to WebRTC ![chatroom screenshot](https://i.imgur.com/CgzqBtG.png) 用 [Elixir](https://elixir-lang.org/), [Phoenix](https://phoenixframework.org/) 寫的 WebSocket server --- ## 等等,網路問題怎麼克服的? #### 被 NAT 隔開還能用嗎? --- ## STUN & TURN 1. [Session Traversal Utilities for NAT, STUN](https://zh.wikipedia.org/wiki/STUN), 用 [UDP hole punching](https://en.wikipedia.org/wiki/UDP_hole_punching) 克服 NAT 的限制 2. [Traversal Using Relay NAT, TURN](https://zh.wikipedia.org/wiki/TURN) ```javascript new RTCPeerConnection({ iceServers: [ {urls: 'stun:stun.l.google.com:19302'} ] }); ``` --- ## [caniuse?](https://caniuse.com/#search=webrtc) * Firefox, Chrome: supported on 2013 and 2012 * safari / iOS safari: supported on 2017/9 (iOS 11) * Edge: no RTCDataChannel, but new chromium-based Edge is on the way * IE: sorry nope --- ### Multiplayer game using WebRTC #### Bazaar ![](https://static.pastleo.me/assets/bazaar-coscup-demo.gif) --- ## Bazaar client 之間的連線方法 #### 用 websocket 跟伺服器連線,接著與所有玩家連線 --- ### Bazaar client 之間的連線方法 <img class='svg' src='https://static.pastleo.me/assets/190817230331.svg'> --- ### Bazaar client 之間的連線方法 <img class='svg' src='https://static.pastleo.me/assets/190817231357.svg'> --- ### Bazaar client 之間的連線方法 <img class='svg' src='https://static.pastleo.me/assets/190817231421.svg'> --- ### Bazaar client 之間的連線方法 <img class='svg' src='https://static.pastleo.me/assets/190817231441.svg'> --- ### Bazaar client 之間的連線方法 <img class='svg' src='https://static.pastleo.me/assets/190817231856.svg'> --- ### Bazaar client 之間的連線方法 <img class='svg' src='https://static.pastleo.me/assets/190817231922.svg'> --- ### Bazaar client 之間的連線方法 <img class='svg' src='https://static.pastleo.me/assets/190817231942.svg'> --- ### Bazaar client 之間的連線方法 <img class='svg' src='https://static.pastleo.me/assets/190817232006.svg'> --- ### Bazaar client 之間的連線方法 <img class='svg' src='https://static.pastleo.me/assets/190817232026.svg'> --- ### Bazaar client 之間的連線方法 <img class='svg' src='https://static.pastleo.me/assets/190817232046.svg'> --- ## 想說 server 可以是一個 NPC... #### 把 server 當成 peer 看待? --- ### rtc 與 ws 共用的 interface <img class='svg' src='https://static.pastleo.me/assets/190815150840.svg'> --- ### rtc 與 ws 共用的 interface <div class="grid"> <div> <img class='svg' src='https://static.pastleo.me/assets/190815154445.svg'> </div> <div class="column-1"> #### <span style="color: transparent;">_</span> * startLink(...args) * send(...message) * on('receive', callback) </div> </div> --- ### rtc 與 ws 共用的 interface <div class="grid"> <div> <img class='svg' src='https://static.pastleo.me/assets/190815154614.svg'> </div> <div class="column-1"> #### <span style="color: transparent;">_</span> * connect(peer, <span style="color: red">viaPeer</span>) * send(peer, ...message) * on('receive', callback) </div> </div> --- ### rtc 與 ws 共用的 interface <div class="grid"> <div> <img class='svg' src='https://static.pastleo.me/assets/190815154614.svg'> </div> <div class="column-1"> * connect(peer, <span style="color: red">viaPeer</span>) * send(peer, ...message) * on('receive', callback) <img class='svg' src='https://static.pastleo.me/assets/190815153930.svg'> </div> </div> --- ### 透過 browser 進行 WebRTC 連線建立 #### 會是什麼樣子? --- ### 透過 browser 進行 WebRTC 連線建立 <img class='svg' src='https://static.pastleo.me/assets/190817233057.svg'> --- ### 透過 browser 進行 WebRTC 連線建立 <img class='svg' src='https://static.pastleo.me/assets/190817233229.svg'> --- ### 透過 browser 進行 WebRTC 連線建立 <img class='svg' src='https://static.pastleo.me/assets/190817233318.svg'> --- ### 透過 browser 進行 WebRTC 連線建立 <img class='svg' src='https://static.pastleo.me/assets/190817233343.svg'> --- ### 透過 browser 進行 WebRTC 連線建立 <img class='svg' src='https://static.pastleo.me/assets/190817233405.svg'> --- ### 透過 browser 進行 WebRTC 連線建立 <img class='svg' src='https://static.pastleo.me/assets/190817233442.svg'> --- ### 透過 browser 進行 WebRTC 連線建立 <img class='svg' src='https://static.pastleo.me/assets/190817233504.svg'> --- ### 透過 browser 進行 WebRTC 連線建立 * 第一個連線一定是 WebSocket * 加入網路之後可以關閉 WebSocket 連線 --- ## 半分散式網路 ### unnamed-network? --- ### unnamed-network? * 改成 general WebSocket * 實做 nodejs 版本 Websocket server * 接下來簡稱 wss * wss 是個 client, 網路中同時可以有複數個 * 群組功能 * 一個 client 可以同時加入多個 group * 群組廣播 --- ### unnamed-network 架構 <img class='svg' src='https://static.pastleo.me/assets/190815183330.svg'> --- #### unnamed-network 架構 <div class="grid"> <div> <img class='svg' src='https://static.pastleo.me/assets/190815183453.svg'> </div> <div class="column-1"> #### <span style="color: transparent;">_</span> * startLink(args) * send(...message) * on('receive', callback) </div> </div> --- #### unnamed-network 架構 <div class="grid"> <div> <img class='svg' src='https://static.pastleo.me/assets/190815183813.svg'> </div> <div class="column-1"> #### <span style="color: transparent;">_</span> * connect(peer, opts) * on(‘new’, callback) </div> </div> --- #### unnamed-network 架構 <div class="grid"> <div> <img class='svg' src='https://static.pastleo.me/assets/190815184426.svg'> </div> <div class="column-1"> * connect(peer, opts) * opts: { viaPeer } * on('ready', callback) * send(peer, ...message) * on('receive', callback) </div> </div> --- #### unnamed-network 架構 #### connManager 連線建立策略 | 左下連右上 | wss | browser | |----------|--------|--------------| | wss | ws 直連 | 邀請連 ws (!) | | browser | ws 直連 | WebRTC (!) | (!): 需要 viaPeer --- #### unnamed-network 架構 <div class="grid"> <div> <img class='svg' src='https://static.pastleo.me/assets/190815190347.svg'> </div> <div class="column-1"> * join(group) * leave(group) * broadcast(group, term, payload) * on(term, callback) </div> </div> --- ### unnamed-network 目前設計 #### 初始化 * 有一個 known list 紀錄 wss * 所有 client 都會加入 `"/"` 群組 --- ### unnamed-network 目前設計 #### 加入群組 * 對 `"/"` 請求 `route-group` * max hops: 10 * 加入群組時發現找不到人就是建立新群組 --- ### unnamed-network 目前設計 #### 初始化 & 加入群組 <img class='svg' src='https://static.pastleo.me/assets/190816015615.svg'> --- ### unnamed-network 目前設計 #### 初始化 & 加入群組 <img class='svg' src='https://static.pastleo.me/assets/190816015700.svg'> --- ### unnamed-network 目前設計 #### 初始化 & 加入群組 <img class='svg' src='https://static.pastleo.me/assets/190816015721.svg'> --- ### unnamed-network 目前設計 #### 初始化 & 加入群組 <img class='svg' src='https://static.pastleo.me/assets/190816020442.svg'> --- ### unnamed-network 目前設計 #### 初始化 & 加入群組 <img class='svg' src='https://static.pastleo.me/assets/190816020531.svg'> --- ### unnamed-network 目前設計 #### 初始化 & 加入群組 <img class='svg' src='https://static.pastleo.me/assets/190816020608.svg'> --- ### unnamed-network 目前設計 #### 初始化 & 加入群組 <img class='svg' src='https://static.pastleo.me/assets/190816015905.svg'> --- ### unnamed-network 目前設計 #### 初始化 & 加入群組 <img class='svg' src='https://static.pastleo.me/assets/190816015947.svg'> --- ### unnamed-network 目前設計 #### 初始化 & 加入群組 <img class='svg' src='https://static.pastleo.me/assets/190816020024.svg'> --- ### unnamed-network 目前設計 #### neighbors 數量維護 * 低水位: 3 * 低於這個數量會嘗試尋找更多 neighbor * 高水位: 6 * 高於這個數量會去減少 neighbor 數量 * 滿水位: 10 * 不接受更多連線 * wss 的滿水位: 100 --- ### unnamed-network 目前設計 #### neighbors 數量維護 <img class='svg' src='https://static.pastleo.me/assets/190816021651.svg'> --- ### unnamed-network 目前設計 #### neighbors 數量維護 <img class='svg' src='https://static.pastleo.me/assets/190816021732.svg'> --- ### unnamed-network 目前設計 #### neighbors 數量維護 <img class='svg' src='https://static.pastleo.me/assets/190816021750.svg'> --- ### unnamed-network 目前設計 #### neighbors 數量維護 <img class='svg' src='https://static.pastleo.me/assets/190816034139.svg'> --- ### unnamed-network 目前設計 #### neighbors 數量維護 <img class='svg' src='https://static.pastleo.me/assets/190816021825.svg'> --- ### unnamed-network 目前設計 ### 群組廣播 * 送出訊息時,附加上隨機的 `msgId` * 如果已經看過這個 `msgId` 就 drop 掉訊息 --- ### unnamed-network 目前設計 #### 廣播/群組請求 <img class='svg' src='https://static.pastleo.me/assets/190816030004.svg'> --- ### unnamed-network 目前設計 #### 廣播/群組請求 <img class='svg' src='https://static.pastleo.me/assets/190816030031.svg'> --- ### unnamed-network 目前設計 #### 廣播/群組請求 <img class='svg' src='https://static.pastleo.me/assets/190816030059.svg'> --- #### unnamed-network ### 有 neighbor 離開或是斷線時 ## ... --- #### unnamed-network ### 有 neighbor 離開或是斷線時 ## 目前還沒有好方法避免孤島產生 --- ### 要讓這個 network 能用的話需要... * 能跨多個 node 進行連線以增加多 hop 連線效率 * 分散式,並且同時可以避免孤島產生的機制 * [Distributed hash table](https://zh.wikipedia.org/wiki/%E5%88%86%E6%95%A3%E5%BC%8F%E9%9B%9C%E6%B9%8A%E8%A1%A8) 可能是解法之一 * 解決安全性問題 * 釋出為一個連線框架 --- ### DEMO [link](https://static.pastleo.me/unnamed-network-coscup2019/) ![](https://static.pastleo.me/assets/un-coscup-demo.gif) --- ### 期望這個 network 可以... * 使得瀏覽器不用安裝任何 plugins / add-ons 就可以使用分散式應用程式 * 降低架設 wss 的技術門檻,人人都可以成為網路進入點 * 讓不同應用程式可以共用這個 network 來建構多人遊戲、共筆系統等 --- ### Git Repos #### https://github.com/pastleo/unnamed-network 另外兩個前面提到的專案: * https://github.com/pastleo/webrtc-phx * https://github.com/pastleo/bazaar --- ## Thank you! ### link to this slides ### https://ppt.cc/fG58Hx ### Any questions? <style> .reveal section img.svg { border: none; box-shadow: none; } .reveal section table { color: #004a1b; } .reveal section table th, .reveal section table td { border: #004a1b solid 1px; border-bottom: #004a1b solid 1px; } .reveal section table tbody tr:last-child th, .reveal section table tbody tr:last-child td { border-bottom: #004a1b solid 1px; } .grid { display: flex; flex-direction: row; } .grid .column-1 { flex: 1; } </style>
{"metaMigratedAt":"2023-06-15T10:11:25.584Z","metaMigratedFrom":"YAML","title":"用 WebRTC 建立半分散式網路","breaks":true,"slideOptions":"{\"spotlight\":{\"enabled\":true,\"size\":80,\"presentingCursor\":\"default\",\"toggleSpotlightOnMouseDown\":false,\"spotlightOnKeyPressAndHold\":90,\"initialPresentationMode\":true,\"disablingUserSelect\":false,\"fadeInAndOut\":500},\"allottedMinutes\":5}","contributors":"[{\"id\":\"0eb274f7-a3a4-4c8e-b0d4-e5c08eaf9e72\",\"add\":14363,\"del\":70}]"}
    2168 views