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.
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):
- Download the appropriate binaries and copy into a folder/directory
- Run wsSocket.exe - click 'Allow' if firewall comes up
- Open testHTML1.html in your web browser
- 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
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
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();
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
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:
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.
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
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
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
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.
<code>private void</code> OnAcceptConnection(object param)
{
------------
------------
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;
-----------
-----------
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);
}
}
----------
----------
}
}
}
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