Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Asp.Net SignalR Chat Room

0.00/5 (No votes)
14 Mar 2013 8  
This article helps you to understand the ASP.NET SignalR API and create a chat room using it.

Introduction 

Recently Microsoft added some new features in its ASP.NET Web Application Framework. SignalR is one of the prominent feature to build real time application e.g. social application, multiuser games, news weather etc. In real time applications server pushes the content to connected clients instantly it become available. It provides a simple ASP.NET API for creating server-to-client remote procedure calls (RPC) that calls JavaScript functions in client browsers from server-side .NET code. 

The web application works in Request-Response model. Browsers and other user agents make requests and web server provide a response to that request. The response is sent to the delivery address provided in the request by user agent. In this model server can’t make responses without a request. So in real time application server pushed newly available contents/data to the client. You can achieve this feature using ASP.NET SignalR API.

Get Started

For explaining the SignalR Web API, in this article we will create a chat application where you can do group chat and private chat with single user. You require Visual Studio 2012 Express Development Tool for creating this project. You can get more details on http://www.asp.net/signalr

Create a new web application project in visual studio and In Solution Explorer, Right-click the project, select Add > New Item, and select the SignalR Hub Class item. Name the class and press the Add button. It will add the Hub class, required references and scripts.

You can see added script and references by expanding ‘Scripts’ and ‘References’ nodes.

Register Hub URL

For using SignalR API, we need to register ~/signalr/hubs URL. Add global.asax file in your solution and in the Application_Start method register hub url using RouteTable.Routes.MapHubs() command.

 public class Global : System.Web.HttpApplication
 {

    protected void Application_Start(object sender, EventArgs e)
    {
        // Register the default hubs route: ~/signalr/hubs
        RouteTable.Routes.MapHubs();
    }

 }    

Hub Class

ChatHub class must be inherited from the Microsoft.AspNet.SignalR.Hub class. The Hub class exposes some properties and methods. Using Clients property you can communicate with connected clients. You can get the information of client who is calling method using Context and you can also manage groups using Groups property.

public abstract class Hub : IHub, IDisposable
{

  public HubConnectionContext Clients { get; set; }
  public HubCallerContext Context { get; set; }
  public IGroupManager Groups { get; set; }

  public virtual Task OnConnected();        
  public virtual Task OnDisconnected();       
  public virtual Task OnReconnected();

}

Derived class can override OnConnected, OnDisconnected, which are very useful to perform some actions on these events. ChatHub class is derived from the Hub class and we will discuss its method in next comming sections.

 public class ChatHub : Hub
 {
          
    public void Connect(string userName);

    public void SendMessageToAll(string userName, string message);

    public void SendPrivateMessage(string toUserId, string message);

    public override System.Threading.Tasks.Task OnDisconnected();

    .
    .
    .

 }    

Client calls Connect method when he wants to join Chat room. For sending message in chat room to all connected clients, it calls SendMessageToAll , For private chat with single user client calls SendPrivateMessage method.

Proxy at Client side

Add new page index.html in the solution and set the references of JQuery, SignalR and autogenerated hubs.

 <script src="http://www.codeproject.com/Scripts/jquery-1.8.2.min.js"></script>

 <!--Reference the SignalR library. -->
 <script src="http://www.codeproject.com/Scripts/jquery.signalR-1.0.0.js"></script>

 <!--Reference the autogenerated SignalR hub script. -->
 <script src="http://www.codeproject.com/signalr/hubs"></script>    

At the client side you need to create the hub proxy and start that proxy. You can create hub proxy using $.connection.yourHubClass and then start hub using $.connection.hub.start() method.

 <script type="text/javascript">

  $(function () {

     // Declare a proxy to reference the hub. 
     var chatHub = $.connection.chatHub;

     registerClientMethods(chatHub);

     // Start Hub
     $.connection.hub.start().done(function () {

        registerEvents(chatHub)

     });

  });

 </script>    

The import thing to be noticed here is the naming convention is being used in auto generated Hub proxy. We named our class ChatHub at server in camel case but at client side we are getting it in lower camel case like $.connection.chatHub

Connect to the Chat Room

