Chapter 11: Socket.IO

As mentioned previously, getting WebSocket ready for your applications takes more than a simple implementation.

Socket.IO is a project I created that aims to solve the most common deficiencies of the simple implementation. It provides a great deal of flexibility while retaining a simple API:

Server API

io.listen(app);

io.sockets.on(‘connection’, function (socket) {

  socket.emit(‘my event’, { my: ‘object’ });

});

Browser/Client API

var socket = io.connect();

socket.on(‘my event’, function (obj) {

  console.log(obj.my);

});

Transports

One of the most appealing features about Socket.IO is that communication is based on transports, not all of which are WebSocket, which means Socket.IO works on a large variety of browsers and devices, all the way from IE6 to iOS.

For example, you can utilize AJAX as a method for real-time communication when using a technique called long polling. Basically, this technique consists of making serial AJAX calls, but if the server doesn’t have any data to send you, the connection stays open for 20–50 seconds so that no extra data transfer due to HTTP request/response headers occurs.

Socket.IO automatically leverages complex and convoluted techniques such as long polling for you, without making the API any more complicated than WebSocket.

In addition, even if WebSocket is supported by the browser but blocked by proxies or firewalls, Socket.IO can still handle that situation gracefully.

Disconnected versus closed

Another fundamental feature that Socket.IO brings to the table is timeouts. As discussed in Chapters 6 and 10, an application that relies on perfectly closed TCP connections is not ready for real-world usage.

Throughout your use of Socket.IO in this chapter, you listen on connect events instead of open, and disconnect instead of close. The reason is that Socket.IO provides reliable events. If the client stops transmitting data but doesn’t properly close the connection after a certain amount of time elapses, Socket.IO considers him disconnected.

This approach allows you to focus on the core of your application logic instead of all the possible different hiccups of networks.

Socket.IO also takes care of reconnecting when the connection is lost, which happens automatically by default.

Events

So far you saw that typical communication on the web has been oriented around retrieving (requesting) documents (resources) over HTTP. The real-time web, however, is about the transmission of events.

Even though Socket.IO still allows you to transmit simple text back and forth like WebSocket, it also enables you to emit and listen on events that send JSON data back and forth In the following example you can see Socket.IO acting as as reliable WebSocket:

io.sockets.on(‘connection’, function (socket) {

  socket.send(‘a’);

  socket.on(‘message’, function (msg) {

    console.log(msg);

  });

});

If you were to re-imagine the cursor example from Chapter 10 with Socket.IO, the application code would be greatly simplified:

Client code

var socket = io.connect();

socket.on(‘position’, move);

socket.on(‘remove’, remove);

Notice that instead of having to parse the incoming strings of a single event (message), we can channel data according to its meaning within the applications. Events can receive any number of parameters in any of the types JSON encodes: Number, Array, String, Object, and so on.

Namespaces

Another powerful feature that Socket.IO offers is the ability to separate a single connection into namespaces that are isolated from each other.

Sometimes your application requires separation of logic into distinct parts, but for performance or speed reasons it’s still desirable to leverage the same connection. Considering you can’t make assumptions about how fast the clients are or how capable their browsers are, it’s usually a good idea to not rely on too many open connections simultaneously.

Therefore, Socket.IO allows you to listen on the connection event of multiple namespaces:

io.sockets.on(‘connection’);

io.of(‘/some/namespace’).on(‘connection’)

io.of(‘/some/other/namespace’).on(‘connection’)

Even though you’ll get different connection objects, when you connect from the browser like in the following example, a single transport (like a WebSocket connection) will be used:

var socket = io.connect();

var socket2 = io.connect(‘/some/namespace’);

var socket3 = io.connect(‘/some/other/namespace’);

In some cases, modules or parts of your application are written in such a way that for the sake of abstraction are completely isolated from the rest. Some part of your client side JavaScript codebase might be completely unaware of another that’s executing in parallel.

For example, you could build a social network that displays a real time chat program alongside a farming game. Even though they could both share some common data, such as the identity of the authenticated user, it would be a good idea to write them in a way that they both assume complete control of a socket.

