Search
Close this search box.

ASP.NET SignalR: Building a Simple Real-Time Chat Application

Introduction

SignalR is a pretty new and very exciting feature in ASP.NET. It offers a simple and clean API that allows you to create real time web applications where the server needs to continuously push data to clients. Common applications are chat, news feeds, notifications and multi-player games.

For this exercise we’re going to build a simple chat application in ASP.NET MVC 4 using the power of SignalR. I presume that you already have the basic knowledge on ASP.NET MVC and how stuff works (e.g. Model, View and Controller) in MVC development approach because the details about them will not be covered in this exercise. If you are new to ASP.NET MVC and want to get your hands dirty then here are some step by step jump starts that you can refer:

If you want more examples then I suggest you to visit the official ASP.NET MVC channel here: http://www.asp.net/mvc

STEP 1: Setting up your ASP.NET MVC Project

To get started, let’s go ahead and fire up Visual Studio 2012 and create a new project by selecting File > New Project. Under templates panel, select Visual C# > Web > ASP.NET MVC 4 Web Application. Name your project and click on OK button.

In ASP.NET MVC 4 Project Template window, just select”Basic” since we don’t really want to use the built-in forms authentication in this exercise. Then click OK to generate the necessary files for you.

STEP 2: Adding SignalR to your Project

SignalR is available in Visual Studio 2010 (.NET 4.0) and Visual Studio 2012 (.NET 4.5) and can be referenced from Nuget.

Under Visual Studio Tools tab, select Library Package Manager > Manage Nuget Package for Solution. In the search bar type “Microsoft.Aspnet.SignalR” and hit enter. The result should give you something like below.

Now click the Install button to add the SignalR library and its dependencies into your project. Just follow the steps mentioned in the wizard until all the necessary files are successfully installed. After the installation you should be able to see the newly added dll’s in your project references folder. See below:

Okay we’re now all set and ready to get our hands dirty. 😉

STEP 3: Adding a Controller

To add a new controller, just right click on the Controller’s folder and then select Add > Controller. Name the controller as ChatRoomController and then click Add.

Here’s the code of the Controller class:

As you can see there’s nothing really fancy in the Controller. It just contains the SignalRChat action method that returns a View.

Now let’s go ahead and create the SignalRChat view.

STEP 4: Adding a View

The first thing we need here is to add a ChatRoom folder within the View folder. The reason for this is that the folder name should match with the name of the Controller you’ve created in STEP 3. That’s one of the MVC conventions. So for example if you have a “HomeController”, then you should have a “Home” folder within your View.

Alright let’s proceed. In your ChatRoom folder add a new view by right clicking on it and then select Add > View. Make sure to name the view as “SignaRChat” since that’s the name of the view that the controller has to expect. Then click Add to generate the View.

Just for the simplicity of this exercise, I just set it up like this:

STEP 5: Adding a Hub

To give you a quick overview – Hub is the center piece of the SignalR. Similar to the Controller in ASP.NET MVC, a Hub is responsible for receiving input and generating the output to the client.

Before we create a Hub, let’s add a new folder first within the root of the project. In this exercise I named the folder as Hubs. Right click on that folder and select Add > ASP.NET SignalR Hub class. Name the class as “ChatHub” and then click OK to generate the file.

Here’s the code block of the Hub class:

using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.AspNet.SignalR;
using System.Collections.Concurrent;
using System.Threading.Tasks;

namespace MvcDemo.Hubs {
public
class ChatHub : Hub {
  static ConcurrentDictionary<string, string> dic =
      new ConcurrentDictionary<string, string>();

 public
  void Send(string name, string message) {
    Clients.All.broadcastMessage(name, message);
  }

 public
  void SendToSpecific(string name, string message, string to) {
    Clients.Caller.broadcastMessage(name, message);
    Clients.Client(dic[to]).broadcastMessage(name, message);
  }

 public
  void Notify(string name, string id) {
    if (dic.ContainsKey(name)) {
      Clients.Caller.differentName();
    } else {
      dic.TryAdd(name, id);
      foreach (KeyValuePair<String, String> entry in dic) {
        Clients.Caller.online(entry.Key);
      }
      Clients.Others.enters(name);
    }
  }

