Me and Kariem have been working a lot on Ra-Ajax lately, and one of my focal areas have been the
Comet parts.

Comet is
*really hard*, mostly because the web wasn't created for having real-time updates - which Comet essentially is. The web, and more importantly the HTTP standard was created as a
request/response thing. And since Comet at its very nature is the exact opposite, this means you have to more or less completely bash the HTTP until it's almost not even recognizable as HTTP anymore.
In addition Ra-Ajax have implemented a Queue for Ajax Requests which you can read the details about in our
Ajax LinkButton sample. And the Ajax Queue thing doesn't really help in making Comet easier to implement...
The Ajax Queue explained in one paragraph
The thing about doing Ajax when you have HTML form-elements you're sending back and forth is that any Ajax Request may modify/add/delete/create form-elements. If you think about that for a while this means that if you have two simultaneous Ajax Requests going on then the page may actually go into an
undefined state. This is even more true for a server-side binded Ajax Library like Ra-Ajax. Therefor unless you're happy with "undefined errors" and impossible-to-track-down bugs you must implement some sort of queue logic on your Ajax requests so that you can guarantee that there will never be more than one Ajax request going on at the same time. Then if you have Ajax requests in your queue the "next" request will dispatch and fire when the previous one have returned (and updated the HTML form elements)
This is crucial for all Ajax Frameworks, especially server-side binded Ajax frameworks. But currently I only know about two Ajax Frameworks which actually implements an Ajax queue; Gaia Ajax Widgets and Ra-Ajax.
Though this makes our Comet implementation grow in complexity since this means the Comet requests must "bypass" this Ajax Queue and cannot be "normal" Ajax requests.
How Ra-Ajax implements Comet
Me and Kariem solved this by creating a two-phase implementation of Comet. First we have a very small (tiny in fact) request from the client-side which is our Comet request. Then this request is "locked down" on the server by creating a ManualResetEvent object on the server. This is created on a spawned thread to make sure we don't fill up our thread-pool on our webserver through using the Asynchronous ASP.NET Pages feature of ASP.NET 2.0. This makes the thing relatively scalable in fact, for being a Comet solution at least.
Then we have a timeout property for our Comet request. This is actually the
AsyncTimeout property from the Page directive. This is because most clients (browsers) will have either directly or through some sort of proxy servers or firewalls a timeout for normal HTTP requests. When the timeout period elapses, the request returns "null" back to the client which will see that this is a timeout and just re-create the same Comet request and go on like that until an event fires.
When an event is fired on the Comet object, an ID will be returned back to the client which again will create a "normal" Ajax request, which goes through the Ajax Queue logic we earlier discussed and goes to our server which is the request which actually fires the Comet Event into user-code and where our users can respond to the Comet event. Since this is a normal Ra-Ajax Request, user-code can do whatever it can do from any other Ra-Ajax request which includes Create, Read, Update and Delete [CRUD] any Ra-Ajax controls and have it return its results back to the client.
In addition the Ra-Ajax Comet component is configurable in regards to maximum simultaneously connected clients before Comet is "shut off" for the following users, we have a counter which counts the number of currently connected users, you can configure the timeout on the "driver Comet request" and you can signal the Comet object from outside of the application by creating a normal HTTP GET request which you send to the page. This should be more than enough for most users to fiddle with for a long time I hope...
Here is a complete working sample of how to implement a Comet Chat-Client with Ra-Ajax for Mono or ASP.NET server-side backends;
.ASPX page
<%@ Page
Language="C#"
MasterPageFile="~/MasterPage.master"
AutoEventWireup="true"
CodeFile="Ajax-Comet.aspx.cs"
Async="true"
AsyncTimeout="20"
Inherits="Samples.AjaxComet"
Title="Ra-Ajax Comet Sample" %>
<%@ Register
Assembly="Ra"
Namespace="Ra.Widgets"
TagPrefix="ra" %>
<%@ Register
Assembly="Extensions"
Namespace="Ra.Extensions"
TagPrefix="ext" %>
<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
<title>Untitled Page</title>
</head>
<body>
<form id="form1" runat="server">
<ra:Panel
runat="server"
ID="chat">
</ra:Panel>
<div>
<ra:TextBox
runat="server"
ID="newChat"
Text="Type text here" />
<ra:Button
runat="server"
ID="submit"
Enabled="true"
Text="Submit"
OnClick="submit_Click" />
<ext:Comet
runat="server"
ID="comet"
MaxClients="200"
Enabled="true"
OnTick="comet_Tick" />
</div>
<p>
<ra:Label
runat="server"
ID="lbl"
Text="Number of connections" />
</p>
</form>
</body>
C# code
using System;
using Ra.Widgets;
using System.Collections.Generic;
using ASP = System.Web.UI.WebControls;
namespace Samples
{
public partial class AjaxComet : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
if (comet.IsQueueFull)
{
lbl.Text = "Sorry, we're out of stock :(";
}
else
{
lbl.Text = "# of users; " + comet.NumberOfConnections;
}
if (!IsPostBack)
{
foreach (string idx in Chats)
{
ASP.Literal lit = new ASP.Literal();
lit.Text = "<p>" + idx + "</p>";
chat.Controls.Add(lit);
}
}
}
protected void comet_Tick(object sender, Ra.Extensions.Comet.CometEventArgs e)
{
// Effect on area...
new EffectHighlight(chat, 600).Render();
lock (typeof(AjaxComet))
{
// Removing all "old" controls
chat.Controls.Clear();
foreach (string idx in Chats)
{
ASP.Literal lit = new ASP.Literal();
lit.Text = "<p>" + idx + "</p>";
chat.Controls.Add(lit);
}
// Signalizing that chat output should re-render...
chat.ReRender();
}
lbl.Text += ", Event sent; " + e.Id;
}
private List<string> Chats
{
get
{
List<string> retVal = Application["CometChats"] as List<string>;
if (retVal == null)
{
retVal = new List<string>();
Application["CometChats"] = retVal;
}
return retVal;
}
set
{
Application["CometChats"] = value;
}
}
protected void submit_Click(object sender, EventArgs e)
{
lock (typeof(AjaxComet))
{
if (Chats.Count >= 5)
{
Chats = new List<string>(Chats.GetRange(1, 4));
}
Chats.Add(newChat.Text);
}
// Signaling to all Comet Listeners that a new Message has arrived...
comet.SendMessage(Guid.NewGuid().ToString());
newChat.Select();
newChat.Focus();
}
}
}
BTW, this whole thing is available for download at the
Ra-Ajax Google Code project website. And the Comet implementation can be seen live (use FireBug for some fun;) at our
Comet Sample...
And it probably should work with all commonly known browsers on the planet, though I haven't tested it on Lynx yet ;)
A great resource for those interested in Comet from a conceptually point of view is
Comet - a new approach to Ajax applications by Alex Russell.
BTW, in case this is your first visit, Ra-Ajax is Open Source (LGPL) and free of charge to use. Also in commercial and proprietary projects.