That socket, thanks to the namespaces (also called multiplexing) feature, does not necessarily have to be its own allocated actual TCP socket. Socket.IO takes care of channeling data through the same resource (the chosen transport for that user) and passing the data to the appropriate callbacks.

Now that you’ve learned the major differences between Socket.IO and WebSocket, you’re ready for the first example application, a chat program.

A chat program

Setting up the program

In the same fashion as websocket.io, you make socket.io attach itself to a regular http.Server that can still handle the requests and responses for your application:

package.json

{

    “name”: “chat.io”

  , “version”: “0.0.1”

  , “dependencies”: {

        “express”: “2.5.1”

      , “socket.io”: “0.9.2”

    }

}

As usual, once you create the package.json file make sure to run npm install to fetch all the dependencies.

Setting up the server

As with websocket.io, you set up a normal Express app with the static middleware:

server.js

/**

* Module dependencies.

*/


var express = require(‘express’)

  , sio = require(‘socket.io’)


/**

* Create app.

*/


app = express.createServer(

    express.bodyParser()

, express.static(‘public’)

);


/**

* Listen.

*/


app.listen(3000);

Now it’s time to attach socket.io. You call sio.listen in the same fashion as you do with websocket.io:

var io = sio.listen(app);

Now you can set up the connection’s listener:

io.sockets.on(‘connection’, function (socket) {

  console.log(‘Someone connected’);

});

For now, you can simply output to the console whenever someone connects. Because Socket.IO is a custom API, you have to load the Socket.IO client on the browser.

Setting up the client

Because you added the static middleware for the public folder, you need to create a file index.html inside.

This time, for the sake of convenience, keep the chat logic separate from the markup, into its own file called chat.js.

One of the handy aspects of Socket.IO is that when it appends itself to http.Server, all the communication that happens to URLs that begin with /socket.io are intercepted.

Socket.IO therefore also takes care of exposing the client code to the browser out of the box. Consequently, you don’t have to worry about obtaining and serving the file manually.

Notice that in the following example you create a <script> tag that references /socket.io/socket.io.js:

index.html

<!doctype html>

<html>

  <head>

    <title>Socket.IO chat</title>

    <script src=”/socket.io/socket.io.js”></script>

    <script src=”/chat.js”></script>

    <link href=”/chat.css” rel=”stylesheet” />

  </head>

  <body>

    <div id=”chat”>

      <ul id=”messages”></ul>

      <form id=”form”>

        <input type=”text” id=”input” />

        <button>Send</button>

      </form>

    </div>

  </body>

</html>

For now, chat.js is going to connect to make sure the client loaded properly. If everything goes well, you should be see the output Someone connected in the console.

chat.js

window.onload = function () {

  var socket = io.connect();

}

All the functions and classes exposed by the Socket.IO client are contained in the io namespace.

io.connect is similar to new WebSocket, but smarter. In this case, because you are not passing any arguments to it, it attempts to connect to the same host that is loading the page, which is a desirable behavior for this example.

You run this application normally with

$ node server

Then you point your browser to http://localhost:3000. You should see the output from the Socket.IO logger regarding what’s going on underneath the hood; for example, you can see what transport in particular this client is using (see Figure 11-1).

9781119963103-fg1101.eps

Figure 11-1: Debug output from socket.io along with the message you print with
console.log

If you connect from a modern browser, as in this example, Socket.IO is likely able to connect and then upgrade the connection to WebSocket.

Socket.IO always tries to find a method for connection that is faster for the user and performs best for your server, but it always ensures a connection despite adverse conditions.

Events and Broadcasting

Now that you have successfully connected, it’s time to identify the fundamental pieces of the Socket.IO server.

Broadcasting upon join

Whenever a user connects, you want to notify everyone else that she did. Because this is going to be a special message not sent by anyone in particular, you can call this an announcement and style it accordingly.

The first thing to do from the client perspective is to ask what the user’s name is.

