为什么我的 php 服务器上的 SSE 文件在浏览器关闭时继续工作,如果没有发送任何内容?

Why my php SSE file on server continues working when browser is closed if nothing is sent?

我对 SSE 有一种奇怪的行为。

我有 2 个文件,一个用于服务器端,一个用于浏览器客户端。

serverside.php是这样的:

<?php
  header('Content-Type: text/event-stream');
  header('Cache-Control: no-cache');
  ob_end_clean();

  while( true ) {
    set_time_limit(30);

    $time = date('d/m/Y H:i:s');

     echo 'data: ' . $time . "\n\n";
     flush();

    error_log('i am here!');
    sleep(1);
   }
?>

客户端文件是这样的:

<!DOCTYPE html>
<html>
<body>

<div id="result"></div>
<script>
    var source;

    if(typeof(EventSource) === "undefined") {
      alert('Problem with your browser.');
    }

    source = new EventSource("serverside.php");
    source.onopen = function (event) {
      console.log('streaming SSE opened');
    };

    source.onmessage = function(event) {
      console.log('Streaming Message Received');
      document.getElementById("result").innerHTML = event.data;
    };

    source.onerror = function (event) {
    console.log('ERROR EventSource');
  }
</script>

</body>
</html>

如果我测试这一切都是 运行 好的。我在浏览器中获得刷新时间,当我在浏览器中关闭选项卡时,serverside.php 不会发送更多 'i am here!' 消息进行记录。这是正确的行为。当浏览器选项卡关闭时,serverside.php 必须停止。

但是现在,如果我在 serverside.php 文件中删除或评论这些行:

// echo 'data: ' . $time . "\n\n";
// flush();

serverside.php 文件仍然是这样的:

<?php
  header('Content-Type: text/event-stream');
  header('Cache-Control: no-cache');
  ob_end_clean();

  while( true ) {
    set_time_limit(30);

    $time = date('d/m/Y H:i:s');

     //  echo 'data: ' . $time . "\n\n";
     //  flush();

    error_log('i am here!');
    sleep(1);
   }
?>

也就是完全没有刷新数据。

如果我现在加载 index.php,我将不会收到来自 serverside.php 的任何信息。 但在这种情况下,当我关闭浏览器选项卡时,serverside.php 继续工作并发送记录 'i am here' 消息。

此外,如果 serverside.php 发送数据,并且在一些消息发送后停止发送更多数据,行为是相同的。 'i am here' 消息在选项卡关闭后继续到达。

这是什么解释?

我完全卡住了。

(我假设 Apache 是服务器,而您使用的是 mod_php,但基本思想应该与任何 Web 服务器相同。)

如果您的 PHP 脚本从不执行 flush() Apache 缓冲区内容将仅在缓冲区已满时发送到客户端(您的浏览器)。这永远不会发生,因为您的脚本永远不会执行 echo.

Apache 意识到客户端已断开连接的方式是尝试在套接字上向其发送一些内容并收到错误。这永远不会发生,因为你从不发送任何东西! (是的,要意识到 TCP/IP 套接字不再工作,您必须尝试使用​​它并失败;没有您可以查看的简单状态。)

我知道的最佳解决方案是让服务器端脚本发送 keep-alive 消息。 IE。每(例如)40 秒显示一条空白消息或时间戳。如果您真正的数据流已经安静下来,但您每 40 秒发送一次 keep-alive,那么您注意到客户端已断开连接的平均时间为 20 秒。

(这绝对是个好主意,尤其是当客户端频繁连接和断开连接时,否则它们会有效地保持套接字和 Apache 进程处于打开状态。)