PHP connection_aborted()无法正常工作

问题描述 投票:2回答:4

我有以下代码:

ignore_user_abort(true);
while(!connection_aborted()) {
    // do stuff
}

并且根据PHP文档,它应该一直运行到关闭连接为止,但是由于某种原因,它不会一直运行,而是一直运行直到脚本超时。我在线上看了看,建议添加一些

echo chr(0);
flush();

进入循环,但这似乎也不起作用。更糟糕的是,如果我将其保留为

while(true) {
    // do stuff
}

客户端断开连接后,PHP仍继续运行脚本。有谁知道如何使它工作?我在某处缺少php.ini设置吗?

[如果有关系,我正在运行PHP 5.3.5。预先感谢!

php timeout connection infinite-loop
4个回答
2
投票

尝试:

    ignore_user_abort(true);

    echo "Testing connection handling";

    while (1) {
            if (connection_status() != CONNECTION_NORMAL)
                    break;
            sleep(1);
            echo "test";
            flush();
    }

1
投票

尝试在ob_flush();之前使用flush();,并且某些浏览器在添加某些数据之前不会更新页面。

尝试做类似的事情

<? php
// preceding scripts

ignore_user_abort(true);

$i = 0;

while(!connection_aborted())
{ $i++;
  echo $i;

  echo str_pad('',4096); // yes i know this will increase the overhead but that can be reduced afterwords

  ob_flush();

  flush();

  usleep(30000); // see what happens when u run this on my WAMP this runs perfectly
}

// Ending scripts
?>

实际上,Chrome浏览器在此代码上存在问题;它不能很好地支持流。


0
投票

不幸的是,我有output_buffering = Off,无论如何,即使客户端关闭了浏览器,我的while循环仍会继续运行,并且connection_aborted()仍为0。还有其他提示吗?


0
投票

我参加这个聚会有点晚了,但是我遇到了这个问题,并且已经深究了。这里发生了很多事情-这里提到了其中的一些:PHP doesn't detect connection abort at all

要点:为了使connection_aborted()正常工作,PHP需要尝试将数据发送到客户端。

输出缓冲区

如前所述,PHP在尝试将数据实际发送到客户端之前,不会检测到连接已死。这并不像echo那样简单,因为echo将数据发送到可能存在的任何output buffers,并且PHP将不会尝试真正发送,除非这些缓冲区已满。我将不讨论输出缓冲的细节,但是值得一提的是,可以有multiple嵌套缓冲区。

无论如何,如果您想测试connection_abort(),必须首先结束所有缓冲区:

while (ob_get_level()){ ob_end_clean(); }

现在,无论何时您想测试连接是否中止,都必须尝试将数据发送到客户端:

echo "Something.";
flush();

// returns expected value...
// ... but only if ignore_user_abort is false!
connection_aborted(); 

忽略用户中止

这是一个非常重要的设置,它决定了在调用上述flush()且用户中止连接后(例如,在浏览器中单击STOP按钮),PHP将执行的操作。

如果true,脚本将轻松运行。 flush()基本上什么也不做。

如果false,作为默认设置,将以下列方式立即停止执行:

  • 如果PHP尚未关闭,它将开始关闭过程。

  • 如果PHP已经关闭,它将退出任何关闭状态功能,然后移至下一个。

析构函数

如果您想在用户中止连接时做一些事情,则需要做三件事:

  1. 检测到用户中止了连接。这意味着您必须定期向用户尝试flush,如上所述。清除所有输出缓冲区,回显,刷新。

    a。如果ignore_connection_aborted为真,则在每次刷新后需要手动测试connection_aborted()

    b。如果ignore_connection_aborted为假,则对flush的调用将导致关机过程开始。 然后您必须特别小心,不要在关闭函数中引起flush],否则PHP将立即停止执行该函数并继续执行下一个关闭函数。

  2. 全部放在一起

将所有内容放在一起,让我们举个例子来检测用户按下“ STOP”并执行操作。

class DestructTester {
    private $fileHandle;

    public function __construct($fileHandle){
        // fileHandle that we log to
        $this->fileHandle = $fileHandle;
        // call $this->onShutdown() when PHP is shutting down.
        register_shutdown_function(array($this, "onShutdown"));
    }

    public function onShutdown() {
        $isAborted = connection_aborted();
        fwrite($this->fileHandle, "PHP is shutting down. isAborted: $isAborted\n");

        // NOTE
        // If connection_aborted() AND ignore_user_abort = false, PHP will immediately terminate
        // this function when it encounters flush. This means your shutdown functions can end
        // prematurely if: connection is aborted, ignore_user_abort=false, and you try to flush().
        echo "Test.";
        flush();
        fwrite($this->fileHandle, "This was written after a flush.\n");
    }
    public function __destruct() {
        $isAborted = connection_aborted();
        fwrite($this->fileHandle, "DestructTester is getting destructed. isAborted: $isAborted\n");
    }
}

// Create a DestructTester
// It'll log to our file on PHP shutdown and __destruct().
$fileHandle = fopen("/path/to/destruct-tester-log.txt", "a+");
fwrite($fileHandle, "---BEGINNING TEST---\n");
$dt = new DestructTester($fileHandle);

// Set this value to see how the logs end up changing
// ignore_user_abort(true);

// Remove any buffers so that PHP attempts to send data on flush();
while (ob_get_level()){
    ob_get_contents();
    ob_end_clean();
}

// Let's loop for 10 seconds
//   If ignore_user_abort=true:
//      This will continue to run regardless.
//   If ignore_user_abort=false:
//      This will immediate terminate when the user disconnects and PHP tries to flush();
//      PHP will begin its shutdown process.
// In either case, connection_aborted() should subsequently return "true" after the user
// has disconnected (hit STOP button in browser), AND after PHP has attempted to flush().
$numSleeps = 0;
while ($numSleeps++ < 10) {
    $connAbortedStr = connection_aborted() ? "YES" : "NO";
    $str = "Slept $numSleeps times. Connection aborted: $connAbortedStr";
    echo "$str<br>";
    // If ignore_user_abort = false, script will terminate right here.
    // Shutdown functions will being.
    // Otherwise, script will continue for all 10 loops and then shutdown.
    flush();

    $connAbortedStr = connection_aborted() ? "YES" : "NO";
    fwrite($fileHandle, "flush()'d $numSleeps times. Connection aborted is now: $connAbortedStr\n");
    sleep(1);
}
echo "DONE SLEEPING!<br>";
die;

评论说明了一切。您可以摆弄ignore_user_abort并查看日志,以了解情况如何变化。

我希望这可以帮助任何在connection_abortregister_shutdown_function__destruct上遇到麻烦的人。

© www.soinside.com 2019 - 2024. All rights reserved.