Tuesday 20 March 2012

LongPolling


In Ajax technique client send the request to the web server and server responds to the client with Json/xml data.

Where in longpolling the request is kept alive at the server and gives the response when data exists.After receiving the request client should make another request for further updates.

In .Net there is IHttpAsyncHandler which is mainly used for longpolling implementation.

->Create a Handler class and inherit it from IHttpAsyncHandler.

   public class Handler : IHttpAsyncHandler
    {
        //Static varible of StateManager.
        private static StateManager stateManager;

        //Constructor.
        static Handler()
        {
            //Intilizing object.
            stateManager = new StateManager();
        }

        //StateManager.
        public static StateManager StateManager
        {
            get { return stateManager; }
        }

        //Initiates an asynchronous call to the HTTP handler.
        public IAsyncResult BeginProcessRequest(HttpContext context, AsyncCallback cb, objectextraData)
        {
            //Calling the ProcessRequest.
            return stateManager.ProcessRequest(context, cb, extraData);
        }

        // Provides an asynchronous process End method when the process ends.
        public void EndProcessRequest(IAsyncResult result)
        {

        }

        //Gets a value indicating whether another request can use the System.Web.IHttpHandler instance.
        public bool IsReusable
        {
            get { return false; }
        }

        //Enables processing of HTTP Web requests by a custom HttpHandler that implements the System.Web.IHttpHandler interface.
        public void ProcessRequest(HttpContext context)
        {

        }
      }

->Create the StateManager class.

   public class StateManager
    {
        //List of clients.
        List<AsyncRequestState> lstClients = new List<AsyncRequestState>();

        public IAsyncResult ProcessRequest(HttpContext context, AsyncCallback cb, object extraData)
        {
            //Get the date value from querystring.
            string date = context.Request["date"];

            //Creating AsyncRequestState object.
            AsyncRequestState requestState = new AsyncRequestState(context, cb, extraData);

            //Adding new client.
            lstClients.Add(requestState);

            //Return IAsyncResult.
            return requestState;
        }

        //Send message to all clients.
        public void SendMessage(string message)
        {
            //Send message to all clients.
            foreach (AsyncRequestState item in lstClients)
            {
                //Check the status of operation
                if (item.IsCompleted == true)
                {
                }
                else
                {
                    //Write the message to response.
                    item.context.Response.Write("<b>Updates from Servce:</b>   " + message);

                    //Flush the message to user.
                    item.context.Response.Flush();

                    //Complete the request.
                    item.CompleteRequest();
                }
            }

            //Clear the client list.
            lstClients.Clear();
        }
      }

->Create the AsyncRequestState class and inherit it from IAsyncResult.

  //Inherit a class from IAsyncResult which Represents the status of an asynchronous operation.
    public class AsyncRequestState : IAsyncResult
    {
        //Constructor.
        public AsyncRequestState(HttpContext con, AsyncCallback cb, object extraData)
        {
            //Httpcontext.
            this.context = con;

            //Callback.
            this.cb = cb;

            //Extradata.
            this.extraData = extraData;
        }

        public HttpContext context;

        public string clientName;

        private AsyncCallback cb;

        private object extraData;

        private bool isCompleted = false;

        internal void CompleteRequest()
        {
            isCompleted = true;
            if (this.cb != null)
                this.cb(this);
        }

        #region InterfaceImplementation

        public object AsyncState
        {
            get
            {
                return (this.extraData);
            }
        }

        public WaitHandle AsyncWaitHandle
        {
            get
            {
                return null;
            }
        }

        public bool CompletedSynchronously
        {
            get
            {
                return (false);
            }
        }

        public bool IsCompleted
        {
            get
            {
                return (isCompleted);
            }
        }

        #endregion
    }

->Register the Handler in Web.config file under System.web configuration.
   <httpHandlers>
      <add verb="POST" path="Handler.ashx" type="LongPolling.Handler, LongPolling"/>
   </httpHandlers>
   If you publish in IIS you should add this configuration under System.webserver      configuration.
    <handlers>
      <add name="Handler" verb="POST" path="Handler.ashx" type="CricgambleLongPolling.Handler, CricgambleLongPolling"/>
    </handlers>

->Create a login.html page which is used to polling the server.
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <title></title>
    <script src="Scripts/jquery-latest.js" type="text/javascript"></script>
</head>
<body>
    <div>
        Click the button to get updates.
        <input type="button" value="GetUpdates" onclick="hook();" /></div>
    <div id="updates">
    </div>
</body>
<script type="text/javascript">
    //Hook the request.
    function hook() {

        //Url of the service.
        var url = 'Handler.ashx?date=' + new Date();

        //Jquery ajax function to call the service.
        $.ajax({
            type: 'POST', //Method type.
            url: url,     //Url of handler.
            beforeSend: function (xhr) {
                //Here we have to pass the Content-Type header to get the date value(Which is passed in the query string).
                xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
            },
            error: function (err) {
                //Error event.
                alert('error occured');
            },
            success: function (res) {

                //Display the updates.
                $("#updates").append($("<div/>").html(res));

                //Hook the request after a successfull response.
                hook();

            }
        });
    }
</script>
</html>

->Create a wcf service which is used to send data to the handler.

namespace LongPolling
{
    [ServiceContract]
    public interface IService
    {
        [OperationContract]
        void SendMessage(string message);
    }
}

namespace LongPolling
{
    public class Service : IService
    {
        public void SendMessage(string message)
        {
            //Calling the SendMessage method to send updates.
            Handler.StateManager.SendMessage(message);
        }
    }
}

->Add service reference to the wcf service.
->Create SendMessage.aspx page.
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title></title>
</head>
<body>
    <form id="form1" runat="server">
    <div>
        Enter Message.
        <asp:TextBox ID="txtMessage" runat="server"></asp:TextBox>
    </div>
    <div>
        <asp:Button ID="btnSend" runat="server" Text="SendMessage"
            onclick="btnSend_Click" /></div>
    </form>
</body>
</html>
->In code behind.
namespace LongPolling
{
    public partial class SendMessage : System.Web.UI.Page
    {
        //Create client object.
        ServiceReference.ServiceClient objClient = new ServiceReference.ServiceClient();

        protected void Page_Load(object sender, EventArgs e)
        {

        }

        protected void btnSend_Click(object sender, EventArgs e)
        {
            if (txtMessage.Text != "")
            {
                //Calling the service function.
                objClient.SendMessage(txtMessage.Text.ToString());
            }
        }
    }
}


Now run the application open the login.html page and click on GetUpdates button.Open SendMessage.aspx in the new browser window enter some text and click on SendMessage button which will update all the clients.

In long polling different browsers have different bahaviors.You should write code based on the browser.

If you have multiple clients you should implement some threading concepts in the server side,then  you can’t miss the single piece of data.


Let me know, if you have any feedback. Mail me for source code. Enjoy reading my articles…
sekhartechblog@gmail.com

No comments:

Post a Comment