zoukankan      html  css  js  c++  java
  • php 实现推技术comet(转)

    实现实时通信一般有两种方式:
    socket或comet。socket是比较好的解决方案,问题在于不是所有的浏览器都兼容,服务器端实现起来也稍微有点麻烦。相比之下,comet(基于HTTP长连接的"服务器推")实现起来更加方便,而且兼容所有的浏览器。所以这次就来说说comet的php实现。

    comet也有好几种实现方式,如iframe, http long request,本文主要探讨http long request实现实时通信。

    先说说http长链接是怎么回事,通俗点讲就是服务器不是一收到请求就直接吐数据,而是在那憋啊憋,一直憋到憋不住了,才告诉你执行结果。

    至于憋多长时间,就看具体应用了,如果憋太久的话,服务器资源的占用也会是个问题。

    现在我们就要通过这种方法来实现实时通信(其实是准实时),先说一下原理:

    1. 客户端发起一个ajax长链接查询,然后服务端就开始执行代码,主要是检查某个文件是否被更新,如果没有,睡一会(sleep),醒来接着检查
    2. 如果客户端又发起了一个查询链接(正常请求),服务端收到后,处理请求,处理完毕后更新某个特定文件的modify time
    3. 这时第一次ajax查询的后台代码还在执行,发现某个文件被更新,说明来了新请求,输出对应的结果
    4. 第一次ajax查询的callback被触发,更新页面,然后再发起一个新的ajax长链接

    <?php
    // NovComet.php
    class NovComet {
        const COMET_OK = 0;
        const COMET_CHANGED = 1;
    
        private $_tries;
        private $_var;
        private $_sleep;
        private $_ids = array();
        private $_callback = null;
    
        public function  __construct($tries = 20, $sleep = 2)
        {
            $this->_tries = $tries;
            $this->_sleep = $sleep;
        }
    
        public function setVar($key, $value)
        {
            $this->_vars[$key] = $value;
        }
    
        public function setTries($tries)
        {
            $this->_tries = $tries;
        }
    
        public function setSleepTime($sleep)
        {
            $this->_sleep = $sleep;
        }
    
        public function setCallbackCheck($callback)
        {
            $this->_callback = $callback;
        }
    
        const DEFAULT_COMET_PATH = "/dev/shm/%s.comet";
    
        public function run() {
            if (is_null($this->_callback)) {
                $defaultCometPAth = self::DEFAULT_COMET_PATH;
                $callback = function($id) use ($defaultCometPAth) {
                    $cometFile = sprintf($defaultCometPAth, $id);
                    return (is_file($cometFile)) ? filemtime($cometFile) : 0;
                };
            } else {
                $callback = $this->_callback;
            }
    
            for ($i = 0; $i < $this->_tries; $i++) {
                foreach ($this->_vars as $id => $timestamp) {
                    if ((integer) $timestamp == 0) {
                        $timestamp = time();
                    }
                    $fileTimestamp = $callback($id);
                    if ($fileTimestamp > $timestamp) {
                        $out[$id] = $fileTimestamp;
                    }
                    clearstatcache();
                }
                if (count($out) > 0) {
                    return json_encode(array('s' => self::COMET_CHANGED, 'k' => $out));
                }
                sleep($this->_sleep);
            }
            return json_encode(array('s' => self::COMET_OK));
        }
    
        public function publish($id)
        {
            return json_encode(touch(sprintf(self::DEFAULT_COMET_PATH, $id)));
        }
    }
    <?php
    // comet.php
    include('NovComet.php');
    
    $comet = new NovComet();
    $publish = filter_input(INPUT_GET, 'publish', FILTER_SANITIZE_STRING);
    if ($publish != '') {
        echo $comet->publish($publish);
    } else {
        foreach (filter_var_array($_GET['subscribed'], FILTER_SANITIZE_NUMBER_INT) as $key => $value) {
            $comet->setVar($key, $value);
        }
        echo $comet->run();
    }
     
        function send(msg){
            $.ajax({
                data : {'msg' : msg},
                type : 'post',
                url : '{:U('Live/SendMsg')}',
                success : function(response){
                   //alert(response);;
                }
            })
        }
        $(document).ready(function(){
            connect();
            $("#btn").click(function(){
                var msg = $('#msg').val();
                send(msg);
                msg.html('');
              });
        })
        public function SendMsg(){
            
            $filename  = './Uploads/live/'.'data.json';
            if ($_POST['msg']!='') {
                file_put_contents($filename,$_POST['msg']);
                $this->ajaxReturn($_POST,'OK',100);
                die();
            }else{
                $this->ajaxReturn($_POST,'on',0);
                die();
            }
            
        }
    
    
    
     
    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
    <html xmlns="http://www.w3.org/1999/xhtml">
    <head>
    <title>Comet demo</title>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <script type="text/javascript" src="./jquery-1.8.2.min.js"></script>
    <script type="text/javascript" src="./json2.js"></script>
    <script>
        var timestamp = 0;
        var url = 'backend.php';
        var error = false;
        // 通过ajax建立和php端处理函数的连接(通过递归调用建立长时间的连接)
        function connect(){
            $.ajax({
                data : {'timestamp' : timestamp},
                url : url,
                type : 'get',
                timeout : 0,
                success : function(response){
                    var data = JSON.parse(response);
                    error = false;
                    timestamp = data.timestamp;
                    if (data.msg != undefined && data.msg != "")
                    {
                        $("#content").append("<div>" + data.msg + "</div>");
                    }
                },
                error : function(){
                    error = true;
                    setTimeout(function(){ connect();}, 5000);
                },
                complete : function(){
                    if (error)
                        // 请求有错误时,延迟5s再连接
                        setTimeout(function(){connect();}, 5000);
                    else
                        connect();
                }
            })
        }
        // 发送信息
        function send(msg){
            $.ajax({
                data : {'msg' : msg},
                type : 'get',
                url : url
            })
        }
        // 创建长时间的连接
        $(document).ready(function(){
            connect();
        })
    </script>
    </head>
    <body>
        <div id="content"></div>
            <form action="" method="get" 
    onsubmit="send($('#word').val());$('#word').val('');return false;">
                <input type="text" name="word" id="word" value="" />
                <input type="submit" name="submit" value="Send" />
            </form>
        </body>
    </html>
    <?php
    // 设置请求运行时间不限制,解决因为超过服务器运行时间而结束请求
    ini_set("max_execution_time", "0");
    
    $filename  = dirname(__FILE__).'/data.txt';
    $msg = isset($_GET['msg']) ? $_GET['msg'] : '';
    
    // 判断页面提交过来的修改内容是否为空,不为空则将内容写入文件,并中断流程
    if ($msg != '')
    {
        file_put_contents($filename,$msg);
        exit;
    }
    
    /* 获取文件上次修改时间戳 和 当前获取到的最近一次文件修改时间戳
     * 文件上次修改时间戳 初始 默认值为0
     * 最近一次文件修改时间戳 通过 函数 filemtime()获取
     */
    $lastmodif    = isset($_GET['timestamp']) ? $_GET['timestamp'] : 0;
    clearstatcache();  // 清除文件状态缓存
    $currentmodif = filemtime($filename);
    
    /* 如果当前返回的文件修改unix时间戳小于或等于上次的修改时间,
     * 表明文件没有更新不需要推送消息
     * 如果当前返回的文件修改unix时间戳大于上次的修改时间
     * 表明文件有更新需要输出修改的内容作为推送消息
     */
    while ($currentmodif <= $lastmodif)
    {
        usleep(10000);     // 休眠10ms释放cpu的占用
        clearstatcache();  // 清除文件状态缓存
        $currentmodif = filemtime($filename);
    }
    
    // 推送信息处理(需要推送说明文件有更改,推送信息包含本次修改时间、内容)
    $response = array();
    $response['msg'] = file_get_contents($filename);
    $response['timestamp'] = $currentmodif;
    echo json_encode($response);
    flush();
    ?>

    最后,话说,php真不适合干这个,我觉得用nodejs 写是最轻松的,erlang好像也不错

  • 相关阅读:
    二分图最大匹配的K&#246;nig定理及其证明
    HDOJ 2389 Rain on your Parade
    HDOJ 1083 Courses
    HDOJ 2063 过山车
    POJ 1469 COURSES
    UESTC 1817 Complete Building the Houses
    POJ 3464 ACM Computer Factory
    POJ 1459 Power Network
    HDOJ 1532 Drainage Ditches
    HDU 1017 A Mathematical Curiosity
  • 原文地址:https://www.cnblogs.com/jackluo/p/3728933.html
Copyright © 2011-2022 走看看