Click here to Skip to main content
Click here to Skip to main content

Building a basic HTML5 client/server application

By , 25 Sep 2012
 

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.

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:

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.
    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.
    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.
    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.

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:

	
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.

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.

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.
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.

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.

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

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.

Any questions, remarks, and comments are welcome.

History

  • 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)

About the Author

Alexander Iacobciuc
Software Developer RTSoft
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.

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
Questiontwo messages in parallel [modified]memberRandSirpi24-Apr-13 7:12 
Excellent example. Very informative.
 
Note 5.
 
A question. How to send and receive messages in each case two simultaneously being very websocket long.sans whether the one following the other. I try to understand how Boost can handle in parallel two (or more) messages instead of displaying them one after the other. I try to make sure that when I send a message websocket, another websocket message can either become the priority is either interrupt or pause.

modified 24-Apr-13 13:27pm.

AnswerRe: two messages in parallelmemberAlexander Iacobciuc24-Apr-13 21:43 
A websocket message is a series of frames. That fact allows sending a message that is of unknown size when the message is started without having to buffer that message. In other words, a message is not ready until we received the last frame of that message. More details on that subject can be found in websocket RFC: http://tools.ietf.org/html/rfc6455#section-5.4[^]
 
There is no such thing as a priority, in case you need it, you should add it on the application level.
GeneralMy vote of 5memberjgcarcamo29-Jan-13 11:45 
excellent example!!!
Questionuser idmemberDaniel Hyuuga23-Jan-13 17:36 
Which part on the server that responsible to generate user id like user1234
AnswerRe: user idmemberAlexander Iacobciuc13-Feb-13 22:37 
The current version just uses the port number to form the user name: "user" + port number.
QuestionIs there a version of win32 api?memberevalhero18-Dec-12 2:08 
Does this chat page written as a Windows program can use the win32 api it?
AnswerRe: Is there a version of win32 api?memberAlexander Iacobciuc18-Dec-12 3:37 
As long as client communicates with server using the WebSocket protocol, it can use any API for networking.
 
Regards,
Alexander
GeneralRe: Is there a version of win32 api?memberevalhero18-Dec-12 14:02 
Alexander Iacobciuc a really nice guy
GeneralMy vote of 5memberhandychang1-Oct-12 0:15 
Very good.
GeneralMy vote of 5memberevalhero20-Sep-12 23:51 
優秀的範例
GeneralRe: My vote of 5memberAlexander Iacobciuc21-Sep-12 1:15 
谢谢 Smile | :)
QuestionI would like to ask you what way the website server?memberevalhero20-Sep-12 23:18 
http://www.boost.org/doc/libs/1_51_0/doc/html/boost_asio/examples.html

I use the HTTP Server example programs,
http://epb.idv.tw/chat.html
, I put on the Internet chat is invalid example
In order to let the other users try to connect
I would like to ask you what way the website server
BOOST can set up it?

AnswerRe: I would like to ask you what way the website server?memberAlexander Iacobciuc21-Sep-12 1:04 
Hi evalhero,
 
To setup the chat server you need a PC with public IP address. Then you need to pass that IP address as an input parameter to the chat server app, including port number. Also you need to change the chat server address in the chat.html file (line 132).
 
For example, if you have a PC with public IP address 122.146.199.190 and you want to run the chat server on port 7777, then you need:
1. start the chat server app using the following command line:
server 122.146.199.190 7777

2. change the chat.html file, line 132, to:
var wsUri = "ws://122.146.199.190:7777";
 
Regards,
Alexander
GeneralRe: I would like to ask you what way the website server?memberevalhero22-Sep-12 23:42 
Thank you, chat rooms work
GeneralMy vote of 5memberRad.Application15-Sep-12 21:53 
good
QuestionTime outmemberDavide Zaccanti15-Sep-12 21:20 
server running on a W2008 Server
2 clients running on W8 64bit with ie10
 
both work and sended message update both clients.
 
wait a couple of minutes without any action.
 
Server is still running without any messages or warnings
If you insert a text into edit control of chat client and hit enter no action at all
Re-connecting (CTRL+F5) leads to a disconnect remote host message on server, all previous messages are restored on client and new messages are shown both on this and on the other client.
On the other client you can receive messages but you cannot send.
Refreshing also second client restore initial working state.
 
Do you have a suggestion to avoid this ? It is related only to IE ?
Sending a 'keep-alive' package from server can be a solution ?
 
TIA.
AnswerRe: Time out [modified]memberAlexander Iacobciuc19-Sep-12 20:22 
The thing is that IE 10 sends pong frames, which were not handled correctly.
Now it is fixed.
 
Thanks,
Alexander

modified 26-Sep-12 1:12am.

BugMessage lengthmemberDavide Zaccanti15-Sep-12 20:59 
As long as you insert more than 100 chars on html client a disconnection happens (disconnecte from remote host)
 
Thank you for sharing this idea (maybe you can add also a C++ client in future updates?)
GeneralRe: Message lengthmemberAlexander Iacobciuc19-Sep-12 20:02 
Hi Davide,
 
