服务器发送事件的循环导致页面冻结 [英] While loops for server-sent events are causing page to freeze

查看:43
本文介绍了服务器发送事件的循环导致页面冻结的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我目前正在使用服务器发送事件来接收消息的聊天.但是,我遇到了一个问题.服务器发送的事件永远不会连接并保持挂起状态,因为页面未加载.

I am currently working on a chat that uses Server-Sent Events to receive the messages. However, I am running into a problem. The server-sent event never connects and stays at pending because the page doesn't load.

例如:

<?php
    while(true) {
        echo "data: This is the message.";
        sleep(3);
        ob_flush();
        flush();
    }
?>

我希望每 3 秒数据:这就是消息".将被输出.相反,页面只是不加载.但是,对于服务器发送的事件,我需要这种行为.有没有办法解决这个问题?

I expect that every 3 seconds, "data: This is the message." will be outputted. Instead, the page just doesn't load. However, I need this behavior for server-sent events. Is there a way to fix this?

完整代码:

<?php
   session_start();

    require "connect.php";
    require "user.php";

    session_write_close();

    echo $data["number"];

    header("Content-Type: text/event-stream\n\n");
    header('Cache-Control: no-cache');

    set_time_limit(1200);

    $store = new StdClass(); // STORE LATEST MESSAGES TO COMPARE TO NEW ONES
    $ms = 200; // REFRESH TIMING (in ms)
    $go = true; // MESSAGE CHANGED

    function formateNumber ($n) {
            $areaCode = substr($n, 0, 3);
            $part1 = substr($n, 3, 3);
            $part2 = substr($n, 6, 4);
            return "($areaCode) $part1-$part2";
    }

    function shorten ($str, $mLen, $elp) {
        if (strlen($str) <= $mLen) { 
            return $str;
        } else {
            return rtrim(substr($str, 0, $mLen)) . $elp;
        }
    }

   do {
    $number = $data["number"];
        $sidebarQ = "
            SELECT * 
            FROM (
                SELECT * 
                FROM messages 
                WHERE deleted NOT LIKE '%$number%' 
                AND (
                    `from`='$number' 
                    OR 
                    `to`='$number'
                ) 
                ORDER BY `timestamp` DESC
            ) as mess 
            GROUP BY `id` 
            ORDER BY `timestamp` DESC";
        $query = $mysqli->query($sidebarQ);

        if ($query->num_rows == 0) {
            echo 'data: null' . $number;
            echo "\n\n";
        } else {

            $qr = array();
            while($row = $query->fetch_assoc()) {
                $qr[] = $row;
            }

            foreach ($qr as $c) {
                $id = $c["id"];
                if (!isset($store->{$id})) {
                    $store->{$id} = $c["messageId"];
                    $go = true;
                } else {
                    if ($store->{$id} != $c["messageId"]) {
                        $go = true;
                        $store->{$id} = $c["messageId"];
                    }
                }
            }

            if($go == true) {
                $el = $n = "";

                foreach ($qr as $rows) {
                    $to = $rows["to"];
                    $id = $rows["id"];
                    $choose = $to == $number ? $rows["from"] : $to;
                    $nameQuery = $mysqli->query("SELECT `savedname` FROM `contacts` WHERE `friend`='$choose' AND `number`='$number'");
                    $nameGet = $nameQuery->fetch_assoc();
                    $hasName = $nameQuery->num_rows == 0 ? formateNumber($choose) : $nameGet["savedname"];

                    $new = $mysqli->query("SELECT `id` FROM `messages` WHERE `to`='$number' AND `tostatus`='0' AND `id`='$id'")->num_rows;
                    if ($new > 0) {
                        $n = "<span class='new'>" . $new . "</span>";
                    }

                    $side = "<span style='color:#222'>" . ($to == $number ? "To you:" : "From you:") . "</span>";
                    $el .= "<div class='messageBox sBox" . ($nameQuery->num_rows == 0 ? " noname" : "") . "' onclick=\"GLOBAL.load($id, $choose)\" data-id='$id'><name>$hasName</name><div>$side " . shorten($rows["message"], 25, "...") . "</div>$n</div>";
                }
                echo 'data: '. $el;
                echo "\n\n";

                $go = false;
            }
        }

        echo " ";

        ob_flush();
        flush();
        sleep(2);
    } while(true);
?>

我还想指出,这个无限循环不应该导致这种情况发生.这就是 SSE 通常的设置方式,甚至在 MDN 网站上也是如此.

I would also like to note, that this infinite loop shouldn't be causing this to happen. This is just how SSE's are set up usually and it is even done so on the MDN website.

推荐答案

毫无疑问,现在您已经明白了这一点,但在偶然情况下,您还没有在几个 sse 脚本中使用如下代码魅力.下面的代码是通用的,不具有您的 sql 或记录集处理功能,但这个想法很合理(!?)

No doubt by now you have figured this out but on the offchance you have not I used code like the following on a couple of sse scripts and it worked like a charm. The code below is generic and does not feature your sql or recordset processing but the idea is sound(!?)

<?php
    set_time_limit( 0 );
    ini_set('auto_detect_line_endings', 1);
    ini_set('mysql.connect_timeout','7200');
    ini_set('max_execution_time', '0');

    date_default_timezone_set( 'Europe/London' );
    ob_end_clean();
    gc_enable();



    header('Content-Type: text/event-stream');
    header('Cache-Control: no-cache');
    header('Access-Control-Allow-Credentials: true');
    header('Access-Control-Allow-Methods: GET');
    header('Access-Control-Expose-Headers: X-Events');  




    if( !function_exists('sse_message') ){
        function sse_message( $evtname='chat', $data=null, $retry=1000 ){
            if( !is_null( $data ) ){
                echo "event:".$evtname."\r\n";
                echo "retry:".$retry."\r\n";
                echo "data:" . json_encode( $data, JSON_FORCE_OBJECT|JSON_HEX_QUOT|JSON_HEX_TAG|JSON_HEX_AMP|JSON_HEX_APOS );
                echo "\r\n\r\n";    
            }
        }
    }

    $sleep=1;
    $c=1;

   $pdo=new dbpdo();/* wrapper class for PDO that simplifies using PDO */

    while( true ){
        if( connection_status() != CONNECTION_NORMAL or connection_aborted() ) {
            break;
        }
        /* Infinite loop is running - perform actions you need */

        /* Query database */
        /*
            $sql='select * from `table`';
            $res=$pdo->query($sql);
        */

        /* Process recordset from db */
        /*
        $payload=array();
        foreach( $res as $rs ){
            $payload[]=array('message'=>$rs->message);  
        }
        */

        /* prepare sse message */
        sse_message( 'chat', array('field'=>'blah blah blah','id'=>'XYZ','payload'=>$payload ) );

        /* Send output */
        if( @ob_get_level() > 0 ) for( $i=0; $i < @ob_get_level(); $i++ ) @ob_flush();
        @flush();

        /* wait */
        sleep( $sleep );
        $c++;

        if( $c % 1000 == 0 ){/* I used this whilst streaming twitter data to try to reduce memory leaks */
            gc_collect_cycles();
            $c=1;   
        }
    }



    if( @ob_get_level() > 0 ) {
        for( $i=0; $i < @ob_get_level(); $i++ ) @ob_flush();
        @ob_end_clean();
    }
?>

这篇关于服务器发送事件的循环导致页面冻结的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