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.
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.
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.
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.
SignalR is broken up into a few package on NuGet:
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.
Notice we're calling /echo from the client? That is hooked up in routing in Global.asax:
RouteTable.Routes.MapConnection<MyConnection>("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);}
Or, we can take it up a level and just do this for our chat client after adding
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('
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).
We've got a tiny tiny chat app running at AppHarbor over at http://chatapp.apphb.com, so go beat on it. There are folks in /join aspnet. Try pasting in YouTubes 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.
$('#messages').append('<li>' + data + '</li>');
$("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.
$(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();};
Remember Me
a@href@title, b, blockquote@cite, em, i, pre, strike, strong, sub, super, u
Scott at DevReach in Bulgaria in October
Developer Stand up Comedy - Coding 4 Fun
TechDays/DevDays Netherlands and Belgium:
Posts by Category Posts by Month
Greatest Hits Dev Tools List