Because you want to disallow any interaction with the chat until the user is actually connected, you need to hide the chat:

chat.css

/* … */

#chat { display: none }

Then you show it upon connection. To this end, you are going to listen on the connect event on the created socket (within the window.onload function you defined earlier):

chat.js

socket.on(‘connect’, function () {

  // send a join event with your name

  socket.emit(‘join’, prompt(‘What is your nickname?’));


  // show the chat

  document.getElementById(‘chat’).style.display = ‘block’;

});

On the server, you are going to listen on the join event to notify all others that the user connected. Replace the previous io.sockets connection handler with the following:

server.js

// …

io.sockets.on(‘connection’, function (socket) {

  socket.on(‘join’, function (name) {

    socket.nickname = name;

    socket.broadcast.emit(‘announcement’, name + ‘ joined the chat.’);

  });

});

Focus your attention on socket.broadcast.emit. broadcast is called a flag, which alters the behavior of the function that follows it.

In this case, if you simply call socket.emit, you echo back the message. But what you really want to do is broadcast that message to everyone else, which is what adding the flag accomplishes.

On the client, you are going to listen on the announcement event and create an element in the list of messages in the DOM. Add this at the bottom of the connect handler:

chat.js

socket.on(‘announcement’, function (msg) {

  var li = document.createElement(‘li’);

  li.className = ‘announcement’;

  li.innerHTML = msg;

  document.getElementById(‘messages’).appendChild(li);

});

Broadcasting chat messages

Next, you can give users the ability to write a message that gets sent to everyone else.

When a user enters data into the form and submits it, you are going to emit a text event with its content:

chat.js

var input = document.getElementById(‘input’);

document.getElementById(‘form’).onsubmit = function () {

  socket.emit(‘text’, input.value);


  // reset the input

  input.value = ‘’;

  input.focus();


  return false;

}

Because obviously the user wrote the message, you don’t want the server to send it back to himself. So you call the function addMessage immediately to display the message as soon as it’s sent:

chat.js

function addMessage (from, text) {

  var li = document.createElement(‘li’);

  li.className = ‘message’;

  li.innerHTML = ‘<b>’ + from + ‘</b>: ‘ + text;

  document.getElementById(‘messages’).appendChild(li);

}

document.getElementById(‘form’).onsubmit = function () {

  addMessage(‘me’, input.value);

  // . . .

}

You want to do the same thing when you receive messages from others. Here, you can simply pass the reference to the addMessage function and ensure that from the server side you broadcast the message with the right parameters:

chat.js

// …

socket.on(‘text’, addMessage);

server.js

socket.on(‘text’, function (msg) {

  socket.broadcast.emit(‘text’, socket.nickname, msg);

});

The code so far for each file should roughly look as follows:

chat.js

window.onload = function () {

  var socket = io.connect();

  socket.on(‘connect’, function () {

    // send a join event with your name

    socket.emit(‘join’, prompt(‘What is your nickname?’));


    // show the chat

    document.getElementById(‘chat’).style.display = ‘block’;


    socket.on(‘announcement’, function (msg) {

      var li = document.createElement(‘li’);

      li.className = ‘announcement’;

      li.innerHTML = msg;

      document.getElementById(‘messages’).appendChild(li);

    });

  });


  function addMessage (from, text) {

    var li = document.createElement(‘li’);

    li.className = ‘message’;

    li.innerHTML = ‘<b>’ + from + ‘</b>: ‘ + text;

    document.getElementById(‘messages’).appendChild(li);

  }


  var input = document.getElementById(‘input’);

  document.getElementById(‘form’).onsubmit = function () {

    addMessage(‘me’, input.value);

    socket.emit(‘text’, input.value);


    // reset the input

    input.value = ‘’;

    input.focus();


    return false;

  }


  socket.on(‘text’, addMessage);

}

server.js

/**

* Module dependencies.

*/


var express = require(‘express’)

  , sio = require(‘socket.io’)


/**

* Create app.

*/


app = express.createServer(

    express.bodyParser()

  , express.static(‘public’)

);


/**

* Listen.

*/


