如何在PHP中禁用输出缓冲

时间:2012-01-16 15:43:16

标签: php http mjpeg

我写了一个简单的中继脚本,它连接到网络摄像头并从套接字读取,并使用print函数输出这些数据。数据是已设置边界的MJPG数据。我只输出读取的数据。

问题是PHP似乎在缓冲这些数据。当我将相机设置为1 FPS时,进纸将冻结7-8秒,然后快速显示8帧。如果我将分辨率设置为一个巨大的尺寸,相机会以大约每秒1帧的速度移动。我假设然后发生了一些缓冲(因为巨大的大小快速填充缓冲区,而低大小则没有),我无法弄清楚如何禁用此缓冲。有谁知道怎么做?

代码:

ignore_user_abort(false);

$boundary = "myboundary";

//Set this so PHP doesn't timeout during a long stream
set_time_limit(0);

$socketConn = @fsockopen ("192.168.1.6", 1989, $errno, $errstr, 2);
if (!$socketConn)
exit();
stream_set_timeout($socketConn, 10);
fputs ($socketConn, "GET /mjpeg HTTP/1.0\r\n\r\n");

//Setup Header Information
header("Cache-Control: no-cache");
header("Cache-Control: private");
header("Pragma: no-cache");
header("Content-type: multipart/x-mixed-replace; boundary=$boundary");

@ini_set('implicit_flush', 1);
for ($i = 0; $i < ob_get_level(); $i++)
ob_end_flush();
ob_implicit_flush(1);

stream_set_blocking($f2, false);

//Send data to client
while (connection_status() == CONNECTION_NORMAL)
{
    $chunk = fread($socketConn, 128);
    print $chunk;   
}

fclose($socketConn);

7 个答案:

答案 0 :(得分:48)

tl; dr version

做两件事:

  1. 禁用用户空间输出缓冲区,...

    • 在全球范围内,通过......

      • 关闭php.ini中的output_buffering
      • 使用

        关闭Apache配置中的output_buffering
        php_flag "output_buffering" Off
        
    • 或者只关注您关注的脚本......

      • 致电ob_end_flush()
      • 致电ob_end_clean()
  2. 此外,尽可能多地禁用服务器级输出缓冲区:

    • 在脚本开头调用ob_implicit_flush()
    • 在每个flush()语句或其他将输出添加到响应主体的语句之后调用echo
  3. 更长的版本

    令人困惑的是,有两层缓冲可能是相关的,PHP文档很难区分两者。

    输出缓冲区

    第一层通常被PHP文档称为“输出缓冲区”。此缓冲层仅影响HTTP响应的 body 的输出,而不影响标头。您可以使用ob_start()打开输出缓冲,然后使用ob_end_flush()ob_end_clean()将其关闭。您还可以使用php.ini中的output_buffering选项自动启动所有脚本的输出缓冲。

    production versions of php.ini的此选项的默认值为4096,这意味着输出缓冲区中的前4096字节输出将被缓冲,此时将刷新输出缓冲区并关闭输出缓冲区。 / p>

    您可以通过在php.ini文件中将output_buffering设置为Off(或使用

    来全局禁用此层缓冲
    php_flag "output_buffering" Off
    
    如果你正在使用Apache,那么在你的Apache配置中

    。或者,您可以通过在脚本开头调用ob_end_clean()ob_end_flush()来禁用单个脚本。

    写入缓冲区和Web服务器缓冲区

    超出输出缓冲区的是PHP手册所称的“写入缓冲区”,以及您的Web服务器所具有的任何缓冲系统。如果您通过mod_php使用PHP与Apache,并且未使用mod_gzip,则可以调用flush()来刷新这些内容;与其他后端一样,它可能也会起作用,尽管手册很谨慎地提供保证:

      

    描述

    void flush ( void )
    
         

    刷新PHP的写缓冲区以及PHP正在使用的任何后端(CGI,Web服务器等)。这会尝试将当前输出一直推送到浏览器,但需要注意几点。

         

    flush()可能无法覆盖Web服务器的缓冲方案,并且它对浏览器中的任何客户端缓冲没有影响。它也不会影响PHP的用户空间输出缓冲机制。这意味着如果您正在使用它们,则必须同时调用ob_flush() flush()来刷新ob输出缓冲区。

    还有一些方法可以让PHP在每次flush()任何事情时自动调用echo(或做任何其他回复输出到响应主体的内容)。

    首先是致电ob_implicit_flush()。请注意,此函数具有欺骗性的名称;给定ob_前缀,任何合理的人都会期望它会影响'输出缓冲区',ob_startob_flush等也是如此。但事实并非如此; ob_implicit_flush()flush()一样,会影响服务器级输出缓冲区,并且不会以任何方式与其他ob_函数控制的输出缓冲区进行交互。

    第二种是通过在php.ini中将implicit_flush标志设置为On来全局启用隐式刷新。这相当于在每个脚本的开头调用ob_implicit_flush()。请注意,手册建议不要这样做,隐晦地引用“严重的性能影响”,其中一些我在此tangentially related answer中探讨。

答案 1 :(得分:17)

您可以在每次读取操作后调用flush(),而不是禁用输出缓冲。这样可以避免混淆服务器配置并使脚本更具可移植性。

答案 2 :(得分:7)

输出缓冲可以分层,我有过早期代码创建多个级别的情况。这将清除所有这些。

while (ob_get_level()) ob_end_clean(); 
// or ob_end_flush() if you want the contents of the buffer.

答案 3 :(得分:2)

我们可以在.htaccess文件中提供以下代码,以便在PHP中禁用输出缓冲

php_flag "output_buffering" off

答案 4 :(得分:0)

我知道这个问题有点旧,但回到这个问题,你可以在脚本基础上关闭输出缓冲,如下所示:

if (ob_get_level())
   ob_end_clean();

这应该关闭跟随它的任何脚本的所有输出缓冲。

答案 5 :(得分:0)

我只需要打印换行符就可以得到输出!
我无法忍受这个线程中有多少个BS!

代替这个:

print("there are bs everywhere in the world");

输入:

print("there is no bs anymore! \n");
# or
print("no more bs \r\n");

只需放置一个新的行字符,它将在输出中打印。如此轻松自在。

答案 6 :(得分:0)

对于Windows IIS服务器,以上解决方案均无效,因为IIS管理自己的缓冲区,也应禁用该缓冲区。

只需使用PHP脚本将以下web.config添加到您的文件夹中即可禁用缓冲。在下面的示例中,使用了PHP v7.3.7,但是您可以将名称替换为另一个版本。

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
    <system.webServer>
        <handlers>
            <clear />
            <add name="php-7.3.7" path="*.php" verb="GET,HEAD,POST" modules="FastCgiModule" scriptProcessor="C:\Program Files\PHP\v7.3\php-cgi.exe" resourceType="Either" requireAccess="Script" responseBufferLimit="0" />
        </handlers>
    </system.webServer>
</configuration>