- WebRTC音视频实时互动技术:原理、实战与源码分析
- 李超编著
- 1428字
- 2021-08-06 14:49:37
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节中给出。
其他几种情况与前面介绍的两种情况是类似的,都是收到服务端的信令后回调对应的函数,在函数中变更状态,然后做相应的逻辑处理,这里就不再赘述了。