Home » Php » Simple PHP long polling chat script, too simple?

Simple PHP long polling chat script, too simple?

Posted by: admin July 12, 2020 Leave a comment

Questions:

Im working on a simple chat app, probably 10 to 20 users per room.

The Script that queries the database for new messages looks too simple for all the request it’ll be getting.

Below is the block of code that loops for new messages, the rest of the script is just getting the variables, construction of the query and the json response object:

$sleepTime = 1; //Seconds
$data = "";
$timeout = 0;

//Query database for data
while(!$data and $timeout < 10){
    $data = getQuery($sql);
    if(!$data){
        //No new messages on the chat
        flush();
        //Wait for new Messages
        sleep($sleepTime);          
        $timeout += 1;
    }else{
        break;
    }
}

The block above will query the database for new messages every second for 10 seconds, if after the 10 seconds there are no new messages it will notify the browser. The browser waits 5 seconds and then sends another request
to get new messages.

However If the script finds new messages, the browser will request more new messages instantly as soon as it gets the response with the new messages from the server.

This process goes on and on…

So how can i optimize this process any further?
Is this as good as it gets?
Is working fine on my local server, but im afraid that just a few users could overload a live server(shared host) with all the requests and the loopings.

Here is live DEMO you can check with firebug http://pixbush.com/chat/chat.php

How to&Answers:

From your description, it sounds like you have a 5 second gap of silence which defeats the benefit of long-polling. Have the browser start another request immediately when a call returns (long or short) from the server. As a back up, with each server call, have the browser start a timeout slightly longer than the server-side timeout, but cancel it when a request is returned. If the server request ever fails and the browser timeout completes, start a new request.

Answer:

This screams for AJAX.

See my post today on how to send JavaScript responses to PHP. There’s no reason why your script should have to loop at all.


EDIT: My bad about the AJAX. When I wrote the IRC chatbot PHP-Egg, I ran into this problem * 100. The way I solved it (back in the PHP 4 days, mind you) was to pcntl_fork() PHP and have it simply return each time there was a message. The benefits are that it doesn’t 100% block the CPU, unlike sleep() and is MUCH faster than 10 seconds or any arbitrary limit you put on it.


I’m revising my answer again (sorry!):

Use some sort of asynchronous process that dumps the text into a file.

Then what you’d do is

if (filemtime(‘chat.log’) > time() – 5)
{
echo json_encode(file_get_contents(‘chat.log’));
}

Benefits: Limited SQL usage; no need to loop.

Answer:

I’ve been making web-chat and came across just the same solution for keeping real-time updates. So, i wonder whether you have figured it out: is it a good way to keep looping server-side using sleep() function, or maybe it’s better to use more ajax queries instead. And is sleep() function really a good idea and it won’t halt server when several usres are polling?

I see meebo using long-polling (time between queries also depends on window focus i guess) while SO chat app. seems just to be using just ajax queries. So that makes me wonder.

Answer:

You could try using files labeled according to conversationId instead of a DB and just check if the file has been ‘touched’. Also, use usleep and set_time_limit(for windows server) to set your interval in milisecs and increase the sleep time. Usleep actually, delays CPU usage but is still fires instantly incase the file has been changed.

Here is a section of my chat script. =)

define('SUCCESS', '__SUCCESS__');
define('FAILED', '__FAILED__');

$tmpLib = $TMPFOLDER;
$msgPath = $MSGFILE;

$timeout = $POLLSPEEDSEC;

$acct = new Account($tmpLib, $_GET['key']);

if (false === $acct) {
    return false;
}

$msg = new Message($msgPath, $acct);

$lastMod = !empty($_GET['ts']) ? $_GET['ts']: 0;
$lastMod = substr($lastMod, 0, 10);
$lastMod = (int)$lastMod;

$result = array();

$start = gettimeofday();
$prevMsg = $acct->getTemp('cache');

do{
    usleep(10000);

    if ($acct->getFileTime() >= $lastMod) {
        $result['account'] = $acct->getAllOnline();
    }

    if($msg->getFileTime() >= $lastMod) {
        $result['message'] = $msg->fetch();
    }

    if (!empty($result)) {
        $theMsg = json_encode($result);
        if ($theMsg != $prevMsg) {
            $acct->setTemp('cache', $theMsg);
            echo $theMsg;
            flush();
            exit;
        }
        $result = array();
        $lastMod = time();
    }

    $end = gettimeofday();
} while($timeout > ($end['sec'] - $start['sec']));

echo FAILED;