This week I’m planning to keep it short. We’ll look at sockets and how to utilize them using Node.js. Luckily this is a relatively easy task for which Node.js is famous. Sockets enable you to build real time websites, something that is difficult at best using HTTP.
This will also be the last article in the series in which we’ll look at a new technology. If you read everything up til now you’re pretty well on your way to becoming a MEAN web developer! Next week we’ll have a wrap up and after that, who knows 🙂
- MEAN web development #1: MEAN, the what and why
- MEAN web development #2: Node.js in the back
- MEAN web development #3: More Node.js
- MEAN web development #4: All aboard the Node.js Express!
- MEAN web development #5: Jade and Express
- MEAN web development #6: AngularJS in the front
- MEAN web development #7: MongoDB and Mongoose
- MEAN web development #8: Sockets will rock your socks!
- MEAN web development #9: Some last remarks
As usual you can find the code samples for this post on my GitHub page under the mean8-blog repository.
What are sockets?
I won’t really go into technical details about sockets, or networks sockets. What is important to know is that a socket is basically an endpoint connection that allows communication between computers in a network. In an earlier post, Web development #1: Internet and the World Wide Web, I have briefly discussed the internet protocols TCP/IP (Transmission Control Protocol/Internet Protocol) and I have mentioned other protocols such as UDP (User Datagram Protocol). Those protocols make use of sockets to transfer data across the web.
As we have seen protocols such as HTTP (and HTTPS) make use of the TCP/IP protocol, which makes use of sockets. As we know HTTP is a one way street, although the data flow isn’t. Think about it, with HTTP we can request data, but that data also has to be sent back to the client. What HTTP can’t do, but sockets obviously can, is sent data to a client. Now what if a server could send data to a client even though the client didn’t request it (at that exact moment)? Look no further! And that is exactly why sockets programming is becoming more and more popular!
The protocol used for this bi-directional exchange of data is called WebSockets and was standardized in 2011. As this is still pretty new keep in mind that older web browsers do not support this protocol. One thing to keep in mind is that WebSockets is really an upgrade from HTTP. A socket connection is actually established using an HTTP request.
Node.js has made it pretty easy to work with sockets. There are actually already quite a lot of tutorials out there and most of them let you build a chat application. Of course you can build anything requiring real time data. Think of real time Facebook or Twitter updates. It’s especially interesting when you consider the recent Internet of Things (or IoT) where everything is connected to the web. Let’s say someone rings your doorbell, which is connected to the web and sends you a picture of your front porch when someone rings the bell. Would you want to use HTTP to constantly poll/request if someone has just rang your doorbell? No you wouldn’t! In these scenario’s sockets are a necessity!
Sockets and Node.js
So let’s set up a small Node.js server that supports sockets. We could use ‘pure’ WebSockets for this, but I’m going to use a library for this called socket.io. This will make our life a lot easier on both the back- and front-end. socket.io is a layer around WebSockets that simplifies the API and, more importantly, falls back on other methods if WebSockets isn’t supported.
We’ll start by creating a Node.js server that serves some HTML page. We’ve done that a few times before, so that should be no problem. Remember that WebSockets requires HTTP to work, so we’ll need the HTTP module as well. And, as always, I’ll be using Express as well.
var express = require('express'); var app = express(); var http = require('http').Server(app); app.use(express.static('public')); app.get(['/', '/index'], function (req, res) { res.sendFile(__dirname + '/public/client.html'); }); http.listen(80, '127.0.0.1');
After that we can install socket.io (npm install socket.io).
var express = require('express'); var app = express(); var http = require('http').Server(app); var io = require('socket.io')(http); app.use(express.static('public')); app.get(['/', '/index'], function (req, res) { res.sendFile(__dirname + '/public/client.html'); }); io.on('connection', function(socket){ console.log('A user connected!'); }); http.listen(80, '127.0.0.1');
As you can see the http object is used to create an instance of socket.io. After that we can listen to the connection event for incoming sockets.
Now socket.io does a little magic trick, it automatically serves socket.io to our front-end. That means creating a socket instance on the client is really easy. First of all we need to include the script somewhere.
<html> <head> <meta charset="utf-8"> <title>Sockets example</title> <script src="/socket.io/socket.io.js"></script> <script src="client.js"></script> </head> <body> <h1>Sockets example</h1> </body> </html>
And then we can use sockets in client.js.
var socket = io();
Wow, that was easy!
And you’ll notice that if you put that in /public/client.html and browse to localhost Node.js will log ‘A user connected!’ to the console! So we’re already connecting.
Now we’ve talked about EventEmitters before in MEAN web development #3: More Node.js. socket.io uses them extensively. Each socket has a special ”disconnect” event.
io.on('connection', function(socket){ console.log('A user connected!'); socket.on('disconnect', function () { console.log('A user disconnected...'); }); });
If you try that, browse to localhost, and refresh the page, you’ll see “A user connectioned!”, “A user disconnected…” and “A user connected!” in the console. So this already works pretty sweet, right?
Let’s add another event. You know I like music and albums are always a favorite test object for me.
io.on('connection', function(socket){ console.log('A user connected!'); socket.on('disconnect', function () { console.log('A user disconnected...'); }); socket.on('add-album', function (album) { console.log(album); }); });
“Wait a minute!”, I hear you say, “No way a socket has an add-album event!” Well, not yet… Let’s check back in our front-end JavaScript again.
var socket = io(); socket.emit('add-album', { artist: 'Led Zeppelin', title: 'Led Zeppelin III' });
Save it, browse to localhost again and surely will the console print the album. That’s pretty sweet!
But we’ll need more. After all, we want to receive data on our client! Of course we aren’t going to see any data unless we show it on our page.
Let’s first check out the server. This is really very easy. Ready?
io.emit('add-album', album);
Nice, we can simply use io.emit and all connections will get an update. So our client sends out an ‘add-album’ event to the server and the server sends an ‘add-album’ event back to the client. We could’ve used any name as event.
Let’s check the front-end. I’ve added in some AngularJS, which I’ve talked about before in MEAN web development #6: AngularJS in the front. Actually I’ve copied this example from that post and changed ‘book’ to ‘album’.
<html> <head> <meta charset="utf-8"> <title>Sockets example</title> <script src="angular.min.js"></script> <script src="/socket.io/socket.io.js"></script> <script src="client.js"></script> </head> <body ng-app="socketsApp"> <div ng-controller="socketsController"> <h1>Sockets example</h1> <ul> <li ng-repeat="album in albums"> {{ album.artist + ' - ' + album.title }} </li> </ul> <input type="text" ng-model="newArtist" /> <input type="text" ng-model="newTitle" /> <button ng-click="addAlbum()">Add album</button> </div> </body> </html>
And here’s the JavaScript.
angular.module('socketsApp', []) .controller('socketsController', function ($scope) { var socket = io(); $scope.newArtist = null; $scope.newTitle = null; $scope.albums = []; $scope.addAlbum = function () { socket.emit('add-album', { artist: $scope.newArtist, title: $scope.newTitle }); $scope.newArtist = null; $scope.newTitle = null; }; socket.on('add-album', function (album) { $scope.$apply(function () { $scope.albums.push(album); }); }); });
Notice the $apply function of AngularJS. It’s a detail, but I need it to execute the code in the AngularJS context so that the view gets updated immediately.
What really matters is the socket.on function. I don’t have to explain it, you know how it works. It’s the exact same code you use on the server!
Now open up two tabs, two browsers, two windows, two whatever, and browse to localhost. Enter an artist and title in one window and submit. Now switch to the other window and you’ll see the album you’ve just added in the other window! No refresh, no nothing! If you’re really using two windows put them next to each other and see the effect live in real time.
Now maybe you’re thinking this is awesome, but there’s no need to send back the object to the client that just sent it to the server. For this you can use socket.broadcast.emit. This will send out a message to all sockets, except the one that’s broadcasting.
socket.broadcast.emit('add-album', album);
And the front-end would now look as follows.
$scope.addAlbum = function () { var album = { artist: $scope.newArtist, title: $scope.newTitle }; socket.emit('add-album', album); $scope.albums.push(album); $scope.newArtist = null; $scope.newTitle = null; };
Pretty sweet!
Rooms
Let’s say you’re building that chat app we were talking about earlier (basically what we created here too). Now maybe you want your web site to have multiple chat rooms. A room for C# discussion, a room for JavaScript discussion, or maybe people can create their own rooms about whatever (maybe cars, music, movies). And perhaps you want to implement a private chat as well (one on one, or only invited people). How can you do this?
I’ll leave the previous example for what it is and start a new one. From our page we want to be able to create a room, join a room, send a message and leave a room. So let’s check out the Node.js server side.
io.on('connection', function(socket){ console.log('A user connected!'); socket.on('disconnect', function () { console.log('A user disconnected...'); }); socket.on('add-room', function (room) { console.log('Added:'); console.log(room); socket.join(room.name); socket.broadcast.emit('add-room', room); }); socket.on('join-room', function (room) { console.log('Joined:'); console.log(room); socket.join(room); }); socket.on('send-message', function (message) { console.log('Send:'); console.log(message); socket.broadcast.to(message.roomName).emit('receive-message', message); }); socket.on('leave-room', function (room) { console.log('Leave:'); console.log(room); socket.leave(room); }); });
That’s quite a bit of code, but there’s not much new, really! When someone adds a room we just broadcast the room to all other clients so everyone can see the new room. Additionally we ‘join’ the room using socket.join. This creates a group of sockets that can be notified at once. When a user joins a room we simply pass in a room name and again call socket.join. Then when someone sends a message to a room we simply call socket.broadcast.to(roomName) and now only clients that have joined that specific group will get a notification. That’s pretty easy, right? When we leave a room we simply call socket.leave and the client will stop receiving notifications from that specific room.
The client-side scripting for this example isn’t really interesting. You can get the complete example from GitHub. And yes, I admit, the HTML could use a little work. The JavaScript works the same as in the previous example. We simply use socket.emit, to send events to the server, and socket.on to receive events from the server. I’ll leave it as practice for the reader to add user names, persist chat rooms, get a list of active rooms, implement private rooms, etc.
If you want to do more with sockets and socket.io I can recommend reading Syncfusion’s Node.js Succinctly. It has a chapter on ‘vanilla’ sockets programming in Node.js (including an example on UDP) and a chapter on socket.io. If you’re still struggling with AngularJS I can recommend reading AngularJS Succinctly.
Additionally Manning has a great book on Node.js, Node.js in Action, which, of course, also covers sockets. A second edition of the book is also in the making!
Next week we’ll be wrapping up the MEAN web programming series! Happy coding!