Most of the solutions mentioned in this thread suffer from the fact that they rely on Comet long-polling (the "pinging" you described) or the web server itself. Conventional web servers are not designed to handle large amounts of long-lived connections. They'll normally kill over after a dozen or so users. While long-pulling initiates a new request for each upstream message, resulting in several hundred bytes of header data to be generated, transmitted and parsed. That additional latency incurred for a full round trip hurts real-time activity.
A better solution would be to use websockets, but not all browsers support it, so you'd need to gracefully degrade to something like long-polling for those that don't. Unlike traditional AJAX, where each request consists of a round trip which sends and receives data, a web socket sends and receives asynchronously on a single connection, which allows websockets to reuse the same TCP stream, "push" data to the browser and skip redundant HTTP headers.
That said, the most efficient (and probably best) solution would be to use a separate evented i/o server (and by server, I really mean daemon), written in a language better suited for this, to hold conversations in memory and serve the websocket and long-polled HTTP requests.