简单的PHP长轮询聊天脚本,太简单了?

时间:2010-09-02 01:45:58

标签: php chat long-polling

我正在开发一个简单的聊天应用程序,每个房间大概有10到20个用户。

在数据库中查询新消息的脚本对于它将要获得的所有请求来说太简单了。

下面是为新消息循环的代码块,脚本的其余部分只是获取变量,构造查询和json响应对象:

$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;
    }
}

上面的块将每秒查询数据库中的新消息,持续10秒,如果在10秒之后没有新消息,它将通知浏览器。浏览器等待5秒钟,然后发送另一个请求 获取新消息。

但是,如果脚本找到新消息,浏览器会在收到来自服务器的新消息的响应后立即请求更多新消息。

这个过程一直持续......

那么我怎样才能进一步优化这个过程呢? 这是不是很好? 在我的本地服务器上工作正常,但我担心只有少数用户可能会使所有请求和循环的实时服务器(共享主机)过载。

以下是您可以使用firebug http://pixbush.com/chat/chat.php

查看的现场演示

4 个答案:

答案 0 :(得分:3)

根据你的描述,听起来你有5秒的沉默差距,这会打败长轮询的好处。当呼叫从服务器返回(长或短)时,让浏览器立即启动另一个请求。作为备份,在每次服务器调用时,让浏览器启动超时比服务器端超时略长,但在返回请求时取消它。如果服务器请求失败并且浏览器超时完成,请启动新请求。

答案 1 :(得分:2)

AJAX的尖叫

今天在how to send JavaScript responses to PHP上查看我的帖子。你的脚本根本没有必要循环。


编辑:我对AJAX不好。当我编写IRC聊天机器人PHP-Egg时,我遇到了这个问题* 100.我解决它的方式(回到PHP 4天,请注意)是pcntl_fork() PHP并让它返回每个时间有消息。好处是它不会100%阻止CPU,不像sleep(),并且比10秒或任何你施加的任意限制快得多。


我再次修改我的答案(对不起!):

使用某种将文本转储到文件中的异步过程。

那你要做的是

if(filemtime('chat.log')&gt; time() - 5) {     echo json_encode(file_get_contents('chat.log')); }

好处:有限的SQL使用;无需循环。

答案 2 :(得分:1)

我一直在进行网络聊天,并且遇到了保持实时更新的相同解决方案。所以,我想知道你是否已经弄明白:它是使用sleep()函数保持循环服务器端的好方法,或者更好的是使用更多的ajax查询。并且sleep()函数真的是一个好主意,当几个usres轮询时它不会停止服务器吗?

我看到meebo使用长轮询(查询之间的时间也取决于我猜的窗口焦点)而SO聊天应用程序。似乎只是使用ajax查询。所以这让我很好奇。

答案 3 :(得分:0)

您可以尝试使用根据conversationId而不是DB标记的文件,只检查文件是否已被“触摸”。另外,使用usleep和set_time_limit(对于Windows服务器)以milisecs设置间隔并增加睡眠时间。实际上,Usleep会延迟CPU的使用,但是当文件被更改时,它仍会立即触发。

以下是我的聊天脚本的一部分。 =)

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;