Low Level Connection
Notice we're calling /echo from the client? That is hooked up in routing in Global.asax:
RouteTable.Routes.MapConnection("echo", "echo/{*operation}");
At this point, we've got two choices of models with SignalR. Let's look at the low level first.
using SignalR;
using System.Threading.Tasks;
public class MyConnection : PersistentConnection
{
protected override Task OnReceivedAsync(string clientId, string data)
{
// Broadcast data to all clients
return Connection.Broadcast(data);
}
}
We derive from PersistentConnection and can basically do whatever we want at this level. There's lots of choices:
public abstract class PersistentConnection : HttpTaskAsyncHandler, IGroupManager
{
protected ITransport _transport;
protected PersistentConnection();
protected PersistentConnection(Signaler signaler, IMessageStore store, IJsonStringifier jsonStringifier);
public IConnection Connection { get; }
public override bool IsReusable { get; }
public void AddToGroup(string clientId, string groupName);
protected virtual IConnection CreateConnection(string clientId, IEnumerable groups, HttpContextBase context);
protected virtual void OnConnected(HttpContextBase context, string clientId);
protected virtual Task OnConnectedAsync(HttpContextBase context, string clientId);
protected virtual void OnDisconnect(string clientId);
protected virtual Task OnDisconnectAsync(string clientId);
protected virtual void OnError(Exception e);
protected virtual Task OnErrorAsync(Exception e);
protected virtual void OnReceived(string clientId, string data);
protected virtual Task OnReceivedAsync(string clientId, string data);
public override Task ProcessRequestAsync(HttpContext context);
public void RemoveFromGroup(string clientId, string groupName);
public void Send(object value);
public void Send(string clientId, object value);
public void SendToGroup(string groupName, object value);
}
High Level Hub
Or, we can take it up a level and just do this for our chat client after adding
<script src="/signalr/hubs" type="text/javascript"></script>
to our page.
$(function () {
// Proxy created on the fly
var chat = $.connection.chat;
// Declare a function on the chat hub so the server can invoke it
chat.addMessage = function (message) {
$('#messages').append('' + message + '');
};
$("#broadcast").click(function () {
// Call the chat method on the server
chat.send($('#msg').val());
});
// Start the connection
$.connection.hub.start();
});
Then there is no need for routing and the connection.chat will map to this on the server, and the server can then call the client back.
public class Chat : Hub
{
public void Send(string message)
{
// Call the addMessage method on all clients
Clients.addMessage(message);
}
}
At this point your brain should have exploded and leaked out of your ears. This is C#, server-side code and we're telling all the clients to call the addMessage() JavaScript function. We're calling the client back from the server by sending the name of the client method to call down from the server via our persistent connection. It's similar to NowJS but not a lot of people are familiar with this technique.
SignalR will handle all the connection stuff on both client and server, making sure it stays open and alive. It'll use the right connection for your browser and will scale on the server with async and await techniques (like I talked about in the node.js post where I showed scalable async evented I/O on asp.net).
Want to see this sample running LIVE?
We've got a tiny tiny chat app running on Azure over at http://jabbr.net/, so go beat on it. There are folks in /join aspnet. Try pasting in YouTube links or images!
It's early, but it's an interesting new LEGO piece for .NET that didn't completely exist before. Feel free to check it out on GitHub and talk to the authors of SignalR, David Fowler and Damian Edwards. Enjoy.
Hosting By