在HTML5服务器发送事件中设置时间间隔

ndh*_*ndh 7 javascript php html5 server-sent-events

我想从服务器到客户端定期发送更新.为此,我使用了服务器发送的事件.我正在粘贴以下代码:

客户端

获取服务器更新

<script>
if(typeof(EventSource)!="undefined")
{
   var source=new EventSource("demo_see.php");
   source.onmessage=function(event)
   {
      document.getElementById("result").innerHTML=event.data + "<br>";
   }
}
else
{
   document.getElementById("result").innerHTML="Sorry, your browser does not support    server-sent events...";
}
</script>
</body>
</html>
Run Code Online (Sandbox Code Playgroud)

服务器端

<?php
    header('Content-Type: text/event-stream');
    header('Cache-Control: no-cache');
    $x=rand(0,1000);
    echo "data:{$x}\n\n";
    flush();
?>
Run Code Online (Sandbox Code Playgroud)

代码工作正常但它会在每个代码中发送更新3 seconds.我想以毫秒为单位发送更新.我尝试sleep(1)之后flush(),但它仅由1秒进一步增加的时间间隔.有没有人有一个想法我怎么能做到这一点?

另外,我可以使用服务器发送的事件发送图像吗?

Dro*_*dOS 12

正如上面的评论中所讨论的那样,在带有a sleep或a 的无限循环中运行PHP脚本有usleep两个原因

  • 当该脚本仍在运行时,浏览器将看不到任何事件数据(可能是等待连接先关闭).我记得SSE的早期浏览器实现允许这样但不再是这种情况.
  • 即使它确实在浏览器端工作,你仍然会遇到PHP脚本运行时间过长的问题(直到PHP.ini time_out设置启动).如果这发生一次或两次就可以了.如果有X千个浏览器同时从您的服务器寻找相同的SSE,它将关闭您的服务器.

正确的做法是让您的PHP脚本响应事件流数据,然后像往常一样正常终止.retry如果要控制浏览器何时再次尝试,请提供一个值(以毫秒为单位).这是一些示例代码

function yourEventData(&$retry)
{
 //do your own stuff here and return your event data.
 //You might want to return a $retry value (milliseconds)
 //so the browser knows when to try again (not the default 3000 ms)
}

header('Content-Type: text/event-stream');
header('Cache-Control: no-cache');
header('Access-Control-Allow-Origin: *');//optional

$data = yourEventData($retry);

echo "data:{$str}\n\nretry:{$retry}\n\n";
Run Code Online (Sandbox Code Playgroud)

作为原始问题的答案,这有点晚,但为了完整性的利益:

以这种方式轮询服务器时获得的只是数据.之后你用它做什么完全取决于你.如果您希望将这些数据视为图像并更新网页中显示的图像,则只需执行此操作即可

document.getElementById("imageID").src = "data:image/png;base64," + Your event stream data;
Run Code Online (Sandbox Code Playgroud)

这些原则非常重要.我偶尔会忘记它retry必须以毫秒为单位并最终返回,例如,retry:5\n\n令我惊讶的是,它仍然有效.但是,我会毫不犹豫地使用SSE以100ms的间隔更新浏览器端图像.更典型的用法将是以下几行

  • 用户在服务器上请求作业.该工作要么在其他工作之后,要么可能需要相当长的时间来执行(例如创建PDF或Excel电子表格并将其发回)
  • 可以启动一个SSE,告诉浏览器ETA完成作业并retry设置一个值,以便浏览器知道何时再次查看结果,而不是让用户等待没有反馈 - 并且冒着超时的危险.
  • ETA用于向用户提供一些反馈
  • 在ETA结束时,浏览器将再次显示(浏览器会自动执行此操作,因此您无需执行任何操作)
  • 如果由于某种原因服务器没有完成作业,它应该指示它返回的事件流,例如,data{"code":-1}\n\n因此浏览器侧代码可以优雅地处理该情况.

还有其他使用场景 - 更新股票报价,新闻标题等.以100毫秒间隔更新图像感觉 - 纯粹是个人观点 - 就像滥用技术一样.


Sha*_*ard 6

这种行为的原因(消息每3秒)在这里解释:

每次连接关闭后大约3秒钟,浏览器会尝试重新连接到源

因此,每100毫秒获取一次消息的一种方法是改变重新连接时间:(在PHP中)

echo "retry: 100\n\n";
Run Code Online (Sandbox Code Playgroud)

这不是很优雅,更好的方法是在PHP中无限循环,每次迭代将睡眠100毫秒.有很好的例子在这里,只是改变了sleep(),以usleep()支持毫秒:

while (1) {
    $x=rand(0,1000);
    echo "data:{$x}\n\n";
    flush();
    usleep(100000); //1000000 = 1 seconds
}
Run Code Online (Sandbox Code Playgroud)


Dav*_*vor 5

我相信接受的答案可能会产生误导。尽管它正确地回答了问题(如何设置 1 秒间隔),但一般来说无限循环并不是一种糟糕的方法。

SSE 用于在实际存在更新时从服务器获取更新,这与 Ajax 轮询相反,Ajax 轮询在某些时间间隔内不断检查更新(即使没有更新)。这可以通过无限循环来完成,该循环使服务器端脚本始终运行,不断检查更新并仅在有更改时才回显它们。

以下说法不正确

当该脚本仍在运行时,浏览器将看不到任何事件数据。

您可以在服务器上运行脚本,但仍然将更新发送到浏览器,而不是像这样结束脚本执行:

while (true) {
  echo "data: test\n\n";
  flush();
  ob_flush();
  sleep(1);
}
Run Code Online (Sandbox Code Playgroud)

通过发送重试参数而不无限循环来执行此操作将结束脚本,然后再次启动脚本,结束它,再次启动...这类似于 Ajax 轮询检查更新,即使没有更新,这不是 SSE 的方式打算工作。当然,在某些情况下,这种方法是合适的,就像已接受的答案中列出的那样(例如等待服务器创建 PDF 并在完成时通知客户端)。

使用无限循环技术将使脚本始终在服务器上运行,因此您应该小心处理大量用户,因为每个用户都有一个脚本实例,这可能会导致服务器过载。另一方面,即使在一些简单的场景中,您突然在网站上获得大量用户(没有 SSE),或者如果您使用 Web Sockets 而不是 SSE,也会发生同样的问题。一切都有其自身的局限性。

另一件需要注意的事情是你在循环中放入的内容。例如,我不建议将数据库查询放入每秒运行的循环中,因为这样您还会使数据库面临过载的风险。我建议在这种情况下使用某种缓存(Redis 甚至简单的文本文件)。