Php 而服务器发送事件的循环导致页面冻结
我目前正在使用服务器发送的事件来接收消息的聊天。然而,我遇到了一个问题。服务器发送事件从未连接,并且由于页面未加载而处于挂起状态 例如:Php 而服务器发送事件的循环导致页面冻结,php,server-sent-events,Php,Server Sent Events,我目前正在使用服务器发送的事件来接收消息的聊天。然而,我遇到了一个问题。服务器发送事件从未连接,并且由于页面未加载而处于挂起状态 例如: <?php while(true) { echo "data: This is the message."; sleep(3); ob_flush(); flush(); } ?> 我预计每3秒钟就会输出一次“数据:这就是消息”。相反,页面只是不加载。但是,对于服
<?php
while(true) {
echo "data: This is the message.";
sleep(3);
ob_flush();
flush();
}
?>
我预计每3秒钟就会输出一次“数据:这就是消息”。相反,页面只是不加载。但是,对于服务器发送的事件,我需要这种行为。有办法解决这个问题吗
编辑:
完整代码:
<?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网站上也是如此。虽然这不是问题的直接答案,但请尝试使用此方法查找错误。。你没有得到错误,但这应该可以帮助你找到它们,也许 基本上,您希望有一个包含主脚本的简单PHP脚本,但此页面允许出现错误。。。下面的例子 index.php/Simple Error Includer
<?php
ini_set('display_errors',1);
ini_set('display_startup_errors',1);
error_reporting(-1);
require "other.php";
?>
在中的示例毫无疑问,到现在为止,您已经明白了这一点,但很可能您还没有明白,我在几个sse脚本中使用了下面这样的代码,它非常有效。下面的代码是通用的,没有sql或记录集处理功能,但其思想是合理的(!?)
我要冒一次险,说出显而易见的事实 您可以每3秒钟查询一次服务器,让客户端进行等待 这可以通过javascript轻松完成 例如,如果
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
}
函数doPoll()
{
$('#response').append($.get(“file.php?action=poll”);
设置超时(doPoll,3000);
}
doPoll();
它能像脚本超时一样简单吗
如果PHP脚本运行时间过长,最终会自动终止。当您不希望这种情况发生时,解决方案是不断重置超时
因此,您可能只需要简单的添加:
<?php
while(true) {
echo "data: This is the message.";
set_time_limit(30);
sleep(3);
ob_flush();
flush();
}
?>
当然,可能不是这样,但我的直觉是这就是问题所在
更新:我在评论中注意到你正在使用一些免费主机。如果他们在安全模式下运行PHP
,则您无法重置超时。我注意到这里有一件事是sleep()
函数与ob\u start()
结合使用,并且-在完整的代码示例中没有-ob\u start()任何地方,但是有flush()
和ob flush()
你到底在冲什么?
为什么不干脆ob\u end\u flush()
问题是sleep()
比echo()
,比sleep()
再比echo()
再等等。。打开输出缓冲时无效。当输出缓冲不在中间时,睡眠功能按预期工作。事实上,它可能会(而且会)产生意想不到的结果,而这些结果并不是我们想要看到的 似乎睡眠功能干扰了输出。在之后使用睡眠功能确实有效:
<?php
while(true) {
echo "data: This is the message.";
ob_flush();
flush();
sleep(3);
}
不要使用循环,试试下面给出的代码,它可以根据您的要求正常工作(我自己测试过)
echo“数据:这是消息。”代码>
$url1=“.php”代码>
标题(“刷新:5;URL=$url1”)代码>
它将每5秒调用一次(在您的情况下,将其设置为3而不是5)并回显输出
希望这能解决您的问题我建议使用if()语句而不是while。在您的情况下,您的条件始终为真,因此它处于无限循环中。下面的代码在这里运行良好,还使用Mayhem的str_repeat函数添加4k数据(这通常是php刷新tcp数据包的最小值)
你到底为什么要使用无限循环呢?这就是为什么页面从未加载,怎么可能呢?下面有一些简单的例子。正如你所看到的,在MDN网站上…他们使用无限循环。取出echo$data[“number”]设置内容类型标题之前,请执行代码>。如果在设置内容类型标题之前打印某些内容,则会得到默认的内容类型,可能是text/html。在这种情况下,它应该返回数据,因为它使用输出缓冲,但对于每个服务器来说,这有时可能有点棘手。。值得用一个更简单的页面测试输出缓冲代码,然后看看它在这种情况下是否仍然有效这是纯PHP无法做到的。更新需要重新加载页面或AJAX。@EvanCarslake这可以用纯PHP完成,这是输出缓冲的酷之处。。如果您有一个iframe加载带有输出缓冲的PHP,,,您可以使用PHP代码触发更新。。。虽然它并不总是一个干净的解决方案,但它确实有效,而且是一个非常有名的方法。我知道我在这里也会使用flush()来尝试将数据发送到浏览器,而不需要填充PHP和Apache缓冲区。这些缓冲区与您提到的代码空间输出缓冲区不同。其中是ob\u start()
。。在你的代码里?我问“你冲什么来着?”就是因为这个。从哪里开始输出缓冲?在使用其他缓冲区函数之前,需要在某个地方启动它。我所看到的“start”是session\u start()
,它与输出缓冲无关。。两个不同的世界。ob_flush()在这里不起任何作用,因为这里没有要刷新的输出缓冲区。编码马虎,但也可能寻址可能在其他地方打开输出缓冲区的库(不在提供的示例中)。关于自己编码,调用header()
函数后,set\u time\u limit()
也是一个坏主意。而且,ob\u flush()
和ob\u end\u flush()
不是一回事<代码>刷新()
比ob\u flush()<?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
}
<?php
while(true) {
echo "data: This is the message.";
set_time_limit(30);
sleep(3);
ob_flush();
flush();
}
?>
<?php
while(true) {
echo "data: This is the message.";
ob_flush();
flush();
sleep(3);
}
echo str_repeat(' ', 4096);
while(true)
{
echo "data: This is the message.";
flush();
sleep(3);
}