In our Chat room user connects to the chat room by passing his name and on the successful connection, we send him a list of connected clients and some recent chat history which we saves in our application. Our first method in the ChatHub class is the Connect.

 public void Connect(string userName)
 {

    var id = Context.ConnectionId;

    if (ConnectedUsers.Count(x => x.ConnectionId == id) == 0)
    {

        ConnectedUsers.Add(new UserDetail { ConnectionId = id, UserName = userName });

        // send to caller
        Clients.Caller.onConnected(id, userName, ConnectedUsers, CurrentMessage);

        // send to all except caller client
        Clients.AllExcept(id).onNewUserConnected(id, userName);

    }

 }    

Get the connection id of the client who is calling the Connect method using Context.ConnectionId property. Check the id in existing connected clients list. If it does not exist then add it into the list. Now we need to do two more steps. First we have to send list of already connected clients and recent message list to the client who wants to connect and second we have to inform the other connected clients about new joiner.

So we can easily call the client side method of single client who want to connect using Clients.Caller property.

  // send to caller
  Clients.Caller.onConnected(id, userName, ConnectedUsers, CurrentMessage);

Inform the other connected clients about the new client, but we don’t want to call the method of the newly connected client. Use AllExcept property of the Clients, which can excludes some client as per your wish.

 // send to all except caller client
 Clients.AllExcept(id).onNewUserConnected(id, userName);     

Define/Expose your client side methods which are being called by server side code by statements Clients.Caller.onConnected(...) and Clients.AllExcept(id).onNewUserConnected(...). You can define your methods using chatHub.client.yourMethodName at client side.

 // Calls when user successfully logged in
 chatHub.client.onConnected = function (id, userName, allUsers, messages) {

    .
    .
    .

 }

 // On New User Connected
 chatHub.client.onNewUserConnected = function (id, name) {

    AddUser(chatHub, id, name);
 }     

From the client side you can Call the server side methods using chatHub.server.yourMethod statement in your web page.  

 chatHub.server.connect(name);    

Send Message in Chat room

In main chat room message typed by user is broadcast to all connected clients. On the server side in ChatHub class write SendMessageToAll and broadcast message using Clients.All.messageReceived method. messageReceived is a client side method.

 public void SendMessageToAll(string userName, string message)
 {
    // store last 100 messages in cache
    AddMessageinCache(userName, message);

    // Broad cast message
    Clients.All.messageReceived(userName, message);
 }    

On client side expose the messageReceived method and in this method simply add message in the chat area using JQuery.

 chatHub.client.messageReceived = function (userName, message) {

    AddMessage(userName, message);
 
 }    

After writing client side and server side methods, now we have to register button click event on client side and when user will click button we will call server side method sendMessageToAll, which broadcast message to the all connected clients.

 $('#btnSendMsg').click(function () {

     var msg = $("#txtMessage").val();
     if (msg.length > 0) {

         var userName = $('#hdUserName').val();

         chatHub.server.sendMessageToAll(userName, msg);
        
         $("#txtMessage").val('');
     }
 });    

Private Chat

You can also initiate private chat by double clicking on the connected client name. In private chat we do not send message to all connected client. In Private chat only two clients can send/receive each other message. So for sending private message sender will call server side sendPrivateMessage method. And in this method we send message to other client using Clients.Client(toUserId).sendPrivateMessage(..) method.

 public void SendPrivateMessage(string toUserId, string message)
 {

    string fromUserId = Context.ConnectionId;

    var toUser = ConnectedUsers.FirstOrDefault(x => x.ConnectionId == toUserId) ;
    var fromUser = ConnectedUsers.FirstOrDefault(x => x.ConnectionId == fromUserId);

    if (toUser != null && fromUser!=null)
    {
        // send to 
        Clients.Client(toUserId).sendPrivateMessage(fromUserId, fromUser.UserName, message); 

        // send to caller user
        Clients.Caller.sendPrivateMessage(toUserId, fromUser.UserName, message); 
    }

 }    

On Disconnect

On the close of the browser SignalR API calls OnDisconnected method. Override this method in your ChatHub class and in this method remove disconnected client from the cache list and send notification to other connected clients.

 public override System.Threading.Tasks.Task OnDisconnected()
 {
     var item = ConnectedUsers.FirstOrDefault(x => x.ConnectionId == Context.ConnectionId);
     if (item != null)
     {
         ConnectedUsers.Remove(item);

         var id = Context.ConnectionId;
         Clients.All.onUserDisconnected(id, item.UserName);

     }

     return base.OnDisconnected();
 }    

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here