Asynchronous scalable web applications with real-time persistent long-running connections with SignalR
I've been spending some time exploring asynchrony and scale recently. You may have seen my post about my explorations with node.js and iisnode running node on Windows.
Every application has different requirements such that rules to "make it scale" don't work for every kind of application. Scaling a web app that gets some data and for loops over it is different from an app that calls out to a high-latency mainframe is different from an app that needs to maintain a persistent connection to the server.
The old adage "when all you have it is a hammer everything looks like a nail" really holds true in the programming and web space. The more tools - and the knowledge to use them - the better. That's why I'm an advocate not only of polyglot programming but also of going deep with your main languages. When you really learn LINQ for example and get really good at dynamics, C# becomes a much more fun and expressive language.
Polling is a common example of hammering a screw. Trying to make a chat program? Poll every 5 seconds. Got a really long running transaction? Throw up an animated GIF and poll until eternity, my friend!
Long polling is another way to get things done. Basically open a connection and keep it open, forcing the client (browser) to wait, pretending it's taking a long time to return. If you have enough control on your server-side programming model, this can allow you to return data as you like over this "open connection." If the connection breaks, it's transparently re-opened and the break is hidden from both sides. In the future things like WebSockets will be another way to solve this problem when it's baked.
Persistent Connections in ASP.NET
Doing this kind of persistent connection in a chat application or stock ticker for example hasn't been easy in ASP.NET. There hasn't been a decent abstraction for this on the server or a client library to talk to it.
SignalR is an asynchronous signaling library for ASP.NET that our team is working on to help build real-time multi-user web application.
Isn't this just Socket.IO or nowjs?
Socket.IO is a client side JavaScript library that talks to node.js. Nowjs is a library that lets you call the client from the server. All these and Signalr are similar and related, but different perspectives on the same concepts. Both these JavaScript libraries expect certain things and conventions on the server-side, so it's probably possible to make the server look the way these clients would want it to look if one wanted.
SignalR is a complete client- and server-side solution with JS on client and ASP.NET on the back end to create these kinds of applications. You can get it up on GitHub.
But can I make a chat application in 12 lines of code?
I like to say
"In code, any sufficient level of abstraction is indistinguishable from magic."
That said, I suppose I could just say, sure!
Chat.DoItBaby()
But that would be a lie. Here's a real chat application in SignalR for example:
Client:
var chat = $.connection.chat;
chat.name = prompt("What's your name?", "");
chat.receive = function(name, message){
$("#messages").append("
"+name+": "+message);
}
$("#send-button").click(function(){
chat.distribute($("#text-input").val());
});
Server:
public class Chat : Hub {
public void Distribute(string message) {
Clients.receive(Caller.name, message);
}
}
That's maybe 12, could be 9, depends on how you roll.
More details on SignalR
SignalR is broken up into a few package on NuGet:
- SignalR - A meta package that brings in SignalR.Server and SignalR.Js (you should install this)
- SignalR.Server - Server side components needed to build SignalR endpoints
- SignalR.Js - Javascript client for SignalR
- SignalR.Client - .NET client for SignalR
- SignalR.Ninject - Ninject dependeny resolver for SignalR
If you just want to play and make a small up, start up Visual Studio 2010.
First, make an Empty ASP.NET application, and install-package SignalR with NuGet, either with the UI or the Package Console.
Second, create a new default.aspx page and add a button, a textbox, references to jQuery and jQuery.signalR along with this script.
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, IEnumerablegroups, 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('
};
$("#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.
About Scott
Scott Hanselman is a former professor, former Chief Architect in finance, now speaker, consultant, father, diabetic, and Microsoft employee. He is a failed stand-up comic, a cornrower, and a book author.
About Newsletter
Is this a prototype project? Will this project make it into the core asp.net library?
Thanks
Why does it need to be core? It works now.
Still, this looks impressive and I'll definitely try it out. Ideally, the IE team would ship WebSocket support in IE tomorrow, and the WCF and ASP.NET teams would do the same for the server-side. Of course, we have to wait for W3C to hurry up and finalise WebSocket in the first instance!
Thanks for sharing this.
You have missed one include line in the "High Level Hub" example:
<script src="/signalr/hubs" type="text/javascript"></script>
Thanks for the great info either way, and everything else you contribute as well :)
Matt
Right now if one wants to print out a post it's a nightmare. Text only takes half of the pages width.
one important notice for IE9 users - web page should contain <!DOCTYPE html> to force IE use "Internet Explorer 9" document mode rather than "Quircks mode".
In "Quircks" mode on the step on initialization SignalR will throw exception "SignalR: No JSON parser found. Please ensure json2.js is referenced before the SignalR.js file if you need to support clients without native JSON parsing support, e.g. IE<8."
Alexey.
Does it make to sense to use Signal with .net client?
I have a client-server app (wcf services+wpf client) where client polls its server for events.
Will SignalR be better (read: more scalable) that polling server via WCF?
http://stackoverflow.com/questions/65673/comet-implementation-for-asp-net
@Hemanshu Bhojak We don't have numbers for you yet, but when we do, we'll post them.
@Anton Yes? Not sure what you mean by work with mvc. Are you asking if it can work in an mvc application?
@Korriban Not yet, and the vast majority of browsers don't support that yet. And even when they do, all people using your app won't have it, so you'll need some kinda of fallback.
@Symon Rottem Comet is an umbrella term. Signalr is an abstraction over a persistent connection. The connection may use different transports to pass messages. The default is long polling, and you can expect something like websockets when that works well enough.
Great work on the project, thanks for the contribution!
http://sergiotapia.com/2011/09/signalr-with-mvc3-chat-app-build-asynchronous-real-time-persistant-connection-websites/
Interesting stuff, as always ...
But I wonder, what are the reasons that the web did not always used persistent connections (by persistent I mean, actually leave the socket open)?
It seems web is designed to be stateless at all costs, why?
What are the downsides of persistent connections (server side performance maybe?)
Thanks,
Ben.
Message: 'undefined' is null or not an object error when I try to create chat object using high level example.
I have added the SignalR using VS2010 package utility. JQuery, SignalR.js, and <script src="/signalr/hubs" type="text/javascript"></script> is added in my head tag.
I have added Chat class as you instructed.
With this, the logic fails @ var chat = $.connection.chat line.
What could be a reason?
Please let me know.
Thanks,
Samir
I am building my app in Azure so please make sure that this will work with multiple Azure web roles.
I found the code in ASP.NET Website project is not working
var chat = $.connection.chat is null
Can anybody tell me how to make it works on ASP.NET Website project?
thank you.
Anthony
This is great good work and very easy to understand.
One question though:
How would you go about the situation where you want to be able to send a message across multiple websites (all in different assemblies) but accessible from only one location.
For example: I have an assembly with the Chat.cs class that inherits from Hub, and that assembly is referenced by 3 other websites.
I want to be able to send messages server-side to all those sites that use my Chat class.
This is essentially to have a notification system that doesn't require constant pooling and instead when there is a new notification, we can simply push it to all applications.
-Emmanuel
Question...You say, "In the future things like WebSockets will be another way to solve this problem when it's baked." Are you guys saying your *not* using Websockets? If so, how exactly are you achieving the persistent connection?
More importantly, does this only work in IE? I have to ask...
Thanks..
For all the beginners and intermediates,
Here is a post on my blog on Real time Push Notifications with SignalR & PNotify http://www.msguy.com/2011/11/real-time-push-notifications-with.html
For all the beginners and intermediates,
Here is a post on my blog on Real time Push Notifications with SignalR & PNotify A HREF="http://www.msguy.com/2011/11/real-time-push-notifications-with.html
Anil
$('#messages').append('<li>' + data + '</li>');
Great - another way to introduce XSS bugs! :P
$("h1").append("Test<script>alert('hello')</script>")
All of jQuery's insertion methods use a domManip function internally to clean/process elements before and after they are inserted into the DOM. One of the things the domManip function does is pull out any script elements about to be inserted and run them through an "evalScript routine" rather than inject them with the rest of the DOM fragment. It inserts the scripts separately, evaluates them, and then removes them from the DOM.
So the script will still be executed, even though it won't appear in the DOM.
On an internal IM just now, Damien Edwards suggested this naive implementation:
$(function () {
$.fn.appendHtml = function (content) {
this.append($("<div/>").text(content).html());
};
$("#content").appendHtml("Hello <script>window.alert('hello');</script>");
});
jQuery.htmlEncode = function(value) {
return jQuery("<div/>").text(value).html();
};
Isn't there some sort of open connection limit on windows server systems? Any idea how many active clients a typical server would actually be able to support?
One other question, when the infrastructure is using the long polling mechanism, does every 'push' back to the client result in the connection being terminated and re established?
I am following your guideline and everything seem good except the OnConnectedAsyncTask was not fire when I close tab or browser..???. I am using PersistentConnection and try to implement raw connection.
How my App define disconnected clientID (or ConnectionID)...???.
Thank for your help...
After reading your post, I spent some time with this library but honestly, there are lots of things missing in SignalR to develop an enterprise level application. Consider a scenario, 10K active connections receiving a message each 5 secs. It fails.. Even though the number would be 2K. (TESTED)
There are libraries around handling this task well under IIS and around 20-25K loads but obviously they are paid solutions.
I encourage you to look at it again with the latest source. And please, if you have issues, provide as much detail as you can so that we might help address them.
Thanks.
PS: I think my test environment is good enough for the test case because I have made this test with another paid product with success.
Thanks
Can you please update the line:
RouteTable.Routes.MapConnection<myconnection>("echo", "echo/{*operation}");</myconnection>
to this:
RouteTable.Routes.MapConnection<MyConnection>("echo", "echo/{*operation}");
Looks like your editor got a bit correction happy. When I was trying to run the low level connection, it totally threw me off at first.
Thanks!
Awesome post, you have inspired me to take a closer look into SignalR and it is impressive. Have already thrown together some cool little applications for it, loving it.
I have seen a few posts around about implementing into CMS's such as Umbraco. Do you have any knowledge of how it fairs in more complex systems? For example Orchard, I only had a quick look at it but it seems that all of Orchards pre application executions bully SignalR out. I'm not quite sure how signalR works exactly but I assume it uses some pre application start hook to inject a http module which does the whole proxy client script generator. Possibly.
Cant remember what my question was now ^^ Anyway, enjoyed the post, have a good one
Nothing is changed in my code, just new references.
I am using jquery 7.1 version, but version 0.35 was working pefrect with 7.1. Please advise.
Here is my function:
$.connection.hub.start(function () {
alert("start function");
chat.join()
.fail(function (e) {
addMessage(e, 'error');
})
.done(function (success) {
if (success === false) {
alert("returned false");
}
});
});
Thanks,
Samir
Thank you for your great post.
I am trying to use SignalR in a RESTfull architecture project with SL5.0 in client side. I could get it running and came up with the following quesitons:
- Current SL is not compatible with SL5.0 because of using System.Threading.Tasks.SL4 assembly. However, when I remove this reference to use SL5.0 classes instead I get compile error because ConcurrentDictionary does not exist in in SL5.0, do you have any plan to support SL5.0 as well?
- When do you think the SignalR.ScaleOut project will be completed?
NB: If you are planning to release it soon then I won't have to implement it something similar myself.
Thank you,
I just have a look at SignalR GitHub issues page and found out that the next release will be ready around one month later, and I hope the scaleout project will be completed then.
Also, make an enquiry about SL5.0 support and David kindly answered that if the threading package that is used in SignalR will support SL5.0 then yes, but when it happens I don't know
Thanks David
It runs on Azure with 4 nodes
Each web server's gonna have a different pool of clients that connected, and so events broadcast are only specific to a web server and its unique set of client connections, no? Not application-farm wide.
http://spmatt.wordpress.com/2012/04/12/harnessing-signalr-in-sharepoint/
I meant Sql Server Scale Out, is there any plan to get it done?
Thanks
Paul
Is the pluggable state system documented anywhere?
We'd like to use it for our web farm based project.
Hojjat
Regarding your 'I've personally seen it scale to hundreds of thousands of connections on one machine' quote. Would you see SignalR as good alternative to jQuery Ajax calls.
I'm trying to build a working example of the above, could you provide a step by step example, which method in the Global.asax should RouteTable.Routes.MapConnection<myconnection>("echo", "echo/{*operation}");</myconnection> go in ? for example ?
Lets say that I want to use SignalR in VS2010 to create a chatroom application.
When I'm using NuGet I'm not finding SignalR but a plethora of SignalR.* packages. I'm confused which one to use?Hence I don't want to use NuGet to download and include in my project. All I want is to add some dlls and JS files at most to my application and then use SignalR. In that case what would be the dlls/JS file which I would need?
Can you please help me out?
Thanks
I am hoping to use SignalR in a self hosted Windows Service.
This Windows Service is supposed to monitor an external system and publish changes to JS clients as and when they happen.
My question is, is there a way to secure the SignalR endpoint hosted within the Windows Service? For example, any one finding out this Url could write a simple app to start receiving notifications. If I hosted these endpoints within IIS, I could use Windows Authentication/Authorisation to restrict access.
Apologies if this is a very easy thing to do, but I am new to SignalR dev.
Thanks very much.
Suneth
Uncaught TypeError: Object #<Object> has no method 'send'
Comments are closed.