Thank you very much for feedback!
 
There was an error in the message length calculations. It is fixed now.
 
Regarding the C++ client, well, the goal of this article is to show a way of communication between a web app and the server. If you are interested in C++ client and server, then you can take a look at the Chat example.
 
Thanks,
Alexander
Questionubuntu errormemberevalhero12-Sep-12 19:00 
g++ -o chatserver main.cpp dataframe.cpp dataframe_parser.cpp reply.cpp request_handler.cpp request_parser.cpp room.cpp server.cpp session.cpp chat.hpp dataframe.hpp dataframe_parser.hpp header.hpp participant.hpp reply.hpp request.hpp request_handler.hpp request_parser.hpp room.hpp server.hpp session.hpp -lboost_system -lboost_signals -lpthread -ldl -std=c++0x
 
boost Version = 1.51
c++0x
 
request_handler.cpp: In static member function ‘static std::string websocket::http::request_handler::to_base64(const std::vector<unsigned char>&)’:
request_handler.cpp:86:56: error: declaration of ‘buff’ as array of references
chat.hpp:64:17: error: ‘request_handler’ does not name a type
chat.hpp: In constructor ‘websocket::applications::chat::server::server(boost::asio::io_service&, const endpoint&)’:
chat.hpp:29:51: error: use of deleted function ‘boost::asio::io_service::io_service(const boost::asio::io_service&)’
In file included from /usr/include/boost/asio/basic_io_object.hpp:19:0,
                 from /usr/include/boost/asio/basic_socket.hpp:19,
                 from /usr/include/boost/asio/basic_datagram_socket.hpp:20,
                 from /usr/include/boost/asio.hpp:20,
                 from chat.hpp:13:
/usr/include/boost/asio/io_service.hpp:187:7: error: ‘boost::asio::io_service::io_service(const boost::asio::io_service&)’ is implicitly deleted because the default definition would be ill-formed:
/usr/include/boost/noncopyable.hpp:27:7: error: ‘boost::noncopyable_::noncopyable::noncopyable(const boost::noncopyable_::noncopyable&)’ is private
/usr/include/boost/asio/io_service.hpp:187:7: error: within this context
chat.hpp:29:51: error: no matching function for call to ‘boost::asio::basic_signal_set<>::basic_signal_set()’
chat.hpp:29:51: note: candidates are:
/usr/include/boost/asio/basic_signal_set.hpp:177:3: note: boost::asio::basic_signal_set<SignalSetService>::basic_signal_set(boost::asio::io_service&, int, int, int) [with SignalSetService = boost::asio::signal_set_service]
/usr/include/boost/asio/basic_signal_set.hpp:177:3: note:   candidate expects 4 arguments, 0 provided
/usr/include/boost/asio/basic_signal_set.hpp:147:3: note: boost::asio::basic_signal_set<SignalSetService>::basic_signal_set(boost::asio::io_service&, int, int) [with SignalSetService = boost::asio::signal_set_service]
/usr/include/boost/asio/basic_signal_set.hpp:147:3: note:   candidate expects 3 arguments, 0 provided
/usr/include/boost/asio/basic_signal_set.hpp:123:3: note: boost::asio::basic_signal_set<SignalSetService>::basic_signal_set(boost::asio::io_service&, int) [with SignalSetService = boost::asio::signal_set_service]
/usr/include/boost/asio/basic_signal_set.hpp:123:3: note:   candidate expects 2 arguments, 0 provided
/usr/include/boost/asio/basic_signal_set.hpp:105:12: note: boost::asio::basic_signal_set<SignalSetService>::basic_signal_set(boost::asio::io_service&) [with SignalSetService = boost::asio::signal_set_service]
/usr/include/boost/asio/basic_signal_set.hpp:105:12: note:   candidate expects 1 argument, 0 provided
OK!
root@Ubuntu01:/PhancloudData/App/chat# In static member function ‘static void websocket::http::request_handler::handle_request(const websocket::http::request&, websoct::htt          p::reply&)’^C

AnswerRe: ubuntu errormemberAlexander Iacobciuc15-Sep-12 10:17 
Thanks! Fixed.
 
Regards,
Alexander
GeneralRe: ubuntu errormemberserup24-Apr-13 23:35 
I am trying to build on Ubuntu myself, however I am having trouble with the linking part
could you perhaps write a few Words on where to put the source code and how to make sure that the boost library is where it should be
AnswerRe: ubuntu errormemberserup24-Apr-13 23:44 
just curious -- why do you compile a file called chat.hpp, that one is not in the project ??
GeneralRe: ubuntu errormemberAlexander Iacobciuc7-May-13 21:14 
that was by mistake
QuestionSource Codememberosy3-Sep-12 21:29 
How about sharing the C++ source code?
Incidentally it's a very interesting article.

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

Permalink | Advertise | Privacy | Mobile
Web04 | 2.6.130617.1 | Last Updated 25 Sep 2012
Article Copyright 2012 by Alexander Iacobciuc
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid