代码之家  ›  专栏  ›  技术社区  ›  Pablo

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

  •  8
  • Pablo  · 技术社区  · 14 年前

    我正在开发一个简单的聊天应用,每个房间大概有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 回复  |  直到 14 年前
        1
  •  3
  •   Billbad    14 年前

    根据你的描述,听起来你有5秒钟的沉默时间,这不利于长时间的投票。当服务器返回呼叫(长或短)时,让浏览器立即启动另一个请求。作为备份,每次服务器调用时,让浏览器启动的超时时间略长于服务器端超时时间,但在返回请求时取消。如果服务器请求失败且浏览器超时完成,请启动新请求。

        2
  •  2
  •   Community kfsone    7 年前

    尖叫 对于AJAX。

    看到我今天的帖子了吗 how to send JavaScript responses to PHP .你的脚本完全没有理由要循环。


    编辑:我对AJAX的不好。当我写IRC聊天机器人的时候 PHP-Egg ,我遇到了这个问题。我解决这个问题的方法(请注意,在PHP的4天中)是 pcntl_fork() PHP并让它在每次出现消息时简单地返回。它的好处是,与sleep()不同,它不会100%阻塞CPU,并且比10秒或任意设置的限制快得多。


    我再次修改我的答案(抱歉!):

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

    那你要做的就是

    如果(filemtime('chat.log')>时间()-5) { echo json_encode(文件获取内容('chat.log'); }

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

        3
  •  1
  •   Tim Cooper    11 年前

    我一直在网上聊天,遇到了同样的解决方案来保持实时更新。所以,我想知道您是否已经弄明白了:使用sleep()函数继续循环服务器端是一种好方法,还是使用更多ajax查询更好。sleep()函数真的是个好主意吗?当几个USRE轮询时,它不会停止服务器?

    我看到meebo在使用长轮询(我猜,两次查询之间的时间也取决于窗口焦点)的同时也在使用聊天应用。似乎只是在使用ajax查询。所以我想知道。

        4
  •  0
  •   gianebao Charitha    14 年前

    您可以尝试使用根据conversationId标记的文件,而不是DB,只需检查文件是否已被“触碰”。此外,使用usleep和set_time_limit(适用于windows server)以毫秒为单位设置间隔,并增加睡眠时间。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;