Login | Register   
LinkedIn
Google+
Twitter
RSS Feed
Download our iPhone app
TODAY'S HEADLINES  |   ARTICLE ARCHIVE  |   FORUMS  |   TIP BANK
Browse DevX
Sign up for e-mail newsletters from DevX


advertisement
 

Introduction to WebSockets

Get an introduction to WebSocket technology using a sample application based on Socket.io.


advertisement

In this article, I'll introduce you the WebSocket technology using a sample application based on Socket.io. At the end of this article you will have a good idea what WebSockets are all about, why it's needed, how it works, go through a simple but illustrative demo application client-server application.

Why WebSockets?

The Web was built around HTTP, which is a request-response protocol. Traditionally, every client request opened a new TCP connection to the server, waited for response and closed the connection. Establishing a TCP connection is relatively slow and expansive process. That limited the amount a requests a server can handle as well as the latency of individual requests. In addition, HTTP carries a lot of overhead with its cookies, headers, etc. One last problem is that the server can't push data to the client. Various solution and workaround were developed. Frequent polling by the client allows simulating server push, but you trade of the resolution of the updates with the overhead of many poll requests. Other solutions like long polling kept the same TCP connection open, but are cumbersome and suffer from various downsides. WebSockets were designed to provide a consistent low-latency persistent connection between browser (or any WebSocket client) and a server.

What Is a Web Socket?

WebSocket is a protocol that establishes a two-way TCP connection between client and server. While TCP is stream-based (you just get a stream of bytes), WebSocket is message-based. You get complete message, which is much nicer to work with because someone else (the WebSocket implementation) does all the framing and buffering for you.

How do WebSockets Work?

The HTTP server does an HTTP handshake with the client and upgrades the connection to a WebSocket connection over port 80 (good for penetrating firewalls).

Tic-Tac-Toe Sample App

The sample application is a two-player tic-tac-toe game based on Socket.io and Node's Express Web framework. Here is the server's package.json file



{
  "name": "tic-tac-toe-websockets",
  "version": "0.0.1",
  "description": "playing with WebSockets",
  "dependencies": {
    "express": "^4.14.0",
    "socket.io": "^1.7.2"
  }
}

The Tic-Tac-Toe Server

The server maintains the 3x3 board, listens to connections and processes 'click' messages form the clients. Whenever a client clicks the server sends the updated board to both players.

Initialize the server and the WebSocket machinery (Socket.io):

var app = require('express')();
var http = require('http').Server(app);
var io = require('socket.io')(http); 

Initialize the board:

var board = {
    '#11': '',
    '#12': '',
    '#13': '',
    '#21': '',
    '#22': '',
    '#23': '',
    '#31': '',
    '#32': '',
    '#33': ''
};

Next, initialize some state for the game. The server manages only one game at a time. The players dictionary maps the 'x' and 'o' players to their corresponding WebSockets (initially null). That way when a message arrives from a particular WebSocket, the server knows which player is it. The player variable initialized to 'x' keeps track of the next player to play.

var players = {'x': null, 'o': null};
var player = 'x';
var gameOver = false; 

This is the core processing loop. Inside the io.on('connection') call a few socket event handlers are set. The 'click' event handler is the most interesting. When it is received the server checks that it's the right player, that the square is not already occupied and that the game is not over. If everything is OK it updates the board, sends it to the players and checks if the player won. If the player won it sends a 'victory' message to all players and the state becomes "game over". If the game is still going it toggles the player, so the other player an play.

io.on('connection', function (socket) {
    if (players['x'] == null) {
        players['x'] = socket;
    } else if (players['o'] == null) {
        players['o'] = socket;
    } else {
        socket.disconnect();
    }

    socket.on('disconnect', function () {
        if (players['x'] == socket) {
            players['x'] = null;
        } else if (players['o'] == socket) {
            players['o'] = null;
        }
    });

    socket.on('click', function (id) {
        console.log(id + ' was clicked!');
        // Ignore players clicking when it's not their turn
        if (players[player] != socket) {
            return;
        }
        // Ignore clicks when game is over
        if (gameOver) {
            return;
        }

        // Ignore clicks on already clicked squares
        if (board[id] != '') {
            return;
        }

        // Ignore clicks before both players are connected
        if ((players['x'] == null) || (players['o'] == null)) {
            return;
        }

        var v = player;
        board[id] = v;
        io.emit('board', board);

        // Check victory (only current player can win)
        if ((board['#11'] == v && board['#12'] == v && board['#13'] == v) ||
            (board['#21'] == v && board['#22'] == v && board['#23'] == v) ||
            (board['#31'] == v && board['#32'] == v && board['#33'] == v) ||
            (board['#11'] == v && board['#21'] == v && board['#31'] == v) ||
            (board['#12'] == v && board['#22'] == v && board['#33'] == v) ||
            (board['#13'] == v && board['#23'] == v && board['#33'] == v) ||
            (board['#11'] == v && board['#22'] == v && board['#33'] == v) ||
            (board['#13'] == v && board['#22'] == v && board['#31'] == v))
        {
            gameOver = true;
            io.emit('victory', player);
            return;
        }

        // Toggle the player
        player = player == 'x' ? 'o' : 'x';
    });

    io.emit('board', board);
});

