Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

While loops for server-sent events are causing page to freeze

Tags:

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.

For example:

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

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?

Edit:

Full Code:

<?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); ?> 

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.

like image 968
Shawn31313 Avatar asked Apr 06 '15 22:04

Shawn31313


1 Answers

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();     } ?> 
like image 63
Professor Abronsius Avatar answered Sep 23 '22 03:09

Professor Abronsius