Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / web / HTML5

Low latency html5 WebSocket Server to stream live and computed data to web browser grid (a flavor of SignalR)

4.91/5 (69 votes)
18 Aug 2015CPOL13 min read 170.8K   5.3K  
This is a HTML5 WebSocket service that streams live, real-time data to browser grid control. This service is capable of automatically computing math expressions on the fly (dynamically at runtime)

Downloads:

Quick Demo (32 bit version)
Quick Demo (works in any flavor of Windows OS)
Source code (Compiled in VS2012, can be auto-migrated to other versions of Visual studio easily)

Introduction

Live updates to browser is an important scenario in today's world.  All businesses (Banking, Trading, Healthcare, Retail etc..) rely heavily on internet/browser based apps to reach their esteemed customers.  Be it stock quote, news, mail - live updates bring agility to your web application.  This article is about html5 WebSocket service that streams real time, live data to browser grid.  Also, this service can compute complex math expressions (formula) dynamically (at runtime.)  Say, for example, you need to live update items such as Stock name, symbol, quantity, price, position (short/long) at real time to user browsers -- this service can stream it to browser grid.  This service can handle it better than AJAX/ COMET based grids as it uses low latency TCP communication using html5 WebSocket.   If you would like to add few more items that are computed based on other items in your data - well, this websocket server takes up that work for you behind the scenes.
Let’s consider, you would like to add a new item Total cost (where Total Position = price * quantity * Long/Short), this service computes it automatically.  Given the math expressin formula, this service performs the computations behind the scenes at runtime.  If all this is not enough, it can add new formulas on the fly.  Say, you deployed this grid to production.  Someone thinks your formula is wrong and needs immediate change.  Just modify the formula at server side.  All client browsers will see live updates with right formula.  Isn’t this nice. 

CSV to Web browser using html5 websocket

Fig 0: Explains CSV file based data and computed data based on math expression is streamed real-time through low latency html5 websocket to client browser

Quick Demo

There are two Quick Demo binaries included with this article:

How to execute demo binaries

Common steps for checking out both demo(s):

  1. Download the appropriate binaries and copy into a folder/directory
  2. Run wsSocket.exe - click 'Allow' if firewall comes up
  3. Open testHTML1.html in your web browser
  4. Click ‘Stream’ button in browser web page
Smart data gridGrid with Formula

This demo is intended to showcase update from CSV file to Browser.  Follow the steps (1) through (4) as above.  Then, follow below steps

  • Open test.csv file in an editor (such as TextPad, Notepad). 
  • Modify the value in the CSV file and save it. 

You will see the update streaming from CSV file to browser.  The most interesting part is, add a new math expression/ formula column in test.csv file (sample included).  You will see the math expressinon automatically computed by the html5 websocket server and updated at the browser grid

Grid with Random number push:

No further steps required for seeing this demo.  This demo is intended to showcase real-time, live updates streaming from websocket service to client browser.  Random numbers are generated by websocket service and pushed to browser using Html5

Screen shots of demo

Grid with on-the-fly Formula update demo

Image 2

Fig 1: Data updated in CSV file at server side is getting streamed to web browser at client side.  Note the formula column, which do not have data in CSV file, is automatically computed by websocket service and streamed to web browser automatically

Grid with random number push demo

Image 3

Fig 2: Random number push using low latency html5 stream

Using the code

Using this websocket service in your application is pretty simple. 

  • Download webSocket service source code (click here) and save it to your desired folder/ directory
  • Compile the project webSocket using Visual Studio 2012 (or other versions of Visual studio compiler).  This server code compiles as dynamic library (webSocket.dll)
  • In your application, add webSocket.dll as reference
    • In your Visual Studio application project, right click References.  Click Add reference > browse and select the file webSocket.dll from the directory that you just compiled in step above
  • Add below code at top of your c# file
using wsSocket;
  • Then, you can start streaming with below four lines of code
<code>string </code>data = @"-1,-1, 4,5,
              Item_1, Item_2, Item_3, Computed_1 = Item_1 + Item_2, Computed_2 = Item_2 + Item_3,
              1,2,3,,,
              6,7,8,,,
              9,1,0,,,";              

         <code>html5Stream </code>wSock = new <code>html5Stream</code>();
         wSock.setStreamData(data);
         wSock.startServer();
// Clients will get update whenever setStreamData method is called
Explanation of above code

First line assigns variable data with streaming content.  Details about this content is explained later.  Next line, creates an object of type html5Stream, named wSock.  Then startServer method is called to start the WebSocket server.  Remember, startServer is a blocking call.  So if you are looking for to stream real-time, live data, then, you need to call setStreamData in separate thread

Output in chrome

Image 4

Fig 3: Output of above three lines of code in google chrome browser

Explanation of data passed:

First line: -1, -1, 4, 5
-1,  -1 - Reserved for future use
4 - Number of rows
5 - Number of columns

Second line: Item_1, Item_2, Item_3, Computed_1 = Item_1 + Item_2, Computed_2 = Item_2 + Item_3
Item_1, Item_2, Item_3 - Column headers titles
Computed_1, Computed_2 - Header tile and formula to compute these columns.  Note these columns are not filled in next data lines

