- WebRTC音視頻實時互動技術:原理、實戰與源碼分析
- 李超編著
- 1428字
- 2021-08-06 14:49:36
5.6 信令狀態機
在開始介紹端到端通信之前,必須先實現客戶端的信令系統,讓客戶端與信令服務器可以互通,從而為端到端交換信息做好準備。那么客戶端的信令系統該如何實現呢?
最簡單的辦法是通過狀態機實現,其基本原理如下:每次發送/接收一個信令后,客戶端都根據狀態機當前的狀態做相應的邏輯處理。比如當客戶端剛啟動時,其處于Init狀態,在此狀態下,用戶只能向服務端發送join消息,待服務端返回joined消息后,客戶端的狀態機發生了變化,變成了joined狀態后,才能開展后續工作。客戶端的狀態機如圖5.1所示。

圖5.1 信令狀態機
從圖中可以發現,客戶端的狀態機共有4種狀態,分別是Init、joined、joined_unbind以及joined_conn。下面詳述一下各種狀態之間是如何變化的。
·客戶端剛啟動時,其初始狀態為Init。
·在Init狀態下,用戶只能向服務器發送join消息;服務端收到join消息后,會返回joined消息;如果客戶端能收到joined消息,則說明用戶已經成功加入房間中,此時客戶端狀態更新為joined。
·在joined狀態下,客戶端有多種選擇,根據不同的選擇可以切換到不同的狀態:
–如果用戶離開房間,客戶端又回到了初始狀態,即Init狀態。
–如果客戶端收到第二個用戶加入的消息(即other_joined消息),則切換到join_conn狀態。在這種狀態下,兩個用戶就可以進行通信了。
–如果客戶端收到第二個用戶離開的消息(即bye消息),則需要將其狀態切換到join_unbind。實際上,join_unbind狀態與joined狀態基本是一致的,不過可以通過這兩種不同的狀態值判斷出用戶之前的狀態。
·如果客戶端處于join_conn狀態,當它收到bye消息時,會變成joined_unbind狀態。
·如果客戶端是joined_unbind狀態,當它收到other_join消息時,會變成join_conn狀態。
接下來看一下客戶端狀態機是如何實現的,參見代碼5.9。
代碼5.9 客戶端狀態機
1 var state = init; 2 3 // 連接信令服務器并根據信令更新狀態機 4 function conn(){ 5 6 // 建立socket.io 連接 7 socket = io.connect (); 8 9 // 收到joined 消息 10 socket.on('joined ', (roomid , id) => 11 state = 'joined '; // 變更狀態 12 … 13 // 創建連接 14 createPeerConnection (); 15 bindTracks (); 16 … 17 }); 18 19 // 收到otherjoin 消息 20 socket.on('otherjoin ', (roomid) => { 21 … 22 state = 'joined_conn '; // 更改狀態 23 call(); 24 … 25 }); 26 27 // 收到full 消息 28 socket.on('full', (roomid , id) => { 29 … 30 hangup (); 31 socket.disconnect (); // 關閉連接 32 state = 'init'; // 回到初始化狀態 33 … 34 }); 35 36 // 收到用戶離開的消息 37 socket.on('left', (roomid , id) => { 38 … 39 hangup (); 40 socket.disconnect (); 41 state='init' // 回到初始化狀態 42 … 43 }); 44 45 socket.on('bye', (room , id) => { 46 … 47 state = 'joined_unbind '; 48 hangup (); 49 … 50 }); 51 52 … 53 // 向服務端發送join 消息 54 roomid = getQueryVariable('room'); 55 socket.emit('join', roomid); 56 57 } 58 59 … 60 conn(); // 與信令服務器建立連接 61 …
在代碼5.9中,首先執行第一行代碼,將狀態機的狀態初始化為init;之后調用conn()函數,讓客戶端與信令服務器建立連接。而在conn()內部做了三件事:一是調用socket.io的connect()方法與信令服務器建立連接;二是向socket.io注冊5個回調函數,分別對應5個信令消息,即joined、otherjoin、full、left以及bye消息,以便收到不同的消息時做不同的邏輯處理;三是向信令服務器發送join消息。至此客戶端的運轉就由信令驅動起來了。
當客戶端收到服務端返回的joined消息后,會在回調之前注冊到socket.io中的回調函數,因此上面代碼的第10~17行會被執行。在這段代碼中,客戶端首先變更自己當前的狀態為joined,然后創建RTCPeerConnection(關于RTCPeerConnection的內容將會在5.7.1節詳細介紹)對象,最后將采集到的音視頻流綁定到之前創建好的RTCPeerConnection對象上。
當第二個用戶上線后,第一個用戶會收到服務端發來的otherjoin消息。上面代碼中的第20~25行會被執行。在這幾行代碼中,也是先變更客戶端狀態為joined_conn,然后調用call()函數。call()函數實現的是媒體協商,有關媒體協商相關的內容會在5.7.3節再做介紹,相關的代碼也在5.7.3節中給出。
其他幾種情況與前面介紹的兩種情況是類似的,都是收到服務端的信令后回調對應的函數,在函數中變更狀態,然后做相應的邏輯處理,這里就不再贅述了。
- Data Visualization with D3 4.x Cookbook(Second Edition)
- Learning NServiceBus(Second Edition)
- Drupal 8 Blueprints
- Maven Build Customization
- INSTANT Sencha Touch
- Learning Laravel 4 Application Development
- Web程序設計(第二版)
- Mastering KnockoutJS
- C# 8.0核心技術指南(原書第8版)
- ServiceNow:Building Powerful Workflows
- 微課學人工智能Python編程
- Python趣味編程與精彩實例
- C++程序設計
- Spring Boot從入門到實戰
- PHP程序設計高級教程