WebRTC 实例之语音演示(一):服务器端
WebRTC 实例之语音演示(一):服务器端
在本章中,我们将构建一个客户端应用程序,允许两个用户使用webrtc音频流进行通信。我们的应用将两页:一个用于登录,另一个用于对另一个用户进行音频调用。
[login_and_audio_call_page]
这两个页面将使用div+css布局方式,大多数输入是通过简单事件处理程序完成的。
信令服务器
要创建WebRTC客户端,必须能够传输消息而不使用WebRTC对等连接,我们将使用Socket技术。现在我们开始使用websocket库。创建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库,然后在端口号9090上创建一个socket服务器。接下来,我们监听连接事件,当用户跟服务器建立websocket连接时将执行这段代码。然后我们监听用户发送的消息。最后,我们向连接的用户发送响应"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对象。更改我们的文件的顶部:
//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 = {};我们将为来自客户端的每条消息添加一个类型字段。例如,如果用户想要登录,他会发送登录类型消息。让我们定义它:
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;
}
});如果用户使用登录类型发送消息,则进行下面的操作:
检查是否有人已使用此用户名登录。
如果是,那么告诉用户他没有成功登录。
如果没有人使用此用户名,我们将用户名作为连接对象的key。
如果命令不被识别,我们发送错误。
下面的代码是用于将消息发送到连接的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;要允许我们的用户断开与其他用户的连接,我们应该实现挂起函数。它还将告诉服务器删除所有用户引用。添加leave处理程序:
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;还将向其他用户发送leave事件,以便断开对等连接。我们还应该处理用户从信令服务器断开连接时的情况。让我们修改我们的关闭处理程序:
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));
}