In this tutorial we are going to create a basic signalling server using javascript
and nodejs
. It will be able to connect two users together.
Assume we have 2 users UserA and UserB the scheme of our server will be:
1. Each user will start by registering themselves on the server(simple string login).
2. Now UserA can call UserB. UserA makes an offer with the user identifier(UserB) he wishes to call.
3. The other user(User B) should answer.
4. In the end ice candidates are sent between users so they can make a connection.
Remember that there are no rules implementing a signalling server.
First of all you should install node.js on your computer. To test it create an index.js
file and put the following code:
1 |
console.log("node testing"); |
Then run it using node index.js
. You might see the output phrase in the console.
We are going to use WebSockets – a bidirectional socket connection between a web browser and a web server. Run npm install ws
to install WebSockets library for node.js.
Let’s start using it. Insert the following code in an index.js
file:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
//require our websocket library var WebSocketServer = require('ws').Server; //creating a websocket server at port 9090 var wss = new WebSocketServer({port: 9090}); 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:", message); }); connection.send("Hello world"); }); |
To test our server install wscat
. Run npm install -g wscat
. This will install wscat
globally so you will be able to access it through a command line.
Run our server in one console window. Then open a new console window and run wscat -c ws://localhost:9090
. Then try to send a message to the server.
Let’s add a simple string based auth. Modify index.js
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 |
//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("Error parsing JSON"); data = {}; } //switching type of the user message switch (data.type){ //when a user tries to login case "login": console.log("User logged as", data.name); //if anyone has already logged with this username 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: "Unrecognized command: " + data.type }); break; } }); //when user exits connection.on("close", function(){ if(connection.name){ delete users[connection.name]; } }); connection.send("Hello world"); }); function sendTo(conn, message){ conn.send(JSON.stringify(message)); } |
Now connect to our server and try to login:
run wscat -c ws://localhost:9090
run {"type":"login","name":"UserA"}
when connected
Let’s implement the offer
handler, which is called when one user want to call another:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
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; |
The answer
handler is called when user answers to someone’s offer:
1 2 3 4 5 6 7 8 9 10 11 12 |
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; |
The final part is handling ICE candidates between users. Remember that candidate messages might happen multiple times between users. Add candidate
handler:
1 2 3 4 5 6 7 8 9 10 11 |
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; |
A good feature is implementing the leave
handler which will allow our users to disconnect from another user and notify our server to disconnect any user references. Add the leave
handler:
1 2 3 4 5 6 7 8 9 10 11 12 |
case "leave": console.log("Disconnecting user 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; |
To handle a case when a user drops a connection we need to modify the close
handler:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
//when user exits, for example closes a browser window //this may help if we are still in "offer","answer" or "canidate" state connection.on("close", function(){ if(connection.name){ delete users[connection.name]; if(connection.otherName){ console.log("Disconnecting user from ", connection.otherName); var conn = users[connection.otherName]; conn.otherName = null; if(conn != null){ sendTo(conn, { type: "leave" }); } } } }); |
The entire code of our signalling server:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 |
//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("Error parsing JSON"); data = {}; } //switching type of the user message switch (data.type){ //when a user tries to login case "login": console.log("User logged as", data.name); //if anyone has already logged with this username 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 user 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: "Unrecognized command: " + data.type }); break; } }); //when user exits, for example closes a browser window //this may help if we are still in "offer","answer" or "canidate" state connection.on("close", function(){ if(connection.name){ delete users[connection.name]; if(connection.otherName){ console.log("Disconnecting user from ", connection.otherName); var conn = users[connection.otherName]; conn.otherName = null; if(conn != null){ sendTo(conn, { type: "leave" }); } } } }); connection.send("Hello world"); }); //when server is ready to accept WebSocket connections wss.on('listening', function(){ console.log("Server started..."); }); function sendTo(conn, message){ conn.send(JSON.stringify(message)); } |
In the real world, signalling is not defined by the WebRTC specification. So there are a few glitches like complex firewall systems or VPN(Virtual Private Network) when you fall back on other technologies such as HTTP instead of WebSockets. I also advise you to check XMPP and SIP protocols which give a lot of power to any typical WebRTC app and SIP-based phone devices integration.
That’s all for today 🙂
Pingback: Comprehensive guide to WebRTC. Part 5/5. - ryzhak.com
Писменно сообщаем Вам о том что принято решение выплатить Вам денежные средства. Рекомендуем прямо сейчас не откладывая пройти шаги перейдя по ссылке на основную страницу нашего сервиса в срок до 2 дней. Не пропустите срок!Переход на страницу: https://google.com