Introduction

I've been playing around with “Comet” a little and trying to make it work in ASP.NET without modifying anything in the IIS. There are a few web servers or IIS enhancements available that provide Comet functionality, but they require you to have control over IIS, or even over the complete system allowing you to replace IIS with a proprietary web server. But you might want to use Comet in a shared or hosted environment, where modifying the web server is not an option.

Background

The solution I found - so far - was inspired by the reactions on Aaron Lerch's blog post. Aaron is definitely into something when he writes his article, but the big issue with his sample is that the code doesn't scale too well, as he writes. The responses suggest using asynchronous handling of the requests, and there's even a link to a Comet-enabled GridView here on CodeProject. That GridView worked for me, but first of all, it was far too complicated for what I wanted: simply pushing a message to a user that is on my website, and next, it only sends messages to one session (browser).

Using the Code

I started with a simple “default.aspx” in a normal web site. This page will call the async handler using the XMLHttpRequest object (well-known from AJAX implementations). When a user visits the website, we’ll somehow have to let the server know that he’s there. There are a number of ways to solve this. I chose to create a List of Session IDs and add the user session IDs to the List in the Session_Start event handler in Global.asax. Of course, there are better solutions, but it’ll do for the purposes of this example.

We need a message handler on the server that will deliver the message to the right session. I created a synchronous handler. Since it handles the messages immediately, it doesn’t hold on any threads or resources after the message is delivered.

////// An IHttpHandler for the messages that are sent from a session
///publicclass MyMessageHandler : IHttpHandler
{
#region IHttpHandler Members
publicbool IsReusable
{
get { returntrue; }
}
publicvoid ProcessRequest(HttpContext context)
{
// Find the handle in the queue, identified by its session id
var recipient = context.Request["recipient"];
var handle = MyAsyncHandler.Queue.Find(q => q.SessionId == recipient);
// just a small check to prevent NullReferenceException
if (handle == null)
return;
// Dump the message in the handle;
handle.Message = context.Request["message"];
// Set the handle to complete, this triggers the callback
handle.SetCompleted(true);
}
#endregion
}

Next is the asynchronous handler to handle requests from the XMLHttpRequest object on the Default.aspx page and deliver a message back. I called it MyAsyncHandler and let it inherit from the System.Web.IHttpAsyncHandler interface. There’s some logic there and I decided to just explain the most important parts. First of all, the static constructor initializes the queue (a System.Collections.Generic.List) that is intended to hold all the asynchronous results. Next, when the request comes in, it calls the BeginProcessRequest method. This method checks if the session already was registered in the queue before, or if it should add the session to the queue. Finally, the EndProcessRequest uses its result parameter (of type MyAsyncResult) and finds the HttpContext object in there. Using the HttpContext.Response.Write, it “pushes” the message to the session that is intended to be the recipient of the message, identified by its session ID.

As soon as the client gets a response, the XMLHttpRequest object notices a “ready state” change and the “onreadystatechange” handler kicks in. The message is parsed into an object on the page (e.g., a DIV), and directly thereafter, the page sends a new asynchronous request, signaling the server that it's ready to receive another message.

Don’t forget to register the handlers in the Web.config. Your web application needs to know what classes handle the requests from the clients. Add the following lines to the system.web/httpHandlers section:

NB! If you’re using IIS 7, you add these lines to the system.webserver/handlers section instead.

There are – at least – three things with this example that you immediately might want to do better. First of all, sending a message to a Session ID is pretty vague. You’d let the website visitor enter his name so that other users can send messages to a name rather than a Session ID.

Next, this example doesn’t check if the Session is still active. The Session might have timed-out or have been abandoned so that the Session_End method in the Global.asax never got called. To deal with this, you should implement a neat solution to keep track of Sessions.

Last but not least: you’ve to refresh your website manually to check if new sessions are available. You could use the same Comet technique to update the DropDownList automatically when a new visitor enters the website. Or, you could use the “old fashioned” AJAX way and poll your page periodically to update the list.

after downloading your source to test on my local, it miss some file, like MyAsyncHandler,MyMessageHandlerafter adding missing file to project , i start run, but it tell me the error below

Could not load file or assembly 'SandBox.CometSample' or one of its dependencies. The system cannot find the file specified.and it hightlist color red on this<add verb="GET,POST" path="MyAsyncHandler.ashx" type="SandBox.CometSample.MyAsyncHandler, SandBox.CometSample" validate="false"/>

First of all, I'd like to make clear that the code is an example of how you could use the "comet" technology in practice. The code isn't meant to use literally in any webside, because it's not 100% proven, it's just a way of making clear what I write in the article.

1. What will happen if you close the browser is that the request will remain in the server. There's no time-out functionality in my sample, but it won't be too hard to build that in, I think. Ideally there would be a time-out manager who sends a response to the client if there hasn't been any activity to/from this client in more than x minutes. If the client has closed its browser, the response will never reach the client, but at least it'll clean up the request that was held on the server

2. See answer on 1

3. I've not tested it myself with more than 3 clients. I've had some responses on this article telling me that the code doesn't scale too well, and that might be. I repeat that the code wasn't meant for "real life", but just as an example of the technology.

I'm not 100% sure why the sample works for a certain amount of time in your IIS. It might be a timeout setting in IIS, that closes the session/application after x minutes of inactivity. If there are no messages sent, no activity is registered within the application, and such timeout might hit it.

Ive had this up and running for months and was working great... but now IE seems to hang after a few page refreshes and it looks like the request to asynchandler.ashx doesnt seem to cancel itself when you leave the page and then blocks all other requests... its fine in firefox/safari/chrome.

Im using IEdebug to monitor the http requests ... any thoughts? Can provide screen shots if you like

The SandBox.CometSample.Global type is the namespace and class name of the "code behind" for the Global.asax and can be found in the Global.asax.cs file. The message you get implies that the website somehow is missing this file. Check if both the .asax and the .asax.cs file got extracted from the download. Also check if your web server is set up to run .NET version 3.5

I also sent a message to the e-mail address you supplied and I'll be glad to help you get this up 'n running.

I checked your said the two files are intact!!But when I use the Visual Studio 2008 to compile the download source it still doesn't work and reports the same mistake!!I feel very strange!!!I have never met such a situation that I saw Global.asax.cs exist SandBox.CometSample.Global but it still reports the mistake that Could not load type 'SandBox.CometSample.Global!

Yeah,The Errors comes during I am using Visual Studio to compile the code !

Have other conditions will cause such a mistake?I just joined software development so I don't have much experience!I need you experienced master to help me!!!

I suggest we try to solve your problem via e-mail communication, and rather publish the solution in a comment on this page. In that way, we don't get a lot of comments that aren't necessarily of interest to other readers. I already got your e-mail address and will send you another e-mail shortly.