Websockets are great, and have been mentioned often, but Android devices and 16% of browsers don't support websockets (CanIUse.com). Many server installations also don't support websockets, including shared LAMP setups. If you have a shared host or if you want to broad support, websockets may not be a valid option.
Long polling is the only valid alternative to websockets. It has broader support (it should work with almost all servers and clients), but it has a significant drawback on servers that don't handle many simultaneous connections well -- like Apache. Another drawback is that you have to perform many regular database queries (likely several per second) regardless of how many users are connected. Using shared memory, like shm_attach()
in PHP, could ease this burden. As the server script monitors new messages, they are immediately sent through the open connection when found. The client will receive the message then restart a long connection with a new request.
If you can't use websockets, which may well be the case, you can use a long and short polling hybrid (see below). Using very long polling is unnecessary and takes up too many resources. After about 10 or 15 seconds of constant connection, you should close it and switch to old-fashioned short polling, which is just a regular GET request that is repeated.
This jQuery code is untested, but you get the idea:
function longpoll(lastid) {
/* Start recursive long polling. The server script must stay
connected for the 15 seconds that the client waits for a response.
This can be done with a `while()` loop in PHP. */
console.log("Long polling started...");
if (typeof lastid == 'undefined') {
lastid = 0;
}
//long polling...
setTimeout(function () {
$.ajax({
url: "stream.php?long=1&lastid=" + lastid, success: function (payload) {
if (payload.status == "result") {
//result isn't an error. lastid is used as bookmark.
console.log("Long poll Msg: " + payload.lastid + ": " + payload.msg);
longpoll(lastid); //Call the next poll recursively
} else if (payload.status == "error") {
console.log("Long poll error.");
} else {
console.log("Long poll no results.");
}
/* Now, we haven't had a message in 15 seconds. Rather than
reconnect by calling poll() again, just start short polling
by repeatedly doing an normal AJAX GET request */
shortpoll(lastid); //start short polling after 15 seconds
}, dataType: "json"
});
}, 15000); //keep connection open for 15 seconds
};
function shortpoll(lastid) {
console.log("Short polling started.");
//short polling...
var delay = 500; //start with half-second intervals
setInterval(function () {
console.log("setinterval started.");
$.ajax({
url: "stream.php?long=0&lastid=" + lastid, success: function (payload) {
if (payload.status == "result") {
console.log(payload.lastid + ": " + payload.msg);
longpoll(lastid); //Call the next poll recursively
} else if (payload.status == "error") {
console.log("Short poll error.");
} else {
console.log("Short poll. No result.");
}
}, dataType: "json"
});
delay = Math.min(delay + 10, 20000) //increment but don't go over 20 seconds
}, delay);
}
Short polling reduces the number of concurrent connections, using repeated polling (requesting) instead. As always, the downside to short polling is the delay in getting new messages. However, this is analogous to real life, so it shouldn't be a big deal. (If somebody hasn't called you in the past week, they're not likely going to call you in the next five minutes, so checking your phone every five minutes would be silly.)