使用 while 循环时服务器端 PHP 事件页面未加载
Server-side PHP event page not loading when using while loop
我有一个名为 handler.php 的文件,它从文本文件中读取数据并将其推送到客户端页面。
相关客户端代码:
<script>
if(typeof(EventSource) !== "undefined") {
var source = new EventSource("handler.php");
source.onmessage = function(event) {
var textarea = document.getElementById("subtitles");
textarea.value += event.data;
textarea.scrollTop = textarea.scrollHeight;
};
} else {
document.getElementById("subtitles").value = "Server-sent events not supported.";
}
</script>
Handler.php代码:
$id = 0;
$event = 'event1';
$oldValue = null;
header('Content-Type: text/event-stream');
header('Cache-Control: no-cache');
header('X-Accel-Buffering: no');
while(true){
try {
$data = file_get_contents('liveData.txt');
} catch(Exception $e) {
$data = $e->getMessage();
}
if ($oldValue !== $data) {
$oldValue = $data;
echo 'id: ' . $id++ . PHP_EOL;
echo 'event: ' . $event . PHP_EOL;
echo 'retry: 2000' . PHP_EOL;
echo 'data: ' . json_encode($data) . PHP_EOL;
echo PHP_EOL;
@ob_flush();
@flush();
sleep(1);
}
}
使用循环时,handler.php 永远不会加载,因此客户端不会收到任何数据。在 Chrome 开发者网络选项卡中,handler.php 显示为 "Pending",然后显示为 "Cancelled"。文件本身保持锁定状态大约 30 秒。
但是,如果我删除 while 循环(如下所示),handler.php 是 加载并且客户端 是 接收数据(仅一次,即使 liveData.txt 文件不断更新)。
Handler.php 无循环:
$id = 0;
$event = 'event1';
$oldValue = null;
header('Content-Type: text/event-stream');
header('Cache-Control: no-cache');
header('X-Accel-Buffering: no');
try {
$data = file_get_contents('liveData.txt');
} catch(Exception $e) {
$data = $e->getMessage();
}
if ($oldValue !== $data) {
$oldValue = $data;
echo 'id: ' . $id++ . PHP_EOL;
echo 'event: ' . $event . PHP_EOL;
echo 'retry: 2000' . PHP_EOL;
echo 'data: ' . json_encode($data) . PHP_EOL;
echo PHP_EOL;
@ob_flush();
@flush();
}
我正在使用 SSE,因为我只需要单向通信(因此 websockets 可能有点矫枉过正)而且我真的不想使用轮询。如果我无法解决这个问题,我可能不得不这样做。
我认为您对网络的工作方式有疑问。 PHP 代码不会 运行 在您的浏览器中 - 它只是创建一些东西,网络服务器通过网络传递给浏览器。
从服务器加载页面后就是这样。您将需要实施一些轮询更改的东西。
我完成此操作的一种方法是将页面置于一个循环中,该循环会刷新并因此每隔一秒左右用新数据再次获取页面(但如果有很多人,这可能会使您的服务器严重超载在该页面上)。
唯一的其他解决方案是使用推送技术和一个 javascript 框架,它可以接受推送并重新填充页面的相关部分,或者在计时器上使用 javascript 循环来拉取数据.
When using the loop, handler.php is never loaded so the client doesn't
get sent any data. In the Chrome developer network tab, handler.php is
shown as "Pending" and then "Cancelled". The file itself stays locked
for around 30 seconds.
这是因为网络服务器(Apache)或浏览器甚至PHP本身在30秒内没有响应时会取消请求。
所以我猜冲洗不起作用,尝试主动启动和结束缓冲区而不使用 @
函数,以便在出现错误时得到线索。
// Start output buffer
ob_start();
// Write content
echo '';
// Flush output buffer
ob_end_flush();
据我所知,SSE 连接的客户端看起来还不错 - 尽管我将 var textarea.....
移到了 onmessage
处理程序之外。
更新:我应该仔细观察,但要监视的事件是 event1
,因此我们需要为该事件设置一个事件侦听器。
<script>
if( typeof( EventSource ) !== "undefined" ) {
var url = 'handler.php'
var source = new EventSource( url );
var textarea = document.getElementById("subtitles");
source.addEventListener('event1', function(e){
textarea.value += e.data;
textarea.scrollTop = textarea.scrollHeight;
console.info(e.data);
},false );
} else {
document.getElementById("subtitles").value = "Server-sent events not supported.";
}
</script>
关于SSE服务器脚本我倾向于采用这样的方法
<?php
/* make sure the script does not timeout */
set_time_limit( 0 );
ini_set('auto_detect_line_endings', 1);
ini_set('max_execution_time', '0');
/* start fresh */
ob_end_clean();
/* ultility function for sending SSE messages */
function sse( $evtname='sse', $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";
}
}
$id = 0;
$event = 'event1';
$oldValue = null;
header('Content-Type: text/event-stream');
header('Cache-Control: no-cache');
header('X-Accel-Buffering: no');
while( true ){
try {
$data = @file_get_contents( 'liveData.txt' );
} catch( Exception $e ) {
$data = $e->getMessage();
}
if( $oldValue !== $data ) {
/* data has changed or first iteration */
$oldValue = $data;
/* send the sse message */
sse( $event, $data );
/* make sure all buffers are cleansed */
if( @ob_get_level() > 0 ) for( $i=0; $i < @ob_get_level(); $i++ ) @ob_flush();
@flush();
}
/*
sleep each iteration regardless of whether the data has changed or not....
*/
sleep(1);
}
if( @ob_get_level() > 0 ) {
for( $i=0; $i < @ob_get_level(); $i++ ) @ob_flush();
@ob_end_clean();
}
?>
(代表问题作者发布解决方案).
成功!第n次调试时,我决定返璞归真,重新开始。我取消了循环并将 PHP 代码减少到最低限度,但保留了 RamRaider 提供的客户端代码。现在一切都很好!通过调整重试值,我可以准确指定数据推送的频率。
PHP(服务器端):
<?php
$id = 0;
$event = 'event1';
$oldValue = null;
header('Content-Type: text/event-stream');
header('Cache-Control: no-cache');
header('X-Accel-Buffering: no');
try {
$data = file_get_contents('liveData.txt');
} catch(Exception $e) {
$data = $e->getMessage();
}
if ($oldValue !== $data) {
$oldValue = $data;
echo 'id: ' . $id++ . PHP_EOL;
echo 'event: ' . $event . PHP_EOL;
echo 'retry: 500' . PHP_EOL;
echo "data: {$data}\n\n";
echo PHP_EOL;
@ob_flush();
@flush();
}
?>
Javascript(客户端):
<script>
if ( typeof(EventSource ) !== "undefined") {
var url = 'handler.php'
var source = new EventSource( url );
var textarea = document.getElementById("subtitles");
source.addEventListener('event1', function(e){
textarea.value += e.data;
textarea.scrollTop = textarea.scrollHeight;
console.info(e.data);
}, false );
} else {
document.getElementById("subtitles").value = "Server-sent events not supported.";
}
</script>
我有一个名为 handler.php 的文件,它从文本文件中读取数据并将其推送到客户端页面。
相关客户端代码:
<script>
if(typeof(EventSource) !== "undefined") {
var source = new EventSource("handler.php");
source.onmessage = function(event) {
var textarea = document.getElementById("subtitles");
textarea.value += event.data;
textarea.scrollTop = textarea.scrollHeight;
};
} else {
document.getElementById("subtitles").value = "Server-sent events not supported.";
}
</script>
Handler.php代码:
$id = 0;
$event = 'event1';
$oldValue = null;
header('Content-Type: text/event-stream');
header('Cache-Control: no-cache');
header('X-Accel-Buffering: no');
while(true){
try {
$data = file_get_contents('liveData.txt');
} catch(Exception $e) {
$data = $e->getMessage();
}
if ($oldValue !== $data) {
$oldValue = $data;
echo 'id: ' . $id++ . PHP_EOL;
echo 'event: ' . $event . PHP_EOL;
echo 'retry: 2000' . PHP_EOL;
echo 'data: ' . json_encode($data) . PHP_EOL;
echo PHP_EOL;
@ob_flush();
@flush();
sleep(1);
}
}
使用循环时,handler.php 永远不会加载,因此客户端不会收到任何数据。在 Chrome 开发者网络选项卡中,handler.php 显示为 "Pending",然后显示为 "Cancelled"。文件本身保持锁定状态大约 30 秒。
但是,如果我删除 while 循环(如下所示),handler.php 是 加载并且客户端 是 接收数据(仅一次,即使 liveData.txt 文件不断更新)。
Handler.php 无循环:
$id = 0;
$event = 'event1';
$oldValue = null;
header('Content-Type: text/event-stream');
header('Cache-Control: no-cache');
header('X-Accel-Buffering: no');
try {
$data = file_get_contents('liveData.txt');
} catch(Exception $e) {
$data = $e->getMessage();
}
if ($oldValue !== $data) {
$oldValue = $data;
echo 'id: ' . $id++ . PHP_EOL;
echo 'event: ' . $event . PHP_EOL;
echo 'retry: 2000' . PHP_EOL;
echo 'data: ' . json_encode($data) . PHP_EOL;
echo PHP_EOL;
@ob_flush();
@flush();
}
我正在使用 SSE,因为我只需要单向通信(因此 websockets 可能有点矫枉过正)而且我真的不想使用轮询。如果我无法解决这个问题,我可能不得不这样做。
我认为您对网络的工作方式有疑问。 PHP 代码不会 运行 在您的浏览器中 - 它只是创建一些东西,网络服务器通过网络传递给浏览器。
从服务器加载页面后就是这样。您将需要实施一些轮询更改的东西。
我完成此操作的一种方法是将页面置于一个循环中,该循环会刷新并因此每隔一秒左右用新数据再次获取页面(但如果有很多人,这可能会使您的服务器严重超载在该页面上)。
唯一的其他解决方案是使用推送技术和一个 javascript 框架,它可以接受推送并重新填充页面的相关部分,或者在计时器上使用 javascript 循环来拉取数据.
When using the loop, handler.php is never loaded so the client doesn't get sent any data. In the Chrome developer network tab, handler.php is shown as "Pending" and then "Cancelled". The file itself stays locked for around 30 seconds.
这是因为网络服务器(Apache)或浏览器甚至PHP本身在30秒内没有响应时会取消请求。
所以我猜冲洗不起作用,尝试主动启动和结束缓冲区而不使用 @
函数,以便在出现错误时得到线索。
// Start output buffer
ob_start();
// Write content
echo '';
// Flush output buffer
ob_end_flush();
据我所知,SSE 连接的客户端看起来还不错 - 尽管我将 var textarea.....
移到了 onmessage
处理程序之外。
更新:我应该仔细观察,但要监视的事件是 event1
,因此我们需要为该事件设置一个事件侦听器。
<script>
if( typeof( EventSource ) !== "undefined" ) {
var url = 'handler.php'
var source = new EventSource( url );
var textarea = document.getElementById("subtitles");
source.addEventListener('event1', function(e){
textarea.value += e.data;
textarea.scrollTop = textarea.scrollHeight;
console.info(e.data);
},false );
} else {
document.getElementById("subtitles").value = "Server-sent events not supported.";
}
</script>
关于SSE服务器脚本我倾向于采用这样的方法
<?php
/* make sure the script does not timeout */
set_time_limit( 0 );
ini_set('auto_detect_line_endings', 1);
ini_set('max_execution_time', '0');
/* start fresh */
ob_end_clean();
/* ultility function for sending SSE messages */
function sse( $evtname='sse', $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";
}
}
$id = 0;
$event = 'event1';
$oldValue = null;
header('Content-Type: text/event-stream');
header('Cache-Control: no-cache');
header('X-Accel-Buffering: no');
while( true ){
try {
$data = @file_get_contents( 'liveData.txt' );
} catch( Exception $e ) {
$data = $e->getMessage();
}
if( $oldValue !== $data ) {
/* data has changed or first iteration */
$oldValue = $data;
/* send the sse message */
sse( $event, $data );
/* make sure all buffers are cleansed */
if( @ob_get_level() > 0 ) for( $i=0; $i < @ob_get_level(); $i++ ) @ob_flush();
@flush();
}
/*
sleep each iteration regardless of whether the data has changed or not....
*/
sleep(1);
}
if( @ob_get_level() > 0 ) {
for( $i=0; $i < @ob_get_level(); $i++ ) @ob_flush();
@ob_end_clean();
}
?>
(代表问题作者发布解决方案).
成功!第n次调试时,我决定返璞归真,重新开始。我取消了循环并将 PHP 代码减少到最低限度,但保留了 RamRaider 提供的客户端代码。现在一切都很好!通过调整重试值,我可以准确指定数据推送的频率。
PHP(服务器端):
<?php
$id = 0;
$event = 'event1';
$oldValue = null;
header('Content-Type: text/event-stream');
header('Cache-Control: no-cache');
header('X-Accel-Buffering: no');
try {
$data = file_get_contents('liveData.txt');
} catch(Exception $e) {
$data = $e->getMessage();
}
if ($oldValue !== $data) {
$oldValue = $data;
echo 'id: ' . $id++ . PHP_EOL;
echo 'event: ' . $event . PHP_EOL;
echo 'retry: 500' . PHP_EOL;
echo "data: {$data}\n\n";
echo PHP_EOL;
@ob_flush();
@flush();
}
?>
Javascript(客户端):
<script>
if ( typeof(EventSource ) !== "undefined") {
var url = 'handler.php'
var source = new EventSource( url );
var textarea = document.getElementById("subtitles");
source.addEventListener('event1', function(e){
textarea.value += e.data;
textarea.scrollTop = textarea.scrollHeight;
console.info(e.data);
}, false );
} else {
document.getElementById("subtitles").value = "Server-sent events not supported.";
}
</script>