All other lines: Data that gets to browser.  Please note, the data for the formula fields are not filled.  They are auto computed by the smart grid

Websocket Security

html5 websocket is an evolving standard.  Security is one of the key factor to consider while using websocket.  No enterprise wants the data to be seen by someone in the middle.  Here are few good pointers to consider:

Secure Web socket:  For production, make it mandatory to use wss (secure websocket) instead of ws.  This protects you from attacks such as man in the middle

Client input:  As websocket is TCP connection, make sure to validate the buffer you receive from client.  Such validation may be little overhead in your design.  But it provides you much bigger benefit of attacks such as Sql Injection, buffer overrun etc

Avoid exposed service:  Avoid exposing an another service throug websocket without proper authentication/authorization in place.  Websocket as of now do not support authenticaion/authorization.  The http connection need to perform this work before handling stepping up connection to websocket.  

Do not believe Origin headers:  Websocket connection have origin connection detail in the header info.  Do not make serious decisions based on this origin information.  A malicious client can pretend as a standard web browser

How it works

Ajax, a brief overview

Browser works as a Request-Response client.  Typically, browser client requests resources (such as html) from a server.  Server (a.k.a Web servers) return resources. Case closed (i.e., connection closed!).  As more and more businesses started moving their applications to browsers, new scenarios (use-cases) desired responsive web application.  We all love google suggestions when we type in their search box, right.  That's good usage of AJAX pattern.  Here is a brief explanation of AJAX:

Image 5

Fig 4: Simple explanation of how AJAX works

As in above picture, AJAX calls are made by browser behind the scenes, i.e., part of webpage is updated without reloading the entire page.  Typically AJAX messages are small.  AJAX calls use a special type of request called XMLHttpRequest.  When response received from server, this information is presented to user without refreshing the entire page.  For user, it appears as if the web page responds instantaneously without a call to server.  AJAX is good for user created events.  Here in the above explanation, user typed into a web page (such as google search box) to initiate AJAX call to server.  Consider another use-case, say, a live data grid use-case: AJAX is not a suitable solution.  Because in live data feed, server creates the event.  That is, server gets to know when the new data arrives. Clients making timer-based AJAX calls unnecessarily increases network traffic, let alone, dries up all server resources.  If the data passed is big, then, sever goes to a state where it cannot accept any new connections.  

COMET, a brief overview

Timer-based AJAX calls clogs server with unnecessary calls.  To solve this, COMET (a.k.a Reverse AJAX) was actively used.  Reverse AJAX/COMET lets server push data instead of client polling for data in specific interval.  This is perfect for server initiated events use-case such as live-feed to browser.

Image 6

Fig 5: Simple explanation of how COMET / Reverse AJAX works

As in above picture, Client calls to server are put on hold by server.  Hold is different from disconnect.  Holding a call means server keeps these open connections as pending.  When new data becomes available for update, server sends response through these pending connection.  This is much better than AJAX, but not perfect.  This suffers from inability to handle error reliably, restriction on number of connection, latency issues, etc.  

HTML5 websocket, a low latency, reliable communication standard

In simple terms, the very same http connection between browser and your server is upgraded to TCP, a reliable protocol.  A nice, simple and clean approach.  This allows full duplex communication between browser and server.  This cuts all the middle-man (I mean extra layer) involved in the process.   HTML5 provides a nice low-latency, reliable tcp communication between browser and server.  More importantly, as this is a standard, the requirement is: it shall work in all browsers.  Every firm is working to embrace HTML5 today

Image 7

Fig 6: Simple explanation of how html5 hand shake is established between client browser and server

How html5 websocket works

html5 websocket works by a client initiating a connection to server.  Typically, a web brower attempts to connect to a server exposing websocket interface.  Initial handshake is as simple as shown in below picture.  When we meet a person, we say, "How are you" and shake their hand.  While shaking hand, the opponent would say, Hello, nice meeting with you.  After this initial handshake, conversation starts.  Both parties can initiate new topic.  Similarly, after initial handshake the http protocol is upgraded to TCP.  Then, the communication becomes low latency, full duplex TCP as shown in below Fig 7

Image 8

Fig7: html5 websocket handshake steps

For example, here is a sample from chrome browser.  Below is what a brower sends to handshake with a server of interest

GET /service HTTP/1.1
Host: localhost:8080
Connection: Upgrade
Pragma: no-cache
Cache-Control: no-cache
Upgrade: websocket
Origin: null
Sec-WebSocket-Version: 13
User-Agent: Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2272.101 Safari/537.36
Accept-Encoding: gzip, deflate, sdch
Accept-Language: en-US,en;q=0.8
Sec-WebSocket-Key: 198ol8E3A0P/DPNlSOq4XA==
Sec-WebSocket-Extensions: permessage-deflate; client_max_window_bits

// copied from chrome browser request

And the below is what a server reply:

HTTP/1.1 101 Switching Protocols\r\n
Upgrade: websocket\r\nConnection: Upgrade\r\n
Sec-WebSocket-Accept: n72SJe/JO84rHrLBkigtRDc+6QA=\r\n\r\n

