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)
{
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>
-->
<script src="http://www.codeproject.com/Scripts/jquery.signalR-1.0.0.js"></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 });
Clients.Caller.onConnected(id, userName, ConnectedUsers, CurrentMessage);
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.
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.
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.
chatHub.client.onConnected = function (id, userName, allUsers, messages) {
.
.
.
}
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)
{
AddMessageinCache(userName, 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)
{
Clients.Client(toUserId).sendPrivateMessage(fromUserId, fromUser.UserName, message);
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();
}