如何在PHP中禁用输出缓冲

Ion*_*non 30 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);
Run Code Online (Sandbox Code Playgroud)

Mar*_*ery 52

tl;博士版

做两件事:

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

    • 在全球范围内,要么......

    • 或者只是你关心的剧本,或者......

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

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

更长的版本

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

输出缓冲区

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

php.ini生产版本的此选项的默认值为4096,这意味着输出缓冲区中的前4096字节输出将被缓冲,此时将刷新输出缓冲区并关闭输出缓冲区.

您可以通过在php.ini文件中设置(或使用.)output_buffering来全局禁用此层缓冲Off

php_flag "output_buffering" Off
Run Code Online (Sandbox Code Playgroud)

在您的Apache配置中,如果您使用的是Apache).或者,您可以通过调用脚本ob_end_clean()ob_end_flush()脚本开头为单个脚本禁用它.

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

超出输出缓冲区的是PHP手册所称的"写入缓冲区",以及您的Web服务器所具有的任何缓冲系统.如果您通过PHP使用PHP mod_php,并且没有使用mod_gzip,则可以调用flush()以刷新这些; 与其他后端一样,它也可能有用,虽然手册很谨慎地提供保证:

描述

void flush ( void )
Run Code Online (Sandbox Code Playgroud)

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

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

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

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

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


Dav*_*dom 17

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

  • 另外,有时也需要`ob_flush()`. (7认同)
  • 使套接字无阻塞可能没有意义,这可能会导致代码出现争用情况。应该在循环中的“ print”调用之后立即放置“ flush()”调用。如果仍然无法使用,请显示“ ini_get('output_buffering');”和“ ini_get('zlib.output_compression');”的输出。 (2认同)
  • 那是因为当您执行`ini_set('output_buffering',0);`时,脚本已经在运行,并且输出缓冲区已经启动-在运行时不可更改。可以将其设置为.htaccess文件吗?如果您允许的话,这应该可以解决。您可能仍然需要`flush()`。 (2认同)

Yeh*_*sef 9

输出缓冲可以是分层的,我有过早期代码已经制作多个级别的情况.这将清除它们.

while (ob_get_level()) ob_end_clean(); 
// or ob_end_flush() if you want the contents of the buffer.
Run Code Online (Sandbox Code Playgroud)

  • 是的,OB可以分层。我的问题解决了。谢谢。 (3认同)