PHP连接_中止()无法正常工作

PHP连接_中止()无法正常工作,php,timeout,connection,infinite-loop,Php,Timeout,Connection,Infinite Loop,我有以下代码: ignore_user_abort(true); while(!connection_aborted()) { // do stuff } 根据PHP文档,它应该一直运行到连接关闭,但由于某些原因,它不会一直运行,直到脚本超时。我在网上浏览了一下,有人建议添加 echo chr(0); flush(); 进入循环,但这似乎也没什么作用。更糟糕的是,如果我把它当作 while(true) { // do stuff } 在客户端断开连接后,PHP仍然继续运行脚

我有以下代码:

ignore_user_abort(true);
while(!connection_aborted()) {
    // do stuff
}
根据PHP文档,它应该一直运行到连接关闭,但由于某些原因,它不会一直运行,直到脚本超时。我在网上浏览了一下,有人建议添加

echo chr(0);
flush();
进入循环,但这似乎也没什么作用。更糟糕的是,如果我把它当作

while(true) {
    // do stuff
}
在客户端断开连接后,PHP仍然继续运行脚本。有人知道如何让它工作吗?是否有我遗漏的php.ini设置

如果重要的话,我正在运行PHP5.3.5。提前谢谢

试试看:

    ignore_user_abort(true);

    echo "Testing connection handling";

    while (1) {
            if (connection_status() != CONNECTION_NORMAL)
                    break;
            sleep(1);
            echo "test";
            flush();
    }
尝试使用
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
?>


实际上,Google Chrome在这段代码上有问题;它不能很好地支持流式传输。

不幸的是,我已经关闭了输出\u buffering=Off,而且无论如何,我的while循环继续运行,连接\u aborted()给出了0,即使客户端关闭了浏览器。
还有其他建议吗?

我参加这个聚会有点晚了,但我只是遇到了这个问题,并找到了问题的症结所在。这里发生了很多事情——这里提到了其中的一些:

其要点是:为了使
连接中止()
正常工作,PHP需要尝试向客户端发送数据。

输出缓冲区 如前所述,PHP在尝试向客户端实际发送数据之前不会检测到连接已断开。这不像执行
echo
那么简单,因为
echo
将数据发送到任何可能存在的
输出缓冲区,并且在这些缓冲区足够满之前,PHP不会尝试真正的发送。我不会详细介绍输出缓冲,但值得一提的是,可以有多个嵌套缓冲区

无论如何,如果要测试连接_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()
并且用户中止了连接(例如:点击浏览器中的停止按钮)时,PHP将执行什么操作

如果true,脚本将愉快地运行
flush()
基本上不起任何作用

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

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

  • 如果PHP已经关闭,它将退出任何关闭 它所在的函数,并继续下一个函数

析构函数 如果您想在用户中止连接时执行某些操作,则需要执行三项操作:

  • 检测用户是否中止了连接。这意味着您必须尝试定期对用户进行
    刷新
    ,如上所述。清除所有输出缓冲区、回显、刷新

    a。如果
    ignore\u connection\u aborted
    为true,则需要在每次刷新后手动测试
    connection\u aborted()

    b。如果
    ignore\u connection\u aborted
    为false,则调用
    flush
    将导致关机过程开始然后必须特别小心,不要导致关闭函数内的
    刷新
    ,否则PHP将立即停止该函数的执行,并转到下一个关闭函数

  • 把它们放在一起 把这一切放在一起,让我们做一个例子,检测用户点击“停止”,并做一些事情

    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;
    
    类析构函数测试仪{
    私人$fileHandle;
    公共函数构造($fileHandle){
    //我们登录到的文件句柄
    $this->fileHandle=$fileHandle;
    //PHP关闭时调用$this->onShutdown()。
    寄存器关闭函数(数组($this,“onShutdown”);
    }
    公共功能关闭(){
    $isAborted=连接被中止();
    fwrite($this->fileHandle,“PHP正在关闭。isborted:$isborted\n”);
    //注
    //如果连接_aborted()和ignore_user_abort=false,PHP将立即终止
    //当遇到flush时,此函数将被禁用。这意味着您的关机功能可以结束
    //如果:连接被中止,则忽略\u user\u abort=false,然后尝试刷新()。
    回声“测试”;
    冲洗();
    fwrite($this->fileHandle,“这是在刷新后写入的。\n”);
    }
    公共函数_udestruct(){
    $isAborted=连接被中止();
    fwrite($this->fileHandle,“DestructTester正在被破坏。IsOrted:$IsOrted\n”);
    }
    }
    //创建一个DestructTester
    //它将在PHP关机和_destruct()时登录到我们的文件。
    $fileHandle=fopen(“/path/to/destruct tester log.txt”,“a+”);
    fwrite($fileHandle,“--开始测试---\n”);
    $dt=新的析构函数测试仪($fileHandle);
    //设置此值以查看日志最终如何更改
    //忽略用户中止(true);
    //删除所有缓冲区,以便PHP尝试在flush()上发送数据;
    while(ob_get_level()){
    ob_get_contents();
    ob_end_clean();
    }
    //让我们循环10秒钟
    //如果忽略用户中止=真:
    //这将继续运行。
    //如果ignore\u user\u abort=false:
    //当用户断开连接并且PHP尝试刷新()时,这将立即终止;
    //PHP将开始其关闭过程。
    //在任何一种情况下,连接_aborted()都应该在用户返回后返回“true”
    //已断开连接(点击浏览器中的停止按钮),并且在PHP尝试刷新()之后。
    $numSleeps=0;
    而($numSleeps++<10){
    $connartedstr=连接被中止()?“是”:“否”;
    $str=“睡眠次数$numSleeps。连接中止:$connartedstr”;
    回声“$str
    ”; //如果忽略\u用户\u中止
    set_time_limit(0); // run the delay as long as the user stays connected
    ignore_user_abort(false);
    ob_end_clean();
    echo "\n";
    while ($delay-- > 0 && !connection_aborted())
    {
        echo str_repeat("\r", 1000) . "<!--sleep-->\n";
        flush();
        sleep(1);
    }
    ob_start();