 public
  override Task OnDisconnected() {
    var name =
        dic.FirstOrDefault(x = > x.Value == Context.ConnectionId.ToString());
    string s;
    dic.TryRemove(name.Key, out s);
    return Clients.All.disconnected(name.Key);
  }
}
}  // namespace Hubs

As you may have noticed, the ChatHub class inherits from Microsoft.Aspnet.SignalR.Hubs.Hub. Keep in mind that Public methods defined within the Hub class are intended to be called from the client code (JavaScript). They actually result in a response being sent to the client.

At runtime, a JavaScript file is generated dynamically which contains the client-side implementations of the public methods defined in the Hub. These client-side methods will act as a proxy to the server-side methods. You write an implementation of public methods from the Hub in your client-side code so that the Hub can call it and thereby provide the response.

The dictionary is where we store the list of available users that have joined the chat room. I used dictionary so we can easily add and remove items from it using key value pair.

The Send() method broadcast the message to the users by passing the name and the message as the parameters. The SendToSpecific() method on the other hand broadcast the message to a specific user. The Notify() method tells the client when someone enters the chat room and then adds them to the list of the available users. The OnDisconnected() method handles the removing of users if someone leaves the chat room. This method is asynchronous and will be called whenever the connection from the client is closed (e.g. closing the browser).

STEP 6: Implementing the Client-Side code

Now that we already have our Hub setup then we can now proceed with the client-side implementation of our chat application.

Add the following code block below in your View (.cshtml/.aspx):

@section scripts {
  @Scripts.Render("~/Scripts/jquery-ui-1.9.2.min.js")
      @Scripts.Render("~/Scripts/jquery.signalR-1.0.1.min.js")
          @Scripts.Render("/signalr/hubs")

      <script type = "text/javascript">

          $(function() { showModalUserNickName(); });

  function showModalUserNickName() {
                     $("#dialog").dialog({
                         modal: true,
                         buttons: {
                             Ok: function () {
                                 $(this).dialog("close");
                                 startChatHub();
  }
}
});
}

function startChatHub() {
  var chat = $.connection.chatHub;

  // Get the user name.
  $('#nickname').val($('#nick').val());
  chat.client.differentName = function(name) {
    showModalUserNickName();
    return false;
    // Prompts for different user name
    $('#nickname').val($('#nick').val());
    chat.server.notify($('#nickname').val(), $.connection.hub.id);
  };

  chat.client.online = function(name) {
    // Update list of users
    if (name == $('#nickname').val())
      $('#onlineusers')
          .append('<div class="border" style="color:green">You: ' + name +
                  '</div>');
    else {
      $('#onlineusers').append('<div class="border">' + name + '</div>');
      $("#users").append('<option value="' + name + '">' + name + '</option>');
    }
  };

  chat.client.enters = function(name) {
    $('#chatlog')
        .append('<div style="font-style:italic;"><i>' + name +
                ' joins the conversation</i></div>');
    $("#users").append('<option value="' + name + '">' + name + '</option>');
    $('#onlineusers').append('<div class="border">' + name + '</div>');
  };
  // Create a function that the hub can call to broadcast chat messages.
  chat.client.broadcastMessage = function(name, message) {
    // Interpret smileys
    message = message.replace(
        ":)", "<img src=\"/images/smile.gif\" class=\"smileys\" />");
    message = message.replace(
        ":D", "<img src=\"/images/laugh.gif\" class=\"smileys\" />");
    message = message.replace(
        ":o", "<img src=\"/images/cool.gif\" class=\"smileys\" />");

    // display the message
    $('#chatlog')
        .append('<div class="border"><span style="color:orange">' + name +
                '</span>: ' + message + '</div>');
  };

  chat.client.disconnected = function(name) {
    // Calls when someone leaves the page
    $('#chatlog')
        .append('<div style="font-style:italic;"><i>' + name +
                ' leaves the conversation</i></div>');
    $('#onlineusers div').remove(":contains('" + name + "')");
    $("#users option").remove(":contains('" + name + "')");
  }

  // Start the connection.
  $.connection.hub.start().done(function() {
    // Calls the notify method of the server
    chat.server.notify($('#nickname').val(), $.connection.hub.id);

    $('#btnsend').click(function() {
      if ($("#users").val() == "All") {
        // Call the Send method on the hub.
        chat.server.send($('#nickname').val(), $('#message').val());
      } else {
        chat.server.sendToSpecific($('#nickname').val(), $('#message').val(),
                                   $("#users").val());
      }
      // Clear text box and reset focus for next comment.
      $('#message').val('').focus();
    });
  });
}
</script>
}

