lördag 27 juli 2013

Realtime communication using SocketIO

This is a short introduction to SocketIO.

SocketIO is a Javascript library for realtime communication over Internet. Using SocketIO you can very easily write client-server apps where data is exchanged between a client and a server, or between multiple clients connected to the server at the same time. Some applications that spring to mind are chat applications and simple multiplayer games.

SocketIO tries to use WebSockets for its communication, but can fallback to other techniques in situations where WebSockets can not be used. So you actually don't have to mind about the communication medium when you write your app.

For the server-side of your app, you write your app in Javascript and run it using the Node.js execution environment.

Here below I have written a small tutorial to show how easy it is to create a simple, browser-based, realtime chat application.

What will the application do?
  • The application will let a client, running in a browser, connect to a server and send messages and get responses from the server in realtime.
  • If multiple clients, running in separate browsers and potentially on separate computers, are connected to the server at the same time, the clients will be able to send text messages to each other.
  • The client-side app will have two text boxes, one for writing text messages to send to the server, and one for writing text messages that will be broadcasted to all connected clients.
  • The client-side app will also have a text label. Whenever the client receives a text message (either from the server, or from another client) the message will be shown in the text label.

First some pre-requisites before we start with the implementation:
  • First of all, this requires that you have some server where you can upload and run your server-side app. See for example my earlier posting about Linode on how you can rent a private virtual server: http://mattiaserlo.blogspot.com/2013/06/virtual-private-server.html (Or I guess you could run the server also on your computer using "localhost" for test purposes.)
  • You'll also need to have Node.js installed on your server. See my earlier posting about how to install Node.js: http://mattiaserlo.blogspot.com/2013/06/nodejs.html
  • You also need to install the SocketIO package on your server. On your server, write: npm install socketio

Now we can get started writing some code! As explained above, the application will consist of two parts: one server-side app that will handle incoming connections and messages from clients, and one client-side app that will run on a client in a browser.
Let's start with the server-side app. Create a file called serverapp.js with the following contents:


var io = require('socket.io').listen(8083);

io.sockets.on('connection', function (socket) {
 console.log("Someone connected");
 
 socket.on('messageToServer', function (data) {
  console.log("Got messageToServer message");
  
  if (data.type != undefined) {
   console.log("data.type = " + data.type);
  }
  
  // Do some work on the server...
  // ...
  
  // Then send a reply to the client
  socket.emit('messageFromServer', { type: 'someOtherType',
       text: 'Hello!' });
 });
 
 socket.on('broadcast', function (data) {
  console.log("Got broadcast message");
  
  // Broadcast the message to all connected clients
  io.sockets.emit('broadcast', data);
 });
});

The first line creates a socketIO object and tells socketIO to listen to incoming connections on port 8083. I chose this port number arbitrarily - you can use any number you want as long as it is not already used by some other app or service on your server.

The second line, io.sockets.on('connection', function (socket) {, declares an anonymous function that will be called whenever some client connects to the app on the port we are listening to. As you see there is a function argument, socket. This socket object is used for communicating with the client from now on. If another clients connects to the server later, the anonymous function will be called again with a new socket used for that new client.
Inside the anonymous function you can see socket.on('messageToServer', function (data) { ... Here we are telling socketIO that whenever a message of type messageToServer comes to the server, execute this function. By the way, message names are just text string and you can decide what to name your messages on your own.

So what we do when "messageToServer" is received, is we do a debug print and then we call socket.emit. Socket.emit is used for sending data via the socket. In our example we are sending a message of another type, messageFromServer, back to the client, with some additional data. Again, you are free to name your messages whatever you want, and you can basically pass any number of parameters in your message. Message data should be JSON formatted.

You can also see that we are listening to another message type, broadcast. I came up with this name for messages that I want to broadcast to all connected clients. So if a client wants to send some text that should be received by all clients, the client will send this message to the server. When the server receives a message of this type, it calls io.sockets.emit('broadcast', data);

io.sockets.emit means that a message will be sent to all the sockets currently open on the server app. And we are reusing the name "broadcast" for this message type. We don't need to modify the data, we just pass on whatever data the client wishes to broadcast.

And this is actually all the code we need for the server-side.

To run this code on your server, upload the code to your node folder on your server, open a shell on your server and write: node serverapp.js

Now we need to write the client-side of the application.

Create a file named index.html, containing the following:


<!DOCTYPE html>
<html>
<head>
 <title>SocketIO test</title>
</head>
<body>
 <script src="http://106.187.52.148:8083/socket.io/socket.io.js"></script>
 <h1>SocketIO test</h1>
 Message to send to server: <input type="text" id="toServerTextBox" onchange="toServerTextChanged()">
 <br>
 Message to broadcast to clients: <input type="text" id="broadcastTextBox" onchange="broadcastTextChanged()">
 <br>
 <label id="textLabel"></label>
 <br>
 <script>
  
  var socket = null;
  
  function toServerTextChanged() {
   var textBox = document.getElementById("toServerTextBox");
   if (socket) {
    socket.emit('messageToServer', { text: textBox.value });
   }
  }
  
  function broadcastTextChanged() {
   var textBox = document.getElementById("broadcastTextBox");
   if (socket) {
    socket.emit('broadcast', { text: textBox.value });
   }
  }

  socket = io.connect('http://106.187.52.148:8083');

  if (socket != null) {
   socket.on('messageFromServer', function (data) {
    console.log("Got messageFromServer");
    document.getElementById('textLabel').innerHTML =
    "Got message from server: " + data.text;
   });

   socket.on('broadcast', function (data) {
    console.log("Got broadcast message");
    document.getElementById('textLabel').innerHTML =
    "Got broadcast message: " + data.text;
   });


   console.log("Now send a message to the server");
   
   socket.emit('messageToServer', { type: 'someType' ,
       moreData: 'someOtherData' });
  }
 </script> 
</body>
</html>


Looking at this code, you can see a line that fetches a script, script src="http://...

You should exchange the IP number with the IP number to your server, and use the same port number as you chose for your server-app to listen to. The socket.io.js script will be magically served by your server when the clients need the script.

Further below in the code you can see that we are calling io.connect:
socket = io.connect('http://106.187.52.148:8083');
Again, make sure here to use your server's IP number and port number.
When this line is executed, the client will connect to the server, and your server-app's line io.sockets.on('connection'... will be called with a socket to use for communicating with this client.

Further below in the code you can see similar code as we wrote for the server-side - we tell which messages we want to listen to and associate each message with an anonymous function to be called. In our example implementation here we simply update the text label with the message contents.

Now that you are running your server-side app on your server, you are ready to receive connections from clients. So on your local computer, start a browser, and open the index.html file you just wrote. Hopefully you'll see the text label being updated with a "hello" from the server, as a response from the client's connection request.

Now, either in another browser tab, or on another computer or smartphone, open up the index.html file. Now both clients should be connected to the server. Writing a message in the broadcast text box (and pressing Enter) should make the message pop up on all clients' screens.

Neat huh? :-)

If you want to download the code from GitHub, here is the link: https://github.com/mattiaserlo/socketiotest

Inga kommentarer: