开发学院,分享开发教程和最新动态

WebRTC 实例之视频通信:(一)服务器端代码

WebRTC 视频Demo

  在本节中,我们将构建一个客户端应用程序,允许两个用户在不同的设备上使用webrtc通信。我们的应用将有两页:一个用于登录,另一个用于通讯。

login_page.jpg

  这两个页面将包含在div标签中。大多数输入是通过简单事件处理程序完成的。

calling_page.jpg

信令服务器

  要创建webRTC连接客户端,必须能够传输消息而不使用webRTC对等连接。这是我们将使用html5 WebSockets-用于建立浏览器和服务器之间的双向套接字连接。现在让我们开始使用WebSockets创建server.js文件并插入以下代码:

  //require our websocket library 

var WebSocketServer = require('ws').Server; 


//creating a websocket server at port 9090 

var wss = new WebSocketServer({port: 9090});

  

//when a user connects to our sever 

wss.on('connection', function(connection) { 

   console.log("user connected"); 

   //when server gets a message from a connected user 

   connection.on('message', function(message) { 

      console.log("Got message from a user:", message); 

   }); 

   connection.send("Hello from server");

});

  第一行中我们引用已经安装的websocket库,如果没有安装请先进行安装(npm install ws)。然后在端口号9090上创建一个socket服务器。接下来,我们监听连接事件。当用户对服务器建立websocket连接时,将执行connection方法。然后我们监听用户发送的消息。最后,我们向连接的用户发送响应“Hello from server”。

  在我们的信令服务器中,我们将为每个连接使用基于字符串的用户名,以便我们知道发送消息的信息。让我们更改我们的连接处理程序:connection.on('message', 

function(message) { 
   var data; 
   //accepting only JSON messages 
   try { 
      data = JSON.parse(message); 
   } catch (e) { 
      console.log("Invalid JSON"); 
      data = {}; 
   } 
});

  我们只接受JSON消息。接下来,我们需要将连接的用户存储在某个地方。我们将使用一个简单的JavaScript对象来保存用户信息。更改server.js的顶部:

//require our websocket library 
var WebSocketServer = require('ws').Server; 

//creating a websocket server at port 9090 
var wss = new WebSocketServer({port: 9090}); 

//all connected to the server users 
var users = {};//这里是新增加的变量

  我们将为来自客户端的消息添加一个类型字段。例如,如果用户想要登录,他会发送login

类型消息。让我们定义它:

connection.on('message', function(message) { 
   var data; 
   //accepting only JSON messages 
   try { 
      data = JSON.parse(message);
   } catch (e) { 
      console.log("Invalid JSON"); 
      data = {}; 
   } 
   //switching type of the user message 
   switch (data.type) { 
      //when a user tries to login 
      case "login": 
         console.log("User logged:", data.name); 
         //if anyone is logged in with this username then refuse 
         if(users[data.name]) { 
            sendTo(connection, { 
               type: "login", 
               success: false 
            }); 
         } else { 
            //save user connection on the server 
            users[data.name] = connection; 
            connection.name = data.name; 
            sendTo(connection, { 
               type: "login", 
               success: true 
            }); 
         } 
         break;
      default: 
         sendTo(connection, { 
            type: "error", 
            message: "Command no found: " + data.type 
         }); 
         break;
   }  
});

  如果用户使用登录类型发送消息,则:

  • 1.检查是否有人已使用此用户名登录

  • 2.如果是,那么告诉用户他没有成功登录

  • 3.如果没有人使用此用户名,我们将用户名作为连接对象的key。

  • 4.如果命令不被识别,我们发送错误。

  下面的代码是用于将消息发送到连接的helper函数。将它添加到server.js文件中:

function sendTo(connection, message) { 
   connection.send(JSON.stringify(message)); 
}

  当用户断开连接时,我们应该清理其连接。当关闭事件触发时,我们可以删除用户。将以下代码添加到连接处理程序:

connection.on("close", function() { 
   if(connection.name) { 
      delete users[connection.name]; 
   } 
});

  在成功登录后,用户希望跟其他用户建立链接的请求。他应该向另一个用户提供实现它的提议。添加提供处理程序:

case "offer": 
   //for ex. UserA wants to call UserB 
   console.log("Sending offer to: ", data.name);
   //if UserB exists then send him offer details 
   var conn = users[data.name]; 
   if(conn != null) { 
      //setting that UserA connected with UserB 
      connection.otherName = data.name; 
      sendTo(conn, { 
         type: "offer",
         offer: data.offer, 
         name: connection.name 
      }); 
   }  
   break;

  首先,我们得到我们想要调用的用户的连接。如果存在,我们就派他提供细节。我们还向连接对象添加othername。这是为了便于后来找到它的简单性。

  对响应的回答有一个类似的模式,我们在提供处理程序中使用。我们的服务器只通过所有消息作为对另一个用户的应答。在提供处理程序后添加以下代码case "answer": 

   console.log("Sending answer to: ", data.name); 
   //for ex. UserB answers UserA 
   var conn = users[data.name]; 
   if(conn != null) { 
      connection.otherName = data.name; 
      sendTo(conn, { 
         type: "answer", 
         answer: data.answer 
      }); 
   } 
   break;

  最后一部分是在用户之间处理ice候选人。我们使用相同的技术在用户之间传递消息。主要区别是,候选消息可能按任何顺序为每个用户执行多次。添加候选处理程序.

