views:

959

answers:

3

Hi,

I am new to this site, so I really hope I will provide all the necessary information regarding my question.

I've been trying to create a "new message arrived notification" using long polling. Currently I am initiating the polling request by window.onLoad event of each page in my site.

On the server side I have an infinite loop:

while(1){
 if(NewMessageArrived($current_user))break;
 sleep(10);
}
echo $newMessageCount;

On the client side I have the following (simplified) ajax functions:

poll_new_messages(){
 xmlhttp=GetXmlHttpObject();
 //...
 xmlhttp.onreadystatechange=got_new_message_count;
 //...
 xmlhttp.send();
}

got_new_message_count(){
 if (xmlhttp.readyState==4){
  updateMessageCount(xmlhttp.responseText);
  //...
  poll_new_messages();
 }
}

The problem is that with each page load, the above loop starts again. The result is multiple infinite loops for each user that eventually make my server hang.

*The NewMessageArived() function queries MySQL DB for new unread messages.

*At the beginning of the php script I run start_session() in order to obtain the $current_user value.

I am currently the only user of this site so it is easy for me to debug this behavior by writing time() to a file inside this loop. What I see is that the file is being written more often than once in 10 seconds, but it starts only when I go from page to page.

Please let me know if any additional information might help.

Thank you.

A: 

You might try checking connection_aborted() periodically. Note that connection_aborted() might not pick up on the fact that the connection has in fact been aborted until you've written some output and done a flush().

In fact, just producing some output periodically may be sufficient for php to notice the connection close itself, and automatically kill your script.

Frank Farmer
Thank you for your answer. Won't this solution trigger the ajax response function each time I echo some data? Making this solution a regular polling and not long-polling.
fibonacci
A: 

It seems common for long-polling requests to timeout after 30 seconds. So in your while loop you could echo 'CLOSE' after 30 seconds.

while(!$new_message && $timer < 30){
    $new_message = NewMessageArrived($current_user);
    if(!$new_message) {
     sleep(10);
     $timer += 10;
    }
}
if($newMessageCount) {
    echo $newMessageCount;
} else {
    echo 'CLOSE';
}


In the Javascript, you can listen for the CLOSE.

function poll_new_messages(){
    xmlhttp=GetXmlHttpObject();
    //...
    xmlhttp.onreadystatechange=got_new_message_count;
    //...
    xmlhttp.send();
}

function got_new_message_count(){
    if (xmlhttp.readyState==4){
     if(xmlhttp.responseText != 'CLOSE') {
      updateMessageCount(xmlhttp.responseText);
     }
     //...
     poll_new_messages();
    }
}

Now, the PHP will return a response within 30 seconds, no matter what. If you use stays on the page, and you receive a CLOSE, you just don't update the count on the page, and re-ask.

If the user moves to a new page, your PHP instance will stop the loop regardless within 30 seconds, and return a response. Being on a new page though, the XHR that cared about that connection no longer exists, so it won't start up another loop.

seanmonstar
Thank you for your answer. My ajax function poll_new_messages() has a response handler got_new_message_count() function. The response handler calls for poll_new_messages(), when triggered.So in both cases I will end up with the same amount of infinite loops or without a response handling function (if on CLOSE I won't call for poll_new_messages again).
fibonacci
in the Javascript you can check for "CLOSE", and if so, re-poll. otherwise, display the count. If the user changes pages, when the loop times out, it wont get restarted from your handler, because the handler is no longer listening (from that page).
seanmonstar
What happens then when another $newMessageCount arrives again, there is no handler for this event? The solution you are proposing will only notify the user once (in case the user won't change a page and start long-polling again).
fibonacci
editted to show what you could do in the JS, and tried to better explain everything in my comments
seanmonstar
Long polling requests timeout after X seconds if X was set with set_time_limit(X). So this is not a problem.In my opinion the idea of long polling is to allow it to be longer than 30 seconds and even if it is only 30 seconds I prefer one single thread running for each user and not one thread per page switch.
fibonacci
well, timing out after 30 seconds is also good for crappier browsers. If a resourse takes too long, the browser can cancel running it, not even allowing you to get a 'CLOSE' notice.
seanmonstar
A: 

I think I found a solution to my problem. I would appreciate if anyone could tell, if this is the technique that is being used in COMET and how scalable this solution.

I used a user based semaphore like this:

$sem_id = sem_get($current_user);
sem_acquire($sem_id);
while(1){
 if(NewMessageArrived($current_user))break;
 sleep(10);
}
sem_release($sem_id);
echo $newMessageCount;
fibonacci