PHP在用户断开连接时中止无限循环

Posted

技术标签:

【中文标题】PHP在用户断开连接时中止无限循环【英文标题】:PHP abort infinite loop on user disconnect 【发布时间】:2014-03-25 13:46:06 【问题描述】:

我正在用 php 做一个小实验。我有一个名为“infinity.txt”的文件,在该文件中,我每 0.25 秒写入一个递增数字。

while(true)

    file_put_contents('infinity.txt', ++$i.PHP_EOL, FILE_APPEND);
    usleep(250 * 1000);


这很好用,但是当我关闭浏览器中的选项卡时,脚本会继续运行。浏览器请求中止不是Ctrl. + C,所以这并不奇怪,但我仍然想知道当用户断开连接时是否可以中止无限循环,或者是否有任何方法可以查看用户是否仍然连接.

顺便说一句,事先调用register_shutdown_function 是完全没有用的,即使链接函数内部有die()

更新:在我看来,使用connection_aborted() 函数可能会完成一些事情。

更新 2:我已将代码更改为如下所示,但是,无限循环并没有取消:

while(true)

    if(connection_aborted())

        file_put_contents('infinity.txt', 'CONNECTION ABORTED.', FILE_APPEND);
        die();

    

    file_put_contents('infinity.txt', ++$i.PHP_EOL, FILE_APPEND);
    usleep(250 * 1000);


更新 3:我现在在每次迭代中回显和刷新一些文本,但仍然无济于事:

while(true)

    echo '0';
    ob_flush();
    flush(); // necessary for proper checking

    if(connection_aborted())

        file_put_contents('infinity.txt', 'CONNECTION ABORTED.', FILE_APPEND);
        die();

    

    file_put_contents('infinity.txt', ++$i.PHP_EOL, FILE_APPEND);
    usleep(250 * 1000);


更新 4:上一个更新中显示的代码在重新启动 Apache 时出于某种奇怪的原因开始工作,而我没有更改 php.ini 中的任何设置。最终的帮助是在一开始就添加了ignore_user_abort(true);

【问题讨论】:

【参考方案1】:

这样做(我在 cmets 中留下了解释):

ignore_user_abort(1); //we'll handle it by ourselves
header('Transfer-Encoding:chunked'); //HTTP 1.1: do not capture immediately (bin)
flush();
ob_flush();

$i = 0;
set_time_limit(0);
while(1)

    echo "0"; //do this: sending data to dead TCP connection will fail
    flush();
    ob_flush();
    if(connection_status()!=0)
    

        file_put_contents('infinity.txt', 'CONNECTION ABORTED.', FILE_APPEND);
        echo "0\r\n\r\n"; //stream termination packet (double \r\n according to proto)
        flush();
        ob_flush();
        exit();

    

    file_put_contents('infinity.txt', ++$i.PHP_EOL, FILE_APPEND);
    usleep(250 * 1000);


【讨论】:

我刚刚重新启动了 Apache,在 Firefox 中检查了它,然后又回到了 Chrome。它开始使用我在第三次更新中使用的代码。我很困惑,但谢谢,最终,是ob_flush(); flush(); 成功了!很抱歉让您感到困惑。 不仅如此。不同之处在于您的第三个代码没有Transfer-Encoding 标头(因此您将收到0 因为flush() 调用)。您还需要正确发送终止数据包以结束流 不,脚本可以在没有 Transfer-Encoding 标头的情况下工作。该标题无关紧要。它正在添加您建议的ignore_user_abort(true);,这可以解决问题。谢谢! 它有效。但会向您发送数据(即浏览器会在收到数据后显示) 没错,但我从来没有打算让浏览器在发送的那一刻看到输出;)【参考方案2】:

在您链接的同一 connection_aborted() 页面上查看 this comment。

像 PHP 这样的 Web 服务器的默认过程是创建整个 html 页面,并在完成后将其发送到浏览器。在浏览器向服务器请求文件后,在文件发送之前没有进一步的通信。

但是,您可以使用 ob_flush() 和相关的输出控制函数来更改此行为。这会导致 PHP 生成一定数量的 HTML,然后将其发送到浏览器。处理时的这种通信允许您之前尝试的connection_aborted() 工作。

编辑添加: 真高兴你做到了!我还为寻找这个的其他人创建了一些工作测试代码:

<?php 

@apache_setenv('no-gzip', 1);
@ini_set('zlib.output_compression', 0);
@ini_set('implicit_flush', 1);
for ($i = 0; $i < ob_get_level(); $i++)  ob_end_flush(); 
ob_implicit_flush(1);
ignore_user_abort(true);
ob_start();

for ($i=1;$i<10;$i++) 
    sleep(2);
    echo "done pass ". $i . ".<br />";
    echo str_pad('',4096)."\n";          
    ob_flush();
    if(connection_aborted())
        file_put_contents('test.txt', "CONNECTION ABORTED on pass $i.".PHP_EOL, FILE_APPEND);
        exit(); 
    
    else 
        file_put_contents('test.txt', "successful pass    on pass $i.".PHP_EOL, FILE_APPEND);
    

【讨论】:

查看我的第三次更新。不幸的是,它仍然不起作用。 第一步是确保渐进式渲染正常工作:***.com/questions/3133209/… 在浏览器中看到输出后,测试 connection_aborted() 部分。我必须包含填充以使其像这里描述的那样工作:us1.php.net/flush#54841 实际上这可能是一个很好的测试代码。 我刚刚重新启动了 Apache,在 Firefox 中检查了它,然后又回到了 Chrome。它开始使用我在第三次更新中使用的代码。我很困惑,但谢谢,最终,是 ob_flush(); flush(); 成功了!

以上是关于PHP在用户断开连接时中止无限循环的主要内容,如果未能解决你的问题,请参考以下文章

如何在无限循环中每 3 分钟回显一次?

手机刷机mtk驱动无限重复连接断开

如何使用 Amphp 捕获 php websocket 断开的 TCP 连接异常?

我怎样才能给 Selenium Grid 中的会话无限的空闲时间,但清理断开连接的会话?

如何将用户名从php会话存储到nodejs中的socket.io库和页面刷新时套接字连接断开。为啥?

一旦客户端断开连接,服务器应用程序就会崩溃