HTTP push with multipart/x-mixed-replace php code not working - php

Have copied some code from the internet to try a HTTP push for images being constantly chnaged by another php code (basically attempting a streaming for images).
but the page does not seem to show any image at all. following is the complete code taken from http://www.howtocreate.co.uk/php/dnld.php?file=6&action=1
function doServerPush($file,$type,$poll) {
if( !file_exists($file) ) {
//on first load, the file must exist, or it ends up sending a broken file
header('Content-type: text/html',true,404);
}
#set_time_limit(0); //PHP must not terminate this script
#ignore_user_abort(false); //do not ignore user aborts (may not be allowed, so status is checked in the loop)
#ini_set( 'zlib.output_compression', 'off' );
$poll *= 1000;
//need a unique boundary
//while it is possible to get a unique boundary for the current image data, it is possible that it will conflict
//with future image data - can't help this, just have to live with it - might as well use a static string
$separator = 'MTWJ_serverpush_boundary_';
for( $i = 0, $randChars = Array('A','B'); $i < 20; $i++ ) {
$separator .= $randChars[rand(0,1)];
}
header('Cache-Control: no-cache');
header('Pragma: no-cache');
//the header that makes server push work
header("Content-Type: multipart/x-mixed-replace;boundary=$separator",true);
$extrabreaks = '';
do {
//send one file starting with a multipart boundary
$filelen = filesize($file);
print "$extrabreaks--$separator\n";
if( !$extrabreaks ) { $extrabreaks = "\n\n"; } //Safari needs exactly one blank line, no extra linebreaks on the first one
print "Content-Type: $type\n";
print "Content-Length: $filelen\n\n";
readfile($file);
//Safari needs 200+ bytes between headers - IE needs 256+ per flush, and this will also take care of that, when IE gets server push
print str_pad('',200-$filelen); //whitespace here is ignored because of content-length
$lastupdsate = filemtime($file);
ob_flush();
flush();
$looptime = time();
do {
clearstatcache(); //need updated file info
usleep($poll);
if( time() - $looptime >= 10 ) {
//every 10 seconds, force it to re-evaluate if the user has disconnected (connection_aborted does not know until this happens)
//most use-cases will not need this protection because they will keep trying to send updates, but it is here just in case
$looptime = time();
print ' '; //whitespace is ignored at this specific point in the stream
ob_flush();
flush();
}
//poll the filesystem until the file changes, then send the update
//the file may not always exist for the instant it is being replaced on the filesystem
//nested loop and filemtime check inspired by http://web.they.org/software/php-push.php
} while( !connection_aborted() && ( !file_exists($file) || ( $lastupdsate == filemtime($file) ) ) );
} while( !connection_aborted() ); //if aborts are ignored, exit anyway to avoid endless threads
}
?>
the php file calling the code is as follows:
require ("serverpush.php");
doServerPush("img.jpg",'image/jpeg',75);
and the html is as follows:
<html>
<head>
<title>Streaming</title>
<style type="text/css" media="screen">
img
{
width: 200px;
height: 200px;
border: 2px solid #ABCDEF;
}
</style>
</head>
<body>
<h1>Image stream test</h1>
<img src="Stream.php" alt="Stream Image" />
</body>
</html>
any assistance will be apprecited. have tried to run the code in Firefox and Chrome

Related

How to fetch data every second from MYSQL database