app.listen(3000);


var io = sio.listen(app);


io.sockets.on(‘connection’, function (socket) {

  socket.on(‘join’, function (name) {

    socket.nickname = name;

    socket.broadcast.emit(‘announcement’, name + ‘ joined the chat.’);

  });


  socket.on(‘text’, function (msg) {

    socket.broadcast.emit(‘text’, socket.nickname, msg);

  });

});

If you run server.js, you should now have a completely functioning real time chat application, like the one shown in Figure 11-2..

9781119963103-fg1102.eps

Figure 11-2: The chat application in action. Here chatting from multiple browser tabs.

Next up, you’ll learn about callbacks for events, and how they can help you add a new feature.

Ensuring reception

In the chat example, you call addMessage immediately upon the user pressing Enter, therefore creating the illusion that everyone else is seeing the message at that exact instant.

And just like WebSocket, Socket.IO does not enforce responses for each message you send. Sometimes, however, the need for confirmation that a message was received arises. Socket.IO calls this type of confirmation an acknowledgment.

To implement acknowledgments, all you have to do is pass a function whenever you’re emitting an event.

First, you’re going to return a reference to the element you create in the addMessage function so that you can append a CSS class to it after the message is confirmed as received. Then you can display a nice icon next to it.

/chat.js

function addMessage (from, text) {

  // …

  return li;

}

Next, you add the callback. Socket.IO can also receive data along with these acknowledgments. For this example, you can send a timestamp indicating when the message was received:

/chat.js

