WYSIWYG

http://kufli.blogspot.com
http://github.com/karthik20522

Friday, March 16, 2012

SignalR - Web Sockets

SignalR is a cool asynchronous signaling library for ASP.NET to build real-time applications or for any push based notifications. SignalR is similar to Socket.IO or NowJS. Before the workd of web-sockets, there was Comet which was basically a long-held HTTP requests.

SignalR can be downloaded from NuGet [http://www.nuget.org] and downloading the files manually from Git [https://github.com/SignalR/SignalR]

More descriptive information on SignalR can be found at Scott Hanselman’s site [http://www.hanselman.com/blog/AsynchronousScalableWebApplicationsWithRealtimePersistentLongrunningConnectionsWithSignalR.aspx]

To get SignalR up and running following are few todo’s on server side:

Step 1 is to create a PersistentConnection class like follows

1
<br /> using SignalR;<br /> using System.Threading.Tasks;<br /><br /> namespace Services<br /> {<br />  public class MyConnection : PersistentConnection<br />  {<br />   protected override Task OnReceivedAsync(string clientId, string data)<br />   {<br />    // Broadcast data to all clients<br />    return Connection.Broadcast(data);<br />   }<br />  }<br /> }<br />

Note that Connection.Broadcast would send the data to all connected clients. If you need to sent to a particular client you can use the Send method.

Send(string clientId, object value);

Note Send requires the intended clientId. If you were to be building a chat client or a custom push service, you probably would be storing clientId’s somewhere in a local object or a central repo.

Other useful functions that PersistantConnection class provides that can be overridden:

1
<br /> public void AddToGroup(string clientId, string groupName);<br /> protected virtual void OnConnected(HttpContextBase context, string clientId);<br /> protected virtual Task OnConnectedAsync(HttpContextBase context, string clientId);<br /> protected virtual void OnDisconnect(string clientId);<br /> protected virtual Task OnDisconnectAsync(string clientId);<br /> protected virtual void OnError(Exception e);<br /> protected virtual Task OnErrorAsync(Exception e);<br /> protected virtual void OnReceived(string clientId, string data);<br /> protected virtual Task OnReceivedAsync(string clientId, string data);<br /> public void RemoveFromGroup(string clientId, string groupName);<br /> public void Send(object value);<br /> public void Send(string clientId, object value);<br /> public void SendToGroup(string groupName, object value);<br />



Step 2 is to add the routes if you are using MVC. This route needs to be registered in Global.asax
1
<br /> protected void Application_Start()<br /> {<br />  Bootstrapper.Run();<br />  RouteTable.Routes.MapConnection<Services.MyConnection>("Notification", "Notification/{*operation}");<br />  RegisterRoutes(RouteTable.Routes);            <br />  AreaRegistration.RegisterAllAreas();<br /> }<br />


That’s pretty much it on the server side

Step 3, if the intended client is a web browser it’s as easy as follows:




That’s all is to SignalR setup. A chat type client would require some sort of client association on the server side to keep communication private.

But what about non-browser based apps, like a console app or a windows service. SignalR has libraries for those too, can be downloaded from NuGet.

In real world applications, no project ends up with a single instance, single class project. In real world apps, there are many class’s and class’s are initialized and disposed all the time. In this scenario opening and closing a SignalR connection or instializing a new SignalR object is a wrong approach since you want be connected to the server all time.

One way to keep the connection persistant is to create a static signalR object, in following case it’s a singleton class
1
<br /> using SignalR.Client;<br /><br /> public sealed class PushClass<br />    {<br />        private static volatile PushClass instance;<br />        private static object syncRoot = new Object();<br />        private Connection broadcastConnection;<br /><br />        private PushClass() <br />        {<br />            broadcastConnection = new Connection("http://localhost/Notification");<br />            broadcastConnection.Start().Wait();<br />        }<br /><br />        public static PushClass  Instance<br />        {<br />            get<br />            {<br />                if (instance == null)<br />                {<br />                    lock (syncRoot)<br />                    {<br />                        if (instance == null)<br />                            instance = new PushClass();<br />                    }<br />                }<br /><br />                return instance;<br />            }<br />        }<br /><br />        public void Broadcast(string msg)<br />        {<br />            broadcastConnection.Send(msg);<br />        }<br />    }<br />


A calling class can get an the instance of the above PushClass
1
<br /> var pushInstance= PushClass.Instance;<br /> pushInstance.Broadcast(“blah”);<br />


Another way but fancier way to achieve the same persistant effect could be like follows:

1
<br />public class PushClass<br />{<br />public static void Broadcast(string data)<br />{<br />try<br />{<br />var client = new SignalR.Client.Connection("http://localhost/Notification");<br />var tx =    client.Start().ContinueWith(t =><br />{<br />if(t.IsFaulted)        Console.WriteLine(t.Exception.GetBaseException());    client.Send(data).ContinueWith(t2 =>{ if(t2.IsFaulted)        Console.WriteLine(t2.Exception.GetBaseException());<br />});<br />});<br />tx.ContinueWith(y =>{<br />if (y.IsFaulted  || y.Exception != null)<br />Console.WriteLine(y.Exception.Message);<br />});<br />tx.Wait();<br />}<br />catch(Exception ex)<br />{<br />Console.WriteLine(ex.Message);<br />}<br />}<br />}<br />


The calling class can do the following:
1
<br /> Task asyncSignalr = Task.Factory.StartNew(() => PushClass.Broadcast("blah");<br /><br /> asyncSignalr.ContinueWith(t =><br />     {<br />      if (t.IsFaulted)<br />       Console.WriteLine(t.Exception.Flatten().ToString());<br />     });<br />

Labels: ,