Friday 6 September 2013

HTML 5 Web Sockets

HTML 5 introduced a new specification for the Bi-Directional communication or the Full Duplex connection between the Server and the client. It is the one of the most awaited technology after AJAX which solves the various problems of connection management and communication between the server and the client.
Web Sockets are basically exposed by JavaScript in HTML 5; also it can work independent of the technologies on Client and Server, almost all the browsers support the HTML 5 Web sockets feature as of now (All the latest versions) and the only restriction on server side is that server should support Web Sockets connection. 

HTML 5 Web Sockets helps in achieving real time communication on stateless model of Web, although earlier we used to achieve this by some conventional techniques like polling, long polling, etc. or with the help of some other libraries like SignalR (Which is nowadays known as ASP.Net SignalR and is also recommended by Microsoft).

To achieve real time communication (required in Gaming applications, chat engines etc.) between client and the server (Stateless model), we actually used to poll the server for the updated response every few seconds (Polling and Long Polling, I will explain both in a moment). The problem with this technique was that for every request that is sent to the server from client, there is a Request and Response object created for that, in addition to this authentication/authorization and many other things comes into picture for each and every request. Also the creating request and Response object in itself is a very heavy process, and the whole page is sent back as response to the client which includes headers and much other unnecessary stuff which becomes overhead. With HTML 5 Web sockets all of these overheads are minimized.

Conventional Techniques of Achieving Real-Time Communication


Polling – Polling is a technique in which Client keeps on sending a HTTP request to the server after a set time of interval and the use the response coming from the server to update the information. So there is always some lag in the display of the updated information and additionally server treats every single request as a new request and thus it includes overhead. One of the Major disadvantages is that even if the update is not available on the server, client will keep polling to the server for updated information. In this technique there is no way you can configure client to make request to the server when update is available because of the stateless model on which the Web works upon.

Long Polling – Long Polling is a technique in which Client keeps on sending a HTTP request to the server and then Server waits for set amount of interval to check if there is any updates available which it needs to send to the client, if so then it created the response and sends the updated response to the client otherwise it sends the response to the client to end the request. It gives us some benefits if the updates are not that frequent but if the updates are very frequent say every few m-seconds or every second then it is of no use over Polling.

To overcome this overhead came, HTML 5 Web Sockets to the rescue. Here I will explain how to work with HTML 5 Web Sockets with a sample application. Apart from all the conventional techniques of achieving Real-Time communication which works on HTTP Protocol, Web Socket Connection and communication works on Web Socket protocol.

Web Socket Attributes:


Socket.readyState – This tells us the actual state of the Socket, if the connection is opened, closed or not. State is defined with the help of Integer values (0 – Connection is not established till now, 1 – Connection Established, 3 – Connection is closed)

Web Socket Events:

  1. Open – Fired when connection is opened
  2. Message – Fired when message is received from the server
  3. Error – Fired when there is some error which have occurred in the socket connection
  4. Close – Fired when the connection gets closed

Web Socket Methods:

  1. Send – Used for sending data
  2. Close – Used for closing the connection

Here in this sample I will create a HTML Page on HTML 5 standards and on server side I will create a HTTP Handler to which client will make Web Socket request and this handler will support the Web Sockets request or connections (For this one thing is mandatory that the Server should support or accept the Web Sockets Request). The sample I am creating is a type of Chat Engine (I will let you know how to get it tested without making much effort, Please remember this is a sample application to show you how to use of WebSockets, and is not the best implementation to achieve this chat functionality in real applications, you can always have better design then this for your applications).

It will be good; I think to create a HTTP Handler first. All my .Net/c# code for HTTP Handler will be based upon .Net Framework 4.5 hosted on IIS 8. This is because IIS 8 provides the low level support for Web Socket Connections and .Net 4.5 provide us some managed libraries (System.Web.WebSockets) for Web Socket objects.

Creating a HTTP Handler

Note: You can find detailed comments inline.