document.getElementById(‘form’).onsubmit = function () {

  var li = addMessage(‘me’, input.value);

  socket.emit(’text’, input.value, function (date) {

    li.className = ’confirmed’;

    li.title = date;

  });

On the server side, Socket.IO appends a callback as the last parameter of the event:

/server.js/

// …

socket.on(‘text’, function (msg, fn) {

  // …

  // confirm the reception

  fn(Date.now());

});

Now, when the server acknowledges that it received your message, a class will be added and the title attribute will be set for the appended list item. This brings the best of both worlds: the application has maximum responsiveness since it shows the message as soon as you press Enter, but you can still give feedback to the user through CSS (for example, by adding an icon next to the message, such as the encircled check mark in Figure 11-3.

9781119963103-fg1103.eps

Figure 11-3: In this example I set a CSS background after the acknowledgement
is received.

A DJ-by-turns application

How cool would it be if you empowered the users of your chat application to be DJs?

The server starts by selecting a DJ.

The DJ is given the ability to query an API, get search results, and select a song. He can then broadcast the song to others.

When the DJ leaves, he leaves the spot open for the next user to be elected DJ.

Extending the chat

The foundations of the chat application are solid enough to add this functionality to it.

The first thing to do is select a DJ if none is selected. Since you also want to keep track of the current song, you’ll define two state variables: currentSong and dj.

Since the DJ can change, you’ll define an elect function that performs the DJ selection task and announcements. When the join event is emitted, a DJ can be elected or the current song (currentSong) is relayed to the user. Later, when you implement search, currentSong is going to be populated with an object.

server.js

var io = sio.listen(app)

, currentSong

  , dj


function elect (socket) {

  dj = socket;

  io.sockets.emit(‘announcement’, socket.nickname + ‘ is the new dj’);

  socket.emit(‘elected’);

  socket.dj = true;

  socket.on(‘disconnect’, function () {

    dj = null;

    io.sockets.emit(‘announcement’, ‘the dj left - next one to join becomes dj’);

  });

}


io.sockets.on(‘connection’, function (socket) {

  socket.on(‘join’, function (name) {

    socket.nickname = name;

    socket.broadcast.emit(‘announcement’, name + ‘ joined the chat.’);

    if (!dj) {

      elect(socket);

    } else {

      socket.emit(‘song’, currentSong);

    }

  });

  // …

});

The elect function does the following:

1. Mark the current user as the DJ

2. Emit an announcement to everyone that a new DJ is ready.

3. Let the user know that she has been elected by emitting an elected event.

4. Upon the user being disconnected, mark the DJ spot as available so that the next connection becomes the DJ.

In the client, add the song selection interface to the markup, under the chat form:

index.html

<div id=”playing”></div>

<form id=”dj”>

  <h3>Search songs</h3>

  <input type=”text” id=”s” />

  <ul id=”results”></ul>

  <button type=submit>Search</button>

</form>

Integrating with the grooveshark API

Grooveshark (http://grooveshark.com) offers a simple and handy API for your purposes; it’s called TinySong.

TinySong allows a search like this:

GET http://tinysong.com/s/Beethoven?key={apiKey}&format=json

And it returns results like these:

[

  {

    “Url”: “http://tinysong.com/7Wm7”,

    “SongID”: 8815585,

    “SongName”: “Moonlight Sonata”,

    “ArtistID”: 1833,

    “ArtistName”: “Beethoven”,

    “AlbumID”: 258724,

    “AlbumName”: “Beethoven”

  },

  {

    “Url”: “http://tinysong.com/6Jk3”,

    “SongID”: 564004,

    “SongName”: “Fur Elise”,

    “ArtistID”: 1833,

    “ArtistName”: “Beethoven”,

    “AlbumID”: 268605,

    “AlbumName”: “Beethoven”

  },

  {

    “Url”: “http://tinysong.com/8We2”,

    “SongID”: 269743,

    “SongName”: “The Legend Of Lil’ Beethoven”,

    “ArtistID”: 7620,

    “ArtistName”: “Sparks”,

    “AlbumID”: 204019,

    “AlbumName”: “Sparks”

  }

]

You therefore expose a Socket.IO event called search that leverages the superagent module to query the API and return the results.

Add the superagent module to package.json and the module dependencies:

server.js

var express = require(‘express’)

  , sio = require(‘socket.io’)

  , request = require(‘superagent’)

package.json

  , “dependencies”: {

        “express”: “2.5.1”

      , “socket.io”: “0.9.2”

      , “superagent”: “0.4.0”

    }

Notice that you have to include your own API key as part of the URL, which you can get on the website http://tinysong.com:

Define the apiKey as follows:

server.js

var io = sio.listen(app)

  , apiKey = ‘{ your API key }’

  , currentSong

  , dj

And then define the search event:

socket.on(‘search’, function (q, fn) {

    request(‘http://tinysong.com/s/’ + encodeURIComponent(q)

      + ‘?key=’ + apiKey + ‘&format=json’, function (res) {

      if (200 == res.status) fn(JSON.parse(res.text));

    });});

Notice that I’m manually parsing the JSON response. This is due to a TinySong not currently sending the right Content-Type response header, which makes superagent’s automatic JSON parsing not be enabled.

To make things more fun in the application, you’re going to make the search available to everyone, but only the Select functionality available to the DJ.

In the chat.css file, add these two lines:

#results a { display: none; }

form.isDJ #results a { display: inline; }

Then you’re going to add the logic to make the search, get the results back through a Socket.IO callback and relay the song choice to everyone.

In the chat.js file, add the following:

  // search form

  var form = document.getElementById(‘dj’);

  var results = document.getElementById(‘results’);

  form.style.display = ‘block’;

  form.onsubmit = function () {

    results.innerHTML = ‘’;

    socket.emit(‘search’, document.getElementById(‘s’).value, function (songs) {

      for (var i = 0, l = songs.length; i < l; i++) {

        (function (song) {

          var result = document.createElement(‘li’);

          result.innerHTML = song.ArtistName + ‘ - <b>’ + song.SongName + ‘</b> ‘;

          var a = document.createElement(‘a’);

          a.href = ‘#’;

          a.innerHTML = ‘Select’;

          a.onclick = function () {

            socket.emit(‘song’, song);

            return false;

          }

          result.appendChild(a);

          results.appendChild(result);

        })(songs[i]);

      }

    });

    return false;

  };


  socket.on(‘elected’, function () {

    form.className = ‘isDJ’;

});

Most of the code is centered around DOM manipulation. Since the server relays all the songs from the TinyURL API, you are free to render it however you want. In this case I decided to show the song’s band next to the song’s name (ArtistName and SongName keys, respectively).

When the elected event is received, you change form’s className to reveal the links to select a given song.

Upon clicking the Select link, you send a song event to the server, whose job is simply going to be marking the current song and broadcasting it. In server.js add the following event:

server.js

  socket.on(‘song’, function (song) {

    if (socket.dj) {

      currentSong = song;

      socket.broadcast.emit(‘song’, song);

    }

});

Now that users have the ability to search and relay songs, all that’s left to add is the ability to play them. That’s what we reserved the <div id=playing> element for.

Playing

Just like you did with the addMessage function, you’re going to define one to mark the current song being played.

Add the following code to chat.js. It simply renders the song’s band and title next to the text Now Playing, and it injects an iframe that points to the Url field that TinySong gave you to play the song.

  var playing = document.getElementById(‘playing’);

  function play (song) {

    if (!song) return;

    playing.innerHTML = ‘<hr><b>Now Playing: </b> ‘

      + song.ArtistName + ‘ ‘ + song.SongName + ‘<br>’;


    var iframe = document.createElement(‘iframe’);

    iframe.frameborder = 0;

    iframe.src = song.Url;

    playing.appendChild(iframe);

  };


You want to use, once again, this function in two situations: as soon as the DJ selects a song (for himself), and when the song event is relayed to a regular user by the DJ.

For the second case, you simply pass the new play function as the callback to the song event in chat.js:

socket.on(‘song’, play);

For the DJ to start listening immediately, you want to call play as soon as he selects it. Go back to the onclick handler where you emit the song event to the server and add call play, so that the handler looks as follows:

          a.onclick = function () {

            socket.emit(‘song’, song);

            play(song);

            return false;

          }

And you’re done!. If you recall from the initial addition to the join event on the server-side, the song event gets emitted if there’s a currentSong on the server side. This means that the song will start playing not just for users that were connected at the time of its selection by the DJ, but also new users joining the room after they select their nickname (see Figure 11-4).

The final code for the complete Chat + DJ application should look roughly as follows:

server.js

var express = require(‘express’)

  , sio = require(‘socket.io’)

  , request = require(‘superagent’)


app = express.createServer(

    express.bodyParser()

  , express.static(‘public’)

);


app.listen(3000);


var io = sio.listen(app)

  , apiKey = ‘{ your API key }’

  , currentSong

  , dj


function elect (socket) {

  dj = socket;

  io.sockets.emit(‘announcement’, socket.nickname + ‘ is the new dj’);

  socket.emit(‘elected’);

  socket.dj = true;

  socket.on(‘disconnect’, function () {

    dj = null;

    io.sockets.emit(‘announcement’, ‘the dj left - next one to join becomes dj’);

  });

}


io.sockets.on(‘connection’, function (socket) {

  socket.on(‘join’, function (name) {

    socket.nickname = name;

    socket.broadcast.emit(‘announcement’, name + ‘ joined the chat.’);

    if (!dj) {

      elect(socket);

    } else {

      socket.emit(‘song’, currentSong);

    }

  });

  socket.on(‘song’, function (song) {

    if (socket.dj) {

      currentSong = song;

      socket.broadcast.emit(‘song’, song);

    }

  });


  socket.on(‘search’, function (q, fn) {

    request(‘http://tinysong.com/s/’ + encodeURIComponent(q)

      + ‘?key=’ + apiKey + ‘&format=json’, function (res) {

      if (200 == res.status) fn(JSON.parse(res.text));

    });

  });


  socket.on(‘text’, function (msg) {

    socket.broadcast.emit(‘text’, socket.nickname, msg);

  });

});

chat.js

window.onload = function () {

  var socket = io.connect();

  socket.on(‘connect’, function () {

    // send a join event with your name

    socket.emit(‘join’, prompt(‘What is your nickname?’));


    // show the chat

    document.getElementById(‘chat’).style.display = ‘block’;


    socket.on(‘announcement’, function (msg) {

      var li = document.createElement(‘li’);

      li.className = ‘announcement’;

      li.innerHTML = msg;

      document.getElementById(‘messages’).appendChild(li);

    });

  });


  function addMessage (from, text) {

    var li = document.createElement(‘li’);

    li.className = ‘message’;

    li.innerHTML = ‘<b>’ + from + ‘</b>: ‘ + text;

    document.getElementById(‘messages’).appendChild(li);

  }


  var input = document.getElementById(‘input’);

  document.getElementById(‘form’).onsubmit = function () {

    addMessage(‘me’, input.value);

    socket.emit(‘text’, input.value);


    // reset the input

    input.value = ‘’;

    input.focus();


    return false;

  }


  socket.on(‘text’, addMessage);


  // plays a song

  var playing = document.getElementById(‘playing’);

  function play (song) {    

    if (!song) return;

    playing.innerHTML = ‘<hr><b>Now Playing: </b> ‘

      + song.ArtistName + ‘ ‘ + song.SongName + ‘<br>’;


    var iframe = document.createElement(‘iframe’);

    iframe.frameborder = 0;

    iframe.src = song.Url;

    playing.appendChild(iframe);

  };

  socket.on(‘song’, play);


  // search form

  var form = document.getElementById(‘dj’);

  var results = document.getElementById(‘results’);

  form.style.display = ‘block’;

  form.onsubmit = function () {

    results.innerHTML = ‘’;

    socket.emit(‘search’, document.getElementById(‘s’).value, function (songs) {

      for (var i = 0, l = songs.length; i < l; i++) {

        (function (song) {

          var result = document.createElement(‘li’);

          result.innerHTML = song.ArtistName + ‘ - <b>’ + song.SongName + ‘</b> ‘;

          var a = document.createElement(‘a’);

          a.href = ‘#’;

          a.innerHTML = ‘Select’;

          a.onclick = function () {

            socket.emit(‘song’, song);

            play(song);

            return false;

          }

          result.appendChild(a);

          results.appendChild(result);

        })(songs[i]);

      }

    });

    return false;

  };

  socket.on(‘elected’, function () {

    form.className = ‘isDJ’;

  });

}

index.html

<!doctype html>

<html>

  <head>

    <title>Socket.IO chat</title>

    <script src=”/socket.io/socket.io.js”></script>

    <script src=”/chat.js”></script>

    <link href=”/chat.css” rel=”stylesheet” />

  </head>

  <body>

    <div id=”chat”>

      <ul id=”messages”></ul>

      <form id=”form”>

        <input type=”text” id=”input” />

        <button>Send</button>

      </form>

      <div id=”playing”></div>


      <form id=”dj”>

        <h3>Search songs</h3>

        <input type=”text” id=”s” />

        <ul id=”results”></ul>

        <button>Search</button>

      </form>

    </div>

  </body>

</html>

9781119963103-fg1104.eps

Figure 11-4: The DJ+Chat application in action

Summary

Socket.IO is a really simple but extremely powerful API to build applications that communicate data back-and-forth really fast, in real time. Socket.IO gives you the confidence that this data exchange happens not only as fast as possible, but also that it works on every browser and lots of mobile devices.

During this chapter you learned how to structure a really simple application that takes advantage of some of the API sugar it provides. You leveraged events as a way of organizing the different type of information that’s sent back and forth between users and the server.

A fundamental part of writing real time applications is broadcasting. You learned how to convey an event to everyone in the server, but also how to have one person convey something to everyone else. You used this technique, for example, to enable a DJ to announce what song is currently playing to everyone else.

One thing to keep in mind is that a lot of the functionality is contained in the client side: you need to have code ready to restructure the interface according to the variety of events that can happen. Since this chapter focuses solely on Socket.IO, I made sure to stay away from templating libraries or higher-level frameworks that can be leveraged on the client-side and worked on top of the DOM APIs directly, but in the real world this can get quite complicated as the application grows.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.218.89.173