Now let's create a simple example. Firstly, run the signaling server we created in the “signaling server” tutorial via “node server”.
There will be three text inputs on the page, one for a login, one for a username, and one for the message we want to send to the other peer. Create an index.html file and add the following code −
<html lang = "en"> <head> <meta charset = "utf-8" /> </head> <body> <div> <input type = "text" id = "loginInput" /> <button id = "loginBtn">Login</button> </div> <div> <input type = "text" id = "otherUsernameInput" /> <button id = "connectToOtherUsernameBtn">Establish connection</button> </div> <div> <input type = "text" id = "msgInput" /> <button id = "sendMsgBtn">Send text message</button> </div> <script src = "client.js"></script> </body> </html>
We've also added three buttons for login, establishing a connection and sending a message. Now create a client.js file and add the following code −
var connection = new WebSocket('ws://localhost:9090'); var name = ""; var loginInput = document.querySelector('#loginInput'); var loginBtn = document.querySelector('#loginBtn'); var otherUsernameInput = document.querySelector('#otherUsernameInput'); var connectToOtherUsernameBtn = document.querySelector('#connectToOtherUsernameBtn'); var msgInput = document.querySelector('#msgInput'); var sendMsgBtn = document.querySelector('#sendMsgBtn'); var connectedUser, myConnection, dataChannel; //when a user clicks the login button loginBtn.addEventListener("click", function(event) { name = loginInput.value; if(name.length > 0) { send({ type: "login", name: name }); } }); //handle messages from the server connection.onmessage = function (message) { console.log("Got message", message.data); var data = JSON.parse(message.data); switch(data.type) { case "login": onLogin(data.success); break; case "offer": onOffer(data.offer, data.name); break; case "answer": onAnswer(data.answer); break; case "candidate": onCandidate(data.candidate); break; default: break; } }; //when a user logs in function onLogin(success) { if (success === false) { alert("oops...try a different username"); } else { //creating our RTCPeerConnection object var configuration = { "iceServers": [{ "url": "stun:stun.1.google.com:19302" }] }; myConnection = new webkitRTCPeerConnection(configuration, { optional: [{RtpDataChannels: true}] }); console.log("RTCPeerConnection object was created"); console.log(myConnection); //setup ice handling //when the browser finds an ice candidate we send it to another peer myConnection.onicecandidate = function (event) { if (event.candidate) { send({ type: "candidate", candidate: event.candidate }); } }; openDataChannel(); } }; connection.onopen = function () { console.log("Connected"); }; connection.onerror = function (err) { console.log("Got error", err); }; // Alias for sending messages in JSON format function send(message) { if (connectedUser) { message.name = connectedUser; } connection.send(JSON.stringify(message)); };
You can see that we establish a socket connection to our signaling server. When a user clicks on the login button the application sends his username to the server. If login is successful the application creates the RTCPeerConnection object and setup onicecandidate handler which sends all found icecandidates to the other peer. It also runs the openDataChannel() function which creates a dataChannel. Notice that when creating the RTCPeerConnection object the second argument in the constructor optional: [{RtpDataChannels: true}] is mandatory if you are using Chrome or Opera. The next step is to create an offer to the other peer. Add the following code to your client.js file−
//setup a peer connection with another user connectToOtherUsernameBtn.addEventListener("click", function () { var otherUsername = otherUsernameInput.value; connectedUser = otherUsername; if (otherUsername.length > 0) { //make an offer myConnection.createOffer(function (offer) { console.log(); send({ type: "offer", offer: offer }); myConnection.setLocalDescription(offer); }, function (error) { alert("An error has occurred."); }); } }); //when somebody wants to call us function onOffer(offer, name) { connectedUser = name; myConnection.setRemoteDescription(new RTCSessionDescription(offer)); myConnection.createAnswer(function (answer) { myConnection.setLocalDescription(answer); send({ type: "answer", answer: answer }); }, function (error) { alert("oops...error"); }); } //when another user answers to our offer function onAnswer(answer) { myConnection.setRemoteDescription(new RTCSessionDescription(answer)); } //when we got ice candidate from another user function onCandidate(candidate) { myConnection.addIceCandidate(new RTCIceCandidate(candidate)); }
You can see that when a user clicks the “Establish connection” button the application makes an SDP offer to the other peer. We also set onAnswer and onCandidate handlers. Finally, let's implement the openDataChannel() function which creates our dataChannel. Add the following code to your client.js file −
//creating data channel function openDataChannel() { var dataChannelOptions = { reliable:true }; dataChannel = myConnection.createDataChannel("myDataChannel", dataChannelOptions); dataChannel.onerror = function (error) { console.log("Error:", error); }; dataChannel.onmessage = function (event) { console.log("Got message:", event.data); }; } //when a user clicks the send message button sendMsgBtn.addEventListener("click", function (event) { console.log("send message"); var val = msgInput.value; dataChannel.send(val); });
Here we create the dataChannel for our connection and add the event handler for the “send message” button. Now open this page in two tabs, login with two users, establish a connection, and try to send messages. You should see them in the console output. Notice that the above example is tested in Opera.
Now you may see that RTCDataChannel is extremely powerful part of the WebRTC API. There are a lot of other use cases for this object, like peer-to-peer gaming or torrent-based file sharing.