Open Microsoft Visual Studio 2012 -> Add New Project (Class Library). Here I have created a project of type class library named “HandlerProject”. I have created a class in this project name “WebSocketHandler” which inherits from “HttpTaskAsyncHandler”, In .Net 4.0 when we used to create a HTTP Handler we used to inherit it from an interface “IHttpHandler”, but now .Net 4.5 provides a way to create a Async handler by just inheriting this base class where we need have to implement “BeginProcessRequest” and “EndProcessRequest” instead we just have to focus on “ProcessRequestAsync”, Also I have used a new feature of .Net 4.5 async/await(If you don’t know about this feature, please refer this link: http://www.codeproject.com/Articles/599756/Five-Great-NET-Framework-4-5-Features as this is out of the scope of my article). In this Class Library we need to import reference of DLL “System.Web”.

Class Library Code:


using System;
using System.Net.WebSockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Web;
using System.Web.WebSockets;

namespace HandlerProject
{
    //Handler Class which is main entry point.
    public class WebSocketHandler : HttpTaskAsyncHandler
    {
        public override bool IsReusable
        {
            get
            {
                return false;
            }
        }

        //Socket Object, Although i have created a Static Dictionary of Scoket objects just to show the sample working. What i do is create this Socket object for each user and
        //keeps it into the dictionary. You can obviously change the implementation in real time.
        private WebSocket Socket { get; set; }

        //Overriden menthod Process Request async/await featur has been used.
        public override async Task ProcessRequestAsync(HttpContext httpContext)
        {
            //task is executed
            await Task.Run(() =>
            {
                //Checks if it is a Web Socket Request
                if (httpContext.IsWebSocketRequest)
                {
                    httpContext.AcceptWebSocketRequest(async delegate(AspNetWebSocketContext aspNetWebSocketContext)
                    {
                        Socket = aspNetWebSocketContext.WebSocket;

                        //Checks if the connection is not already closed
                        while (Socket != null || Socket.State != WebSocketState.Closed)
                        {
                            //Recieves the message from client
                            ArraySegment<byte> buffer = new ArraySegment<byte>(new byte[1024]);
                            WebSocketReceiveResult webSocketReceiveResult = await Socket.ReceiveAsync(buffer, CancellationToken.None);

                            //Here i have handled the case of text based communication, you can also put down your hode to handle byte arrays etc.
                            switch (webSocketReceiveResult.MessageType)
                            {
                                case WebSocketMessageType.Text:
                                    OnMessageReceived(Encoding.UTF8.GetString(buffer.Array, 0, webSocketReceiveResult.Count));
                                    break;
                            }
                        }
                    });
                }
            });
        }

        //Sends message to the client
        private async Task SendMessageAsync(string message, WebSocket socket)
        {
            await SendMessageAsync(Encoding.UTF8.GetBytes(message), socket);
        }

        //Sends the message to the client
        private async Task SendMessageAsync(byte[] message, WebSocket socket)
        {
            await socket.SendAsync(
                new ArraySegment<byte>(message),
                WebSocketMessageType.Text,
                true,
                CancellationToken.None);
        }

        //This message is fired and parent can forget about this, what this method do is gets the message and push it to the different clients which are connected
        protected void OnMessageReceived(string message)
        {
            Task task;

            if (message.IndexOf("JOINEDSAMPLECHAT") == 0)
            {
                WebSocketDictionary.Sockets[message.Replace("JOINEDSAMPLECHAT:", string.Empty)] = Socket;
                foreach (string key in WebSocketDictionary.Sockets.Keys)
                {
                    task = SendMessageAsync(string.Concat(message.Replace("JOINEDSAMPLECHAT:", string.Empty), " Joined Chat."), WebSocketDictionary.Sockets[key]);
                }
            }
            else
            {
                if (message.IndexOf("BROADCAST") == 0)
                {
                    foreach (string key in WebSocketDictionary.Sockets.Keys)
                    {
                        task = SendMessageAsync(message.Replace("BROADCAST:", string.Empty), WebSocketDictionary.Sockets[key]);
                    }
                }
            }
        }
    }
}

Creating HTML Page in a WebSite

Note: You can find detailed comments inline.

Now Add a New Web Site and host it on to IIS 8. The port number on IIS which I have kept for my application is 801. Now add a New page HTML Page in this Web site. Code Given Below:

HTML Page:


<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <title>Web Socket Sample</title>
    <script type="text/javascript">
    
    var webSocket;
    var username;

    //Check is made if the WebSocket are supported on the browser or not and if they are supported then connection is established. As this is the sample i have created the
    //connection here on page load but you can also create the connection on click of the join button.
   function WebSocketTest()
   {
        if ("WebSocket" in window)
        {
            webSocket = new WebSocket("ws://localhost:801/default.Socket");
            webSocket.onopen = function()
            {
                //Connection Opened, if you want to do something while opening connection do it here
            };
        }
        else
        {
            alert("WebSocket NOT supported by your Browser!");
        }
   }

   WebSocketTest();
  
   //When user joins in by clicking in Join button, Message is sent to the server that the user joined in which is broadcasted for every user
   function JoinUser()
   {
        username = document.getElementById('txtUserName').value;
        var joinButton = document.getElementById('btnJoin');
        webSocket.send("JOINEDSAMPLECHAT:" + username);
        username.disabled = true;
        joinButton.disabled = true;
   }
  
   //When the user writes it any message it is broadcasted to every user.
   function SendMessage()
   {
        var message = document.getElementById('txtMessage').value;
        webSocket.send("BROADCAST:" + username + ": " + message);
   }

    //Fired when message is recieved from the server and displays it in the user window.
    webSocket.onmessage = function (evt)
    {
        var messages = document.getElementById('divMessages');
        var received_msg = evt.data;
        messages.innerHTML = messages.innerHTML + received_msg + '</br>';
    };

    //fired when the connection gets closed
    webSocket.onclose = function()
    {
        alert("Connection is closed");
    };

    //Fired when there comes some error in the web socket connection
    webSocket.onerror = funtion (error)
    {
        alert(error.data);
    };

    </script>

</head>
<body>
    Username:
    <input type="text" id="txtUserName" />&nbsp;<input type="button" id="btnJoin" value="Join" onclick="JoinUser();" /><br />
    Message:
    <input type="text" id="txtMessage" />&nbsp;<input type="button" id="btnBroadcaseMessage" value="Broadcast" onclick="SendMessage();" /><br />
    <div id="divMessages">
    </div>
</body>
</html>

Now we need to configure Handler for this WebSite so just open the “Web.Config” file and paste the following code into it for configuring the Handler.

Web.Config:


<configuration>
    <system.web>
      <compilation debug="false" targetFramework="4.5" />
      <httpRuntime targetFramework="4.5" />
    </system.web>
  <system.webServer>
    <handlers>
      <add name="WebSocketHandler" path="*.Socket" verb="*" type="HandlerProject.WebSocketHandler" preCondition="integratedMode"/>
    </handlers>
  </system.webServer>
</configuration>

And with this we are done, we are ready to use this chat application. Please find below the screenshots of the same. What I have done here is I have opened two instances of Internet Explorer and Joined the chat with two different users and you can see the communication going on.

Screenshot:

Also I have attached a sample application code, which you can use. Just download it, Host the Website on IIS and configure the handler, the only thing after that you need to do is if your hosting this sample application or website on some other port(Other than 801), just make changes in HTML Page(change the port number where we are creating Web socket Connection).
Hope it helps in understanding Web Sockets.

No comments:

Post a Comment