Intentionally, I have this '\r\n' readable in the message.  Note, the last line ending with two of them.  They are important for the browser to accept server's handshake.  After this handshake, server and client are ready for full duplex, low latency communication

Explanation of code

Let’s look at how basic html5 connection is established.  html5Stream.StartServer is a simple method that accepts client connection and processes it asynchronously.  This method also spawns a separate thread that processes all client publishing work.  We will take a look at this method later.  For now, this method is not html5 significant.  It’s plain simple socket accept, nothing more.  Html5Stream.AcceptConnection performs the necessary html5 specific handshake with client.  

C#
<code>private void</code> OnAcceptConnection(object param)

{

    ------------ // code skipped for brevity

    ------------
   int <code>rcvBytes </code>= client.Receive(buffer);
   headerResponse = getStrFromByte(buffer, rcvBytes);
   if (client != null)
   {
       prepareClientResponse(headerResponse, ref sendBuffer);
       client.Send(sendBuffer);
       rcvBytes = client.Receive(buffer);
       string dataFromClient = decodeData(buffer);
       subscribe(client);
       eventDataAvailable.Set();

   }
   -------

   -------
}

This function performs the important handshake necessary for upgrading http to tcp connection.  
Steps to establish html5 handshake:

  • Create a socket and bind to a specific port. 
  • Once the connection is established, browser sends in a request as below. Older browser may send little different data.  Some different browser may send slightly different data.  So if you are writing ‘pro’ grade code, you need to address all of these cases.  Please refer Reference (2)
  • Based on handshake details, answer key needs to be generated.  Take a look at html5Stream.prepareClientResponse method.  This method prepares a handshake response from server to connecting client.  Handshake response looks as below.  If you read, surprisingly, it’s quite simple.  That’s it, handshake complete.  Now on, client and server can enjoy low latent, reliable, full duplex communication between each other.

Now, you are probably wondering, how simple this is, correct?  


Everything should be made as simple as possible, but not simpler.
                                                                             -- Albert Einstein


So to avoid being simpler the messages are not raw.  Communication messages between server and client employ data framing (remember, Einstein said, not simpler).  Just kidding, as in reference (3), Data framing is employed to avoid confusing network intermediaries and for security reasons.  Even for SSL/TLS this masking is performed.  Fortunately, It’s not that difficult to implement.  I implemented the part that we need for our grid to work (brevity).  Take a look at methods html5Stream.encodeData and html5Stream.decodeData.  

Now that handshake and encoding is complete, we are ready to stream data.  html5Stream.publishAll method does this work.  It waits on dataReceived event.  This event is set when user calls html5Stream.setStreamData.  i.e., when data is ready to be published to client.  The available data is encoded and sent to client.  Each client in the list is enumerated and sent with new update.

private void publishAll(object dummy)
{
  byte[] byteToSend = null;

  ----------- // lines skipped for simplicity
  -----------
  while(!shutDown)
  {

    bool dataReceived = eventDataAvailable.WaitOne(1000);
    if (dataReceived)
    {

      encodeData(getStreamData(), ref byteToSend);
      lock (lockObj)
      {
        HashSet<Socket>.Enumerator enumSockets = socketsToUpdate.GetEnumerator();
        SocketAsyncEventArgs sendData = new SocketAsyncEventArgs();
        sendData.SetBuffer(byteToSend, 0, byteToSend.Length);
        while (enumSockets.MoveNext())
        {
            enumSockets.Current.SendAsync(sendData);
        }
      }
    ----------
    ---------- // lines skipped for brevity
   }

  }

}

Points of Interest

=>  This implementation is aimed for learning.  So code brevity and simplicity is exercised all across the code for easy understanding rather than performance.  An enterprise grade source will use a totally different data structure, interface, error/exception handling etc.  In case if you are interested in professional/ enterprise grade library get in touch with me


=>  A free file streamer is added with sample project.  Open the CSV file, add new columns, rows.  Also try adding new formula.  It’s quite interesting to see the dynamic update getting generated to browser screen on other machine.  Adding new computed columns has never been easier than this.

My sincere thanks to..

I would like to thank you for reading this article.  I would love to hear back from you.  If you like this article, please vote for this article at top of this page and leave your comment at the bottom. 

Many thanks to muParser, the math parsing library used in this grid.  It's pretty fast expression parse library. Thanks to https://creately.com and https://cacoo.com.  I used their online tool for making diagrams for this article

References

History

  • 24th March 2015 - First version
  • 25th March 2015 
    • Modified heading to include steaming service
    • Added meaningful References titles
  • 29th March 2015 - Updated pictures to match screen size
  • 30th March 2015 - Updated decode logic in source code.  Fixed bug in the decode logic.  Commented earlier code for reference
  • 1st April 2015 - Added section 'Websocket security'
  • 7th April 2015 - Moved section 'Websocket security'
  • 16th April 2015 - Added new section 'How html5 websocket works'
  • 18th April 2015 - Modified the picture fig0 issue in previous version
  • 18th Aug 2015 - Added 32 bit version demo as requested 

 

License

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