Click here to Skip to main content
15,860,972 members
Articles / Web Development / HTML5

Building a basic HTML5 client/server application

Rate me:
Please Sign up or sign in to vote.
4.84/5 (50 votes)
4 Mar 2015CPOL4 min read 278.1K   16.4K   171   59
This article is about a basic implementation of HTML5 client/server chat application.

Introduction

In this article I would like to show a basic implementation of HTML5 client/server application. The client is implemented using HTML5, the server is implemented in C++.

Background 

The client/server application implies sending data between participants. This task will be accomplished by using a new HTML5 feature called WebSocket, which is described in RFC6455. At the moment the most complete support of RFC6455 is implemented by Mozila Firefox (15.0), Google Chrome (21.0.1180.89) and Internet Explorer 10 (10.0.8400.0) web browsers, they were used to test the application.

The server-side socket support is implemented using the boost.asio library, the server’s code pretends to be a cross-platform one.

The Client

The client’s code is based on a simple chat client example from www.html5demos.com, credits to Remy Sharp.

Image 1

The client is implemented as a web page, which creates a websocket object and subscribes to its events: open, close, error and message.

To connect to a server we need to call the websocket constructor with the following string as a parameter:

JavaScript
var wsUri = "ws://127.0.0.1:7777";  	
websocket = new WebSocket(wsUri);

As you can see here, the wsUri string contains an IP address and a port of a server application. In our case the server application runs on a localhost and the port number is 7777.

The client uses the following procedure:

  1. Initializing variables to access web page elements.
    JavaScript
    connected = document.getElementById("connected");
    log = document.getElementById("log");
    chat = document.getElementById("chat");
    form = chat.form;
    state = document.getElementById("status");
  2. A check whether a web browser supports the WebSocket feature.
    JavaScript
    if (window.WebSocket === undefined)
    {
        state.innerHTML = "sockets not supported";
        state.className = "fail";
    }
    else
    {
        // ...
        
        window.addEventListener("load", onLoad, false);
    }
  3. Creation of a websocket object and subscibing to its events.
    JavaScript
    function onLoad()
    {
        var wsUri = "ws://127.0.0.1:7777";  
     
        websocket = new WebSocket(wsUri);
        websocket.onopen = function(evt) { onOpen(evt) };
        websocket.onclose = function(evt) { onClose(evt) };
        websocket.onmessage = function(evt) { onMessage(evt) };
        websocket.onerror = function(evt) { onError(evt) };
    }

Below are the event handlers.

JavaScript
function onOpen(evt)
{
    state.className = "success";
    state.innerHTML = "Connected to server";	
}
  
function onClose(evt)
{
    state.className = "fail";
    state.innerHTML = "Not connected";
    connected.innerHTML = "0";	
}
 
function onMessage(evt)
{
    // There are two types of messages: 
    //     1. a chat participant message itself
    //     2. a message with a number of connected chat participants. 

    var message = evt.data;
	
    if (message.startsWith("log:"))
    {
        message = message.slice("log:".length);
        log.innerHTML = '<li class="message">' + message + "</li>" + log.innerHTML;	
    }
    else if (message.startsWith("connected:"))
    {
        message = message.slice("connected:".length);
        connected.innerHTML = message;	
    }    
}
 
function onError(evt)
{ 
    state.className = "fail";
    state.innerHTML = "Communication error";
}

Note, there are two types of messages the server sends to the client:

  1. a chat participant message itself, the "log:" message,
  2. a message with a number of connected chat participants, the "connected:" message.

Based on a message type we show its content in a corresponding web page element.

To send a message to the server the send function is used:

JavaScript
function addMessage()
{
    var message = chat.value;
     
    chat.value = "";
	  
    websocket.send(message);
}

The Server

The server’s code is based on the Chat application example from the boost.asio library, credits to Christopher M. Kohlhoff.

Image 2

The server works with sockets in an asynchronous manner, using I/O completion ports, and can easily be extended to run two or more threads.

Below is a class diagram for the chat application.

Image 3

The logic behind is the following:

  1. The server contains a room object, which is a container for chat participants.
  2. When a client connects to the server, the server creates a chat_session object and adds the object to the room.
  3. Each chat_session object contains a socket to interact with the client. When a message from the client arrives it is being delivered to all the room participants.

The WebSocket

The client code is based on the example from www.html5demos.com, the server code is based on the boost.asio example, where is your code, one may ask? Here you are! A basic implementation of the WebSocket protocol.

A websocket session consists of two consecutive phases: a handshake and data exchange.

Handshake

In order to establish a websocket connection, a client (a web browser) sends a HTTP GET request with a number of HTTP headers. Among those headers there is the Sec-WebSocket-Key header, which contains a handshake key. According to the WebSocket protocol, the server should:

  1. Concatenate the handshake key with the magic guid {258EAFA5-E914-47DA-95CA-C5AB0DC85B11}.
  2. Take the SHA1 hash of the concatenation result.
  3. Send the base64 equivalent of the hash in HTTP response to the client.

The following code illustrates the handshake procedure.

C++
namespace websocket {
    namespace http {
 
        void request_handler::handle_request(const request& req, reply& rep)
        {
            std::string key;
            for (std::vector<header>::const_iterator h = req.headers.cbegin();
                h != req.headers.cend(); ++h)
            {
                if (h->name == "Sec-WebSocket-Key")
                {
                    key = h->value;
                    break;
                }
            }
 
            if (key.empty())
            {
                rep = reply::stock_reply(reply::bad_request);
                return;
            }
 
            const std::string magic_guid("258EAFA5-E914-47DA-95CA-C5AB0DC85B11");
            std::string buffer(key + magic_guid);
            std::string accept(to_base64(to_sha1(buffer)));
 
            rep.status = reply::switching_protocols;
            rep.headers.resize(3);
            rep.headers[0].name = "Upgrade";
            rep.headers[0].value = "websocket";
            rep.headers[1].name = "Connection";
            rep.headers[1].value = "Upgrade";
            rep.headers[2].name = "Sec-WebSocket-Accept";
            rep.headers[2].value = accept;
        }
 
    } // namespace http
} // namespace websocket

Once the HTTP response is sent to the client, the handshake phase considered as complete and we can start data exchange. Coming back to our chat application, below is a state diagram of the chat session.

Image 4

Data Exchange

The data exchange between a websocket client and server is done by means of messages. Each message consists of a number of frames. The following picture shows a structure of a websocket data frame.

Image 5

The meanings of the fields can be found in RFC6455. Here is a structure to work with a data frame:

C++
namespace websocket {
 
    // A structure to hold websocket frame data. 
    struct dataframe
    {
        dataframe();
 
        bool fin;
        enum operation_code { continuation_frame, text_frame, binary_frame, connection_close, ping, pong, reserved } opcode;
        bool mask;
        boost::int8_t fin_opcode;
        boost::int8_t mask_payload_len;
        boost::int8_t payload_len;
        boost::uint64_t extended_payload_len;
        boost::array<boost::uint8_t, 4> masking_key;
        std::vector<boost::uint8_t> payload;
 
        // Convert the dataframe into a vector of buffers. 
        std::vector<boost::asio::const_buffer> to_buffers();
    };
 
} // namespace websocket

The Demo

To run the demo:

  1. Start the server app using the following command line:
    server 0.0.0.0 7777
  2. Open the chat.html web page in the Google Chrome or Mozilla Firefox web browsers.

Image 6

Any questions, remarks, and comments are welcome.

History

  • March 5, 2015 - Fixed a bug with payload length.
  • September 26, 2012 - Added support for Internet Explorer 10.
  • September 15, 2012 - Added a demo for Ubuntu.
  • September 2, 2012 - Initial version.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer
Russian Federation Russian Federation
Niko Bellic (Serbian: Niko Belić) is the main protagonist and playable character in the video game Grand Theft Auto IV. He is a 30 year old former soldier who moved to Liberty City to escape his troubled past and pursue the American Dream.

Comments and Discussions

 
QuestionFonctionne... super Pin
Didier Dubié 202111-May-21 1:02
Didier Dubié 202111-May-21 1:02 
Questionnot functioning in a network Pin
Ollie Rodriguez9-Nov-15 19:15
Ollie Rodriguez9-Nov-15 19:15 
AnswerRe: not functioning in a network Pin
vickzkater23-May-16 20:35
vickzkater23-May-16 20:35 
QuestionWSS Pin
Member 110922937-May-15 3:26
Member 110922937-May-15 3:26 
AnswerRe: WSS Pin
Sasha Yakobchuk10-May-15 5:15
Sasha Yakobchuk10-May-15 5:15 
GeneralMy vote of 5 Pin
Isaac RF8-Mar-15 23:47
professionalIsaac RF8-Mar-15 23:47 
GeneralMy vote of 5 Pin
Humayun Kabir Mamun4-Mar-15 21:53
Humayun Kabir Mamun4-Mar-15 21:53 
BugPayload length encoding bug Pin
Konstantin Korotkov3-Mar-15 9:53
Konstantin Korotkov3-Mar-15 9:53 
GeneralRe: Payload length encoding bug Pin
Sasha Yakobchuk4-Mar-15 9:32
Sasha Yakobchuk4-Mar-15 9:32 
GeneralMy vote of 5 Pin
Alex Sapozhnikov23-Jul-14 21:44
Alex Sapozhnikov23-Jul-14 21:44 
Questioninclude boost ... Pin
Patroklov13-May-14 3:44
Patroklov13-May-14 3:44 
AnswerRe: include boost ... Pin
Sasha Yakobchuk23-May-14 0:51
Sasha Yakobchuk23-May-14 0:51 
QuestionUbuntu build Pin
Member 1049108228-Dec-13 3:53
Member 1049108228-Dec-13 3:53 
AnswerRe: Ubuntu build Pin
Sasha Yakobchuk20-Jan-14 8:41
Sasha Yakobchuk20-Jan-14 8:41 
QuestionChange or set user name or ID Pin
DanielCostaBR13-Sep-13 4:59
DanielCostaBR13-Sep-13 4:59 
AnswerRe: Change or set user name or ID Pin
Sasha Yakobchuk16-Sep-13 7:22
Sasha Yakobchuk16-Sep-13 7:22 
QuestionSend Binary Message Pin
PhilChung29-Aug-13 21:00
PhilChung29-Aug-13 21:00 
AnswerRe: Send Binary Message Pin
Sasha Yakobchuk2-Sep-13 0:03
Sasha Yakobchuk2-Sep-13 0:03 
GeneralRe: Send Binary Message Pin
ykachanov12-Mar-15 6:41
ykachanov12-Mar-15 6:41 
Questiondisplaying all participants Pin
nazrin yusnaidi2-Jul-13 20:11
nazrin yusnaidi2-Jul-13 20:11 
AnswerRe: displaying all participants Pin
Sasha Yakobchuk3-Jul-13 20:34
Sasha Yakobchuk3-Jul-13 20:34 
Questiontwo messages in parallel Pin
RandSirpi24-Apr-13 7:12
RandSirpi24-Apr-13 7:12 
AnswerRe: two messages in parallel Pin
Sasha Yakobchuk24-Apr-13 21:43
Sasha Yakobchuk24-Apr-13 21:43 
GeneralMy vote of 5 Pin
jgcarcamo29-Jan-13 11:45
jgcarcamo29-Jan-13 11:45 
Questionuser id Pin
Daniel Hyuuga23-Jan-13 17:36
Daniel Hyuuga23-Jan-13 17:36 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.