case "candidate": 
   console.log("Sending candidate to:",data.name); 
   var conn = users[data.name];
   if(conn != null) { 
      sendTo(conn, { 
         type: "candidate", 
         candidate: data.candidate 
      }); 
   } 
   break;

  要允许我们的用户断开与其他用户的连接,我们应该实现挂起函数。它还将告诉服务器删除所有用户引用。添加离开处理程序.

case "leave": 
   console.log("Disconnecting from", data.name); 
   var conn = users[data.name]; 
   conn.otherName = null; 
   //notify the other user so he can disconnect his peer connection 
   if(conn != null) { 
      sendTo(conn, { 
         type: "leave" 
      }); 
   } 
   break;

  还需要向其他用户发送离开事件,以便因此断开对等连接。我们还应该处理用户从信令服务器放弃连接时的情况。让我们修改我们的关闭处理程序.

connection.on("close", function() { 
   if(connection.name) { 
      delete users[connection.name]; 
      if(connection.otherName) { 
         console.log("Disconnecting from ", connection.otherName); 
         var conn = users[connection.otherName]; 
         conn.otherName = null;  
         if(conn != null) { 
            sendTo(conn, { 
               type: "leave" 
            });
         }
      } 
   } 
});

  以下是我们的信令服务器的全部代码:

//require our websocket library 
var WebSocketServer = require('ws').Server; 

//creating a websocket server at port 9090 
var wss = new WebSocketServer({port: 9090}); 

//all connected to the server users 
var users = {};
  
//when a user connects to our sever 
wss.on('connection', function(connection) {
  
   console.log("User connected");
   //when server gets a message from a connected user 
   connection.on('message', function(message) { 
      var data; 
      //accepting only JSON messages 
      try { 
         data = JSON.parse(message); 
      } catch (e) { 
         console.log("Invalid JSON"); 
         data = {}; 
      }
      //switching type of the user message 
      switch (data.type) { 
         //when a user tries to login
         case "login": 
            console.log("User logged", data.name); 
            //if anyone is logged in with this username then refuse 
            if(users[data.name]) { 
               sendTo(connection, { 
                  type: "login", 
                  success: false 
               }); 
            } else { 
               //save user connection on the server 
               users[data.name] = connection; 
               connection.name = data.name; 
               sendTo(connection, { 
                  type: "login", 
                  success: true 
               }); 
            } 
            break;
         case "offer": 
            //for ex. UserA wants to call UserB 
            console.log("Sending offer to: ", data.name);
            //if UserB exists then send him offer details 
            var conn = users[data.name]; 
            if(conn != null) { 
               //setting that UserA connected with UserB 
               connection.otherName = data.name; 
               sendTo(conn, { 
                  type: "offer", 
                  offer: data.offer, 
                  name: connection.name 
               }); 
            }
            break;
         case "answer": 
            console.log("Sending answer to: ", data.name); 
            //for ex. UserB answers UserA 
            var conn = users[data.name]; 
            if(conn != null) { 
               connection.otherName = data.name; 
               sendTo(conn, { 
                  type: "answer", 
                  answer: data.answer 
               }); 
            } 
            break; 
         case "candidate": 
            console.log("Sending candidate to:",data.name); 
            var conn = users[data.name];
            if(conn != null) { 
               sendTo(conn, { 
                  type: "candidate", 
                  candidate: data.candidate 
               }); 
            } 
            break;
         case "leave": 
            console.log("Disconnecting from", data.name); 
            var conn = users[data.name]; 
            conn.otherName = null; 
            //notify the other user so he can disconnect his peer connection 
            if(conn != null) {
               sendTo(conn, { 
                  type: "leave" 
              }); 
            }
            break;
         default: 
            sendTo(connection, { 
               type: "error", 
               message: "Command not found: " + data.type 
            }); 
            break; 
      }
   }); 
   //when user exits, for example closes a browser window 
   //this may help if we are still in "offer","answer" or "candidate" state 
   connection.on("close", function() { 
      if(connection.name) { 
         delete users[connection.name]; 
         if(connection.otherName) { 
            console.log("Disconnecting from ", connection.otherName); 
            var conn = users[connection.otherName]; 
            conn.otherName = null;
            if(conn != null) { 
               sendTo(conn, { 
                  type: "leave" 
               }); 
            }
         } 
      }
   });  
   connection.send("Hello world");  
});
  
function sendTo(connection, message) { 
   connection.send(JSON.stringify(message)); 
}