At the very top within the @RenderSection you need to define a reference to jQueryUI and SignalR scripts. Please note that script reference should be in the following order:

  1. jQuery script
  2. jQueryUI scripts
  3. SignalR script
  4. SignalR/Hubs
  5. Your custom defined script

You might be wondering why we haven’t included a reference to jQuery script in the code above. The reason for this is that by the time you created the Template, the engine will automatically add a reference to jQuery script within your _Layout.cshml before the closing tag of the <body> element and automatically bundled it for you for page performance.

Note: Adding a reference to jQuery script within your View will cause an error when running your application that uses SignalR. This is because the reference to jQuery script will get duplicated and the order of the script references will be changed which can cause the SignalR script to throw exception.

The showModalUserNickName() function calls the jQueryUI dialog. This function will be called when the browser is loaded to prompt the user to enter their name before joining the chat room. After entering their name the startChatHub() function will be called. This function is where the actual implementation of the chat is defined. The logic is that it first creates a proxy connection to the Hub (ChatHub). Once connected we can now have access to the public methods defined in our Hub class. The next step there is to get the name of the user. If the user already exists in the dictionary then they will be prompted to enter another name. After that it will call the Notify() method to add the new user in the dictionary and notify other users that someone has joined the chat room. The next function updates the message logs, available users and the online user’s panel when someone joins the room. After that it calls the broadcastMessage() function to display the messages between users in the message logs. You will also notice that I’ve added some code within that function that will interpret smileys (that was just a sample, you can add/declare emoticons as many as you want). The client.disconnected() function is responsible for updating the UI and the list of users when someone leaves the chat room. And finally call the hub.start() function to start the connection between the client and the hub (server). This method contains the implementation of sending the message to other users or specific users.

STEP 7: Wrapping Up

Adding a new CSS file

Create a new CSS file under the Content folder. Name it as “chat.css”. Then add the following style definitions in the CSS file:

#container {
width : 400px;
margin - left : auto;
margin - right : auto;
}
#chatlog {
width : 250px;
background - color : aliceblue;
float : left;
}
#onlineusers {
float : right;
width : 100px;
background - color : #D4D4D4;
}
#chatarea {
clear : both;
width : 200px;
}
#chatarea.messagelog {
float : left;
height : 90 % ;
top : 10 % ;
position : relative;
}
#chatarea.messagelog.messagebox {
width : 400px;
height : 80 % ;
}
#chatarea.actionpane {
position : relative;
float : left;
}
.smileys {
width:
  14px;
height:
  14px;
}

Adding Smiley images

Create a new folder within the root project and name it as “Images”. Then add your smileys within that folder.

Referencing CSS files

Then add the following references at the <head> section in your _Layout.cshtml:

    <link href="~/Content/chat.css" rel="stylesheet" />
    <link href="~/Content/themes/base/jquery-ui.css" rel="stylesheet" />

Modifying the default Route

Update the default route under App_Start > RouteConfig so that it will automatically display the SignalRChat page when you run the application. Here’s the updated code:

routes.MapRoute(name
                : "Default", url
                : "{controller}/{action}/{id}", defaults
                : new {controller = "ChatRoom", action = "SignalRChat",
                       id = UrlParameter.Optional});

Registering the MapHubs Route

And finally, the most import thing to make things work; add the following line below in your Global.asax.cs under Application_Start event:

RouteTable.Routes.MapHubs();

Note: Make sure to define the MapHubs() route before RegisterRoutes() call.

STEP 8: Run the Page

Here’s the sample output below when running the page:

That’s it! I hope someone find this post useful!

Technorati Tags: ASP.NET MVC,ASP.NET,SignalR,jQuery

This article is part of the GWB Archives. Original Author: Vinz’ Blog (ProudMonkey)

Related Posts