The root entry point just serves the client's index.html file:

app.get('/', function (req, res) {
    res.sendFile(__dirname + '/index.html');
});

The server just listens on port 7777:

var port = 7777;
http.listen(port, function () {
    console.log('Listening on port ' + port + '...');
});

Tic-Tac-Toe Client

The client is a simple Socket.io + jquery Javascript application (jquery not really needed, just for convenience).

Here is the head with some styling:

<!DOCTYPE html>
<html>
  <head>
    <title>Socket.IO Tic-Tac-Toe</title>
    <style>
      * { margin: 0; padding: 0; box-sizing: border-box; }
      td { cursor: pointer; border: solid 1px green; padding: 5px; width: 30px; height: 30px; alignment: center }
      body { font: 13px Helvetica, Arial; }
      #messages { list-style-type: none; margin: 0; padding: 0; }
      #messages li { padding: 5px 10px; }
      #messages li:nth-child(odd) { background: #eee; }
    </style>
  </head>

Here the client gets the socket.io and JQuery libraries.

<script src="https://cdn.socket.io/socket.io-1.2.0.js"></script>
  <script src="http://code.jquery.com/jquery-1.11.1.js"></script>

The body just has a table of 3x3 to represent the tic-tac-toe board.

<body>
    </div>
        <table>
            <tr>
                <td id="11"></td>
                <td id="12"></td>
                <td id="13"></td>
            </tr>
            <tr>
                <td id="21"></td>
                <td id="22"></td>
                <td id="23"></td>
            </tr>
            <tr>
                <td id="31"></td>
                <td id="32"></td>
                <td id="33"></td>
            </tr>
        </table>
    </div>
  </body>

Now, we get to the client's logic. The call to "var socket = io();" connects to the server. There is no need to specify a URL or IP address. It just connects to its own Web server. Then it binds a click handler to every square in the board (<td> cell). The handler just logs its 'click' and sends a 'click' message to the server, with the ID of the clicked cell.

<script>
    var socket = io();
    for (var i = 1; i < 4; ++i){
        for (var j = 1; j < 4; ++j){
            var id = '#' + i + j;
            $(id).bind(
                'click',
                (function(id){
                    return function(){
                    console.log('clicked ' + id);
                    socket.emit('click', id);
                    return false;
            }}(id)));
        }
    }

Here the client listens to the 'board' message from the server and updates its board state.

socket.on('board', function(board) {
        console.log('Received board!');
        for (var i = 1; i < 4; ++i) {
            for (var j = 1; j < 4; ++j) {
                var id = '#' + i + j;
                $(id).text(board[id]);
            }
        }
    });

Finally, when it gets the 'victory' message it disconnects.

socket.on('victory', function(player) {
        console.log(player + ' wins!');
        io.disconnect();
        //$('#messages').append($('<li>').text(msg));
  });
  </script>
</html>

This is just demo app, so I didn't even handle the case in which there is no winner (very common in tic-tac-toe).

Conclusion

WebSockets allow sophisticated and efficient workflow that require bi-directional communication between Web servers and browsers. It is efficient and easy to work with. Consider it for your next Web application.



   
Gigi Sayfan is the chief platform architect of VRVIU, a start-up developing cutting-edge hardware + software technology in the virtual reality space. Gigi has been developing software professionally for 21 years in domains as diverse as instant messaging, morphing, chip fabrication process control, embedded multi-media application for game consoles, brain-inspired machine learning, custom browser development, web services for 3D distributed game platform, IoT/sensors and most recently virtual reality. He has written production code every day in many programming languages such as C, C++, C#, Python, Java, Delphi, Javascript and even Cobol and PowerBuilder for operating systems such as Windows (3.11 through 7), Linux, Mac OSX, Lynx (embedded) and Sony Playstation. His technical expertise includes databases, low-level networking, distributed systems, unorthodox user interfaces and general software development life cycle.
Comment and Contribute

 

 

 

 

 


(Maximum characters: 1200). You have 1200 characters left.

 

 

Sitemap
Thanks for your registration, follow us on our social networks to keep up-to-date