I'm trying to make a Chat App using HTML, CSS, JS, PHP and Mysql.
I've completed all the functionalities that includes sending a message, receiving a message, displaying users... But the issue i'm facing is that i need to refresh the page every time i received a new message.
I'm looking for a way to auto update data with new data from mysql database.
Code:
<?php
if ($_GET['id']){
$id = $_GET['id'];
$id = preg_replace("/[^0-9]/", "", $id);
$fetching_messages = "SELECT * FROM users_messages WHERE from_user='$id' OR to_user='$id' ORDER BY id";
$check_fetching_messages = $db->prepare($fetching_messages);
$check_fetching_messages->execute();
$messages_all = $check_fetching_messages->fetchAll();
} else {
}
?>
<div id="autodata">
<?php foreach($to_users as $to_user) : ?>
<?php
$to_user_id = $to_user['to_user'];
$to_user_name = "SELECT * FROM users_accounts WHERE id='$to_user_id'";
$check_to_user_name = $db->query($to_user_name);
while ($row_to_user_name = $check_to_user_name->fetch()) {
$id_user = $row_to_user_name['id'];
$username = $row_to_user_name['username'];
$pdp = $row_to_user_name['profile_image'];
}
if ($id_user == $user_id){
} else {
echo '
<form style="height: fit-content;" name="goto'.$to_user_id.'" action="inbox.php">
<div onclick="window.location.replace('."'".'?id='.$to_user_id."'".')" class="inbox_chat_field_user">';
if (empty($pdp)){
echo "<img class='inbox_chat_field_user_img' src='uploads\profile\default.jpg'/>";
} else {
echo "<img class='inbox_chat_field_user_img' src='".$pdp."'/>";
}
echo '
<span class="inbox_chat_field_user_p">'.$username.'</span>
</div>
</form>
<hr class="inbox_separing_hr">';
}
?>
<?php endforeach;?>
</div>
Simply you can't do that, PHP is a server-side language, you can't tell the clients to refresh from PHP.
To accomplish that chat you should consider JavaScript in the browser.
The easiest way is by sending an AJAX request to your server and check if there are new messages every 5 or 10 seconds, and then do what you want with the messages in the response.
If you use jquery in your application you can send ajax request in this way:
$.get( "messages.php", function( data ) {
console.log( "Data Loaded: " + data );
});
and in messages.php script, you can fetch new messages from the database and return them with HTML or JSON format
You may also use FCM service offered by firebase to push your messages to the client directly, Check this package for PHP FCM.
There are other solutions like websockets etc...
It would have been easier for me to directly update your code had you separated business logic from presentation, so I am not going to attempt to do that. Instead I will describe a technique you can use and leave it to you to figure out the best way to use it. You might consider using server-sent events. See the JavaScript class EventSource.
The following "business logic" PHP program, sse_cgi.php, periodically has new output every 2 seconds (for a total of 5 times). In this case the output is just the current date and time as a string. But it could be, for example, a JSON record. Note the special header that it outputs:
<?php
header("Content-Type: text/event-stream");
$firstTime = True;
for ($i = 0; $i < 5; $i++) {
if (connection_aborted()) {
break;
}
$curDate = date(DATE_ISO8601);
echo 'data: This is a message at time ' . $curDate, "\n\n";
// flush the output buffer and send echoed messages to the browser
while (ob_get_level() > 0) {
ob_end_flush();
}
flush();
if ($i < 4) {
sleep(2); # Sleep for 2 seconds
}
}
And this is the presentation HTML that would be outputted. JavaScript code in this case is just replacing the old date with the updated value. It could just as well append new <li> elements to an existing <ul> tag or <tr> elements to an existing <table>.
<html>
<head>
<meta charset="UTF-8">
<title>Server-sent events demo</title>
</head>
<body>
<div id='date'></div>
<script>
var evtSource = new EventSource('sse_cgi.php');
var date = document.getElementById('date');
evtSource.onmessage = function(e) {
// replace old content
date.innerHTML = e.data;
};
evtSource.onerror = function() {
// occurs when script terminates:
evtSource.close();
console.log('Done!');
};
</script>
</body>
</html>
Note that this presentation references the "business logic" scripts that returns the successive dates.
Important Note
It is important to realize that this technique keeps the connection to the server open for the duration until all the data has been ultimately sent and the business logic script ultimately terminates (or the presentation running in the browser issues a call to evtSource.close() to close the connection). So if you have a lot of simultaneous users, this could be an issue.
If your application does not have a limited number of messages to return then the previously described problem can be overcome by having the business logic script return immediately after having sent one message. This will break the connection with the browser, which if it is still there, will automatically attempt to re-connect with the business logic script (note that this reconnection can take a while):
Updated Business Logic
<?php
header("Content-Type: text/event-stream");
# Simulate waiting for next message:
sleep(2);
$curDate = date(DATE_ISO8601);
echo 'data: This is a message at time ' . $curDate, "\n\n";
// flush the output buffer and send echoed messages to the browser
while (ob_get_level() > 0) {
ob_end_flush();
}
flush();
Updated Presentation
<html>
<head>
<meta charset="UTF-8">
<title>Server-sent events demo</title>
</head>
<body>
<div id='date'></div>
<script>
var evtSource = new EventSource('sse_cgi.php');
var date = document.getElementById('date');
evtSource.onmessage = function(e) {
// replace old content
date.innerHTML = e.data;
};
</script>
</body>
</html>

How to continuously watch a directory for the presence of files with a certain file-extension?

For example, I want to continuously monitor a directory for the presence of .xml files.
As soon as a .xml file is found in that directory, the program should start processing on the file, e.g. reading the data inside the file, extracting useful data and further actions.
Solution I tried to use:
I tried to use an INFINITE while loop to continuously monitor the directory, and used glob() to check for the presence of files with .xml extension. glob() returns an array with paths of all files found.
So then within the infinite while loop, I check if the array returned by glob() is NON-EMPTY. If yes, then I read the file at each path in the array returned by glob() and do appropriate processing on the file.
The problem is that when i run it, I get
Fatal error: Maximum execution time of 30 seconds exceeded in C:\xampp\htdocs\Alpha\index.php on line 9
I believe this is due to the infinite while loop.
Questions:
Is my way of solving my problem a good way?
What should I do to bypass the above error?
As an example of using SSE to achieve your stated intention of monitoring a directory
The PHP script that does the directory scans - in this example it is a simple glob but could be much more sophisticated using recursiveIterator etc
<?php
/*
sse.php
*/
set_time_limit( 0 );
ini_set('auto_detect_line_endings', 1);
ini_set('max_execution_time', '0');
ob_end_clean();
/* -- set headers -- */
header('Content-Type: text/event-stream'); /* !important! */
header('Cache-Control: no-cache');
header('Access-Control-Allow-Credentials: true');
header('Access-Control-Allow-Methods: GET');
header('Access-Control-Expose-Headers: X-Events');
/* -- utility function to send formatted sse message -- */
if( !function_exists('sse_message') ){
function sse_message( $evtname='dirmon', $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";
}
}
}
/* How often messages will be pushed to the client app */
$sleep = 5;
/* The directory to monitor */
$dir = 'c:/temp/';
/* The name of the event to monitor in js client app */
$evt = 'dirmon';
while( true ){
if( connection_status() != CONNECTION_NORMAL or connection_aborted() ) {
break;
}
/* Methods here to scan directory and produce data to send */
$files=glob( $dir . '*.xml' );
$count = count( $files );
$payload=array(
'dir' => $dir,
'files' => $count
);
/* -- prepare and send sse message -- */
sse_message( $evt, $payload );
/* -- Send output -- */
if( #ob_get_level() > 0 ) for( $i=0; $i < #ob_get_level(); $i++ ) #ob_flush();
#flush();
/* wait */
sleep( $sleep );
}
if( #ob_get_level() > 0 ) {
for( $i=0; $i < #ob_get_level(); $i++ ) #ob_flush();
#ob_end_clean();
}
?>
The client page
<!doctype html>
<html>
<head>
<title>SSE</title>
<script type='text/javascript'>
var count=0;
function bindEvtSource(){
var evt='dirmon';
var url='/sse.php';
if ( !!window.EventSource ) {
var evtSource = new EventSource( url );
evtSource.addEventListener( 'open', function(e){
console.log(e.type);
},false);
evtSource.addEventListener( 'error', function(e){
console.error('%o %s',e,e.type);
},false);
evtSource.addEventListener( evt, function(e){
var json=JSON.parse(e.data);
var files=json.files;
if( files!=count ) {
count=files;
alert('contents changed - process changes! Send Ajax Request? etc etc');
}
document.getElementById('dirdata').innerHTML=e.data;
},false);
} else {
alert('Server Sent Events are not supported in this browser');
}
}
document.addEventListener( 'DOMContentLoaded', bindEvtSource, false );
</script>
</head>
<body>
<div id='dirdata'></div>
</body>
</html>
You could use the response to then perform further actions by initiating a function within js - ie: send Ajax Request to a script that will do whatever processing you require ( Server Sent Events are uni-directional and you can send / receive - only receive! )

Echo current date and time and free space on disk every second in php

I'm new to php and I'm trying to get the current date and time and the available space on disk and update it every second on a web page.
For the date and time I use: date("d-m-Y H:i:s").
For getting the free space I know I can use the diskfreespace() function that takes the path as argument.
In my case I'm trying with diskfreespace("C:").
It returns the number of bytes and since I have Gigabytes of space I divide the number of bytes in order to get the number of Gigabytes.
diskfreespace("C:") / pow(1024, 3)
It works though it's executed only once and I'd like the function to execute every second and display the value through the echo function.
Then I tried using an infinite loop with a sleep() of 1 second but it seems there is a problem because the values aren't updated every second and it seems like if the page doesn't load properly.
<?php
while(1)
{
echo "Current date and time: " . date("d-m-Y H:i:s");
echo "</br></br>Free space on C: disk " . (diskfreespace("C:") / pow(1024, 3)) . " Gb";
sleep(1);
}
?>
If you use Server Sent Events you can have a connection to a PHP script that runs in an infinite loop that pushes out the data to a javascript listener.
<?php
/*
diskspace_sse.php
*/
set_time_limit( 0 );
ini_set('auto_detect_line_endings', 1);
ini_set('max_execution_time', '0');
/* -- Edit to suit your location -- */
date_default_timezone_set( 'Europe/London' );
ob_end_clean();
/* -- set headers -- */
header('Content-Type: text/event-stream'); /* !important! */
header('Cache-Control: no-cache');
header('Access-Control-Allow-Credentials: true');
header('Access-Control-Allow-Methods: GET');
header('Access-Control-Expose-Headers: X-Events');
/* -- utility function to send formatted sse message -- */
if( !function_exists('sse_message') ){
function sse_message( $evtname='gas', $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";
}
}
}
/* -- How often to send messages -- */
$sleep=1;
$disk='c:';
while( true ){
if( connection_status() != CONNECTION_NORMAL or connection_aborted() ) {
break;
}
/* Infinite loop is running - perform actions you need */
$payload=array(
'date' => date(DATE_COOKIE),
'diskspace' => disk_free_space($disk),
'totalspace'=> disk_total_space($disk),
'formatted_diskspace' => round( disk_free_space($disk) / pow( 1024,3 ), 2 ).'Gb',
'formatted_total' => round( disk_total_space($disk) / pow( 1024,3 ), 2 ).'Gb'
);
/* -- prepare sse message -- */
sse_message( 'diskspace', $payload );
/* -- Send output -- */
if( #ob_get_level() > 0 ) for( $i=0; $i < #ob_get_level(); $i++ ) #ob_flush();
#flush();
/* wait */
sleep( $sleep );
}
if( #ob_get_level() > 0 ) {
for( $i=0; $i < #ob_get_level(); $i++ ) #ob_flush();
#ob_end_clean();
}
?>
In your html page
<div id='diskspace'></div>
<script type='text/javascript'>
function bindEvtSource(){
var url='http://localhost/diskspace_sse.php';
if ( !!window.EventSource ) {
var evtSource = new EventSource( url );
evtSource.addEventListener( 'open', function(e){
console.log(e.type);
},false);
evtSource.addEventListener( 'error', function(e){
console.error('%o %s',e,e.type);
},false);
evtSource.addEventListener( 'diskspace', function(e){
var json=JSON.parse(e.data);
/* you could work with the json data here */
getobject('diskspace').innerHTML=e.data;
},false);
} else {
alert('Server Sent Events are not supported in this browser');
}
}
document.addEventListener( 'DOMContentLoaded', bindEvtSource, false );
</script>
You should get feedback about the diskspace usage every second with no ( very little ) slowdown in page load times.
Echo shows result after code finishs the job. In your case, it may work in console application, not in web. To get your goal you have to look at ajax. In front side you need to call your php code, and render result. After a second do it again.
Check this answer.
Also, instead of the infinite loop in PHP, you may want to implement a simple polling mechanism with AJAX requests. Otherwise, depending on your server settings, your infinite loop may be stopped after a period of time. See max_execution_time.

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

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.
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();
}
?>
While this is not a direct answer as to the problem, try using this method to find the error.. Your not getting errors, but this should help you find them maybe?
Basically you want to have a simple PHP script which includes your main script, but this page enables errors... Example below..
index.php / Simple Error Includer
<?php
ini_set('display_errors',1);
ini_set('display_startup_errors',1);
error_reporting(-1);
require "other.php";
?>
other.php / You Main Script
<?php
ini_set('display_errors',1);
ini_set('display_startup_errors',1);
error_reporting(-1);
weqwe qweqeq
qweqweqweqwe
?>
If you create a setup like this, if you view index.php you will see the following error Parse error: syntax error, unexpected 'qweqeq' (T_STRING) in /var/www/html/syntax_errors/other.php on line 5 because it does not have an invalid syntax on the main page and allows any includes to be error checked..
But if you where to view other.php, you would simply get a white / blank page because its unable to validate the whole page/script.
I use this method in my projects, that way regardless of what i do in other.php or any linked php pages, i will see an error report for them.
Please understand the code before commenting
to say this disables error control means you did not bother to RTM
Fill the buffer
Another issue in the past that i remember was filling the buffer before it would output to the browser. So try something like this before your loop.
echo str_repeat("\n",4096); // Exceed the required browser threshold
for($i=0;$i<70;$i++) {
echo "something as normal";
flush();
sleep(1);
}
Examples at http://www.sitepoint.com/php-streaming-output-buffering-explained/
It seems like the sleep function is interfering with the output. Putting the sleep function AFTERWARDS did work:
<?php
while(true) {
echo "data: This is the message.";
ob_flush();
flush();
sleep(3);
}
As other people suggest, I would encourage to use AJAX instead of an infinite loop, but that was not your question.
One thing I have noticed here is sleep() function in combination with ob_start() and - THERE IS NO - ob_start() anywhere in the full code example, yet there is flush() and ob_flush() ..
What are you flushing anyway?
And why not simply ob_end_flush() ?
The thing is that sleep() than echo(), than sleep() again, than echo() again, etc, etc.. has no effect when output buffering is turned on. Sleep function works as expected when output buffering is not in play - in between. In fact, it might *(and it will) produce quite unexpected results, and those results won't be the one we want to see.
The following code works fine here, also using Mayhem his str_repeat function to add 4k of data (that is usually the minimum for a tcp packet to be flushed by php)
echo str_repeat(' ', 4096);
while(true)
{
echo "data: This is the message.";
flush();
sleep(3);
}
Instead of using loop try this code given below which is working(tested myself) fine as per your requirement
echo "data: This is the message.";
$url1="<your-page-name>.php";
header("Refresh: 5; URL=$url1");
what this will do is it will call itself every 5 seconds (in your case set it to 3 instead of 5) and echo the output.
I am going to take a chance and state the obvious,
you could query the server every 3 seconds, and let the client do the waiting...
This could be done easily with javascript
for example, try this code and name if file.php
<?php
$action='';
if (array_key_exists('action',$_GET))
{$action=$_GET['action'];}
if ($action=='poll')
{
echo "this message will be sent every 3 sec";
}
else
{
?><HTML><HEAD>
<SCRIPT SRC="http://code.jquery.com/jquery-2.1.3.min.js"></SCRIPT>
<SCRIPT>
function doPoll()
{
$('#response').append($.get("file.php?action=poll"));
setTimeout(doPoll, 3000);
}
doPoll();
</SCRIPT>
</HEAD><BODY><DIV id="response"></DIV></BODY></HTML><?php
}
Could it be as simple as the script timing out?
Eventually PHP scripts self terminate if they run for too long. The solution for when you don't want this to happen is to keep resetting the time out.
So a simple addition might be all you need:
<?php
while(true) {
echo "data: This is the message.";
set_time_limit(30);
sleep(3);
ob_flush();
flush();
}
?>
Of course, that might not be it but my gut instinct is that this is the problem.
http://php.net/manual/en/function.set-time-limit.php
UPDATE: I noticed in the comments that you are using some free hosting. If they are running PHP in safe mode then you cannot reset your timeout.
I had the same issue and finally found the easy and quick solution on kevin choppin's blog:
Session Locks
First and foremost, if you're using sessions for whatever reason you will need to make them read-only on the stream. If they're writable, this will lock them everywhere else, so any page loads will hang while the server waits for them to become writable again. This is easily fixed by calling; session_write_close();
I suggest using if() statement instead of using while. And in your case your condition is always true, hence it is in infinite loop.

php while loop high cpu usage

I have an ipcamera from which /video/feed/1.jpg (mounted as a ramdrive) is being written to at approx 5fps. Sometimes this can be less than 1 fps if the connection is poor.
I'm trying to update the image in a browser every 500ms but i have two goals:
I don't want the server to output the same image if it has not been updated by the camera.
I dont want to output a partial image if the camera had not finished writing it yet.
I tried to achieve this by creating an md5 of the image and storing it in the session, if on the next browser request the md5 is unchanged, the server loops until the md5 is different. The server will also loop until the md5 matches the previous time it was loaded, this way I can be sure that the camera had finished creating the image.
The process works as expected but the cpu usage goes through the roof, so I'm looking for suggestions on improvements.
test.php
<?php
session_start();
$imgFile = '/video/feed/1.jpg';
$lastImg = $_SESSION['image'];
$imgMd5 =0;
do {
sleep(.2);
$img = (file_get_contents($imgFile));
$lastMd5 = $imgMd5;
$imgMd5 = md5($img);
if ($lastMd5 != $imgMd5) {
continue;
}
if ($imgMd5 != $lastImg) {
break;
}
} while (0 == 0);
header("Content-type: image/jpg");
$_SESSION['image'] = md5($img);
echo $img;
exit;
?>
JS
<script>
img = new Image
function f() {
img.src = "test.php?rnd=" + Date.now();
img.onload = function() {
feed.src = img.src;
setTimeout(function() {
f();
}, 500);
};
img.onerror= function(){
setTimeout(function() {
f();
}, 500);
};
}
f();
</script>
What I really needed was usleep(200000)
sleep(.2) did not work as I expected.
Instead of using an MD5 hash to check if the file changed, use the last modified time which should be less resource-intensive than computing an MD5 hash each time (but for some reason on my Windows machine it still uses a lot of CPU so I'm not sure), you can try my code :
<?php
session_start();
$path = "/video/feed/1.jpg";
if (isset($_SESSION["lastmodified"])) { // if there's no previous timestamp, do not execute all of this and jump straight into serving the image.
while ($_SESSION["lastmodified"] == filemtime($path)) {
sleep(1); // sleep while timestamp is the same as before.
clearstatcache(); // PHP caches file informations such as last modified timestamp, so we need to clear it each time or we'll run into an infinite loop.
}
}
$_SESSION["lastmodified"] = filemtime($path); // set the new time variable.
header("Content-type: image/jpg");
echo file_get_contents($path);
?>
Edit: you can just let your webserver handle all of this by directly serving the RAMdisk directory, setting appropriate cache-control headers and using a meta refresh tag to reload the page every second. When the page reloads, the webserver will serve a new image only if it exists (and if not, it'll just return "not modified" to the browser and it'll serve the last image from its cache).

Categories