如何在 PHP 中禁用输出缓冲

2022-08-30 12:35:57

我编写了一个简单的中继脚本,该脚本连接到网络摄像头并从插座读取,并使用打印功能输出此数据。数据是已设置边界的 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);

答案 1

tl;dr 版本

做两件事:

  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

加长版

令人困惑的是,有两层缓冲可能是相关的,PHP文档在区分两者方面做得很差。

输出缓冲器

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

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

您可以通过在 php.ini 文件中设置 (或使用output_bufferingOff

php_flag "output_buffering" Off

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

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

除了输出缓冲区之外,还有PHP手册所称的“写入缓冲区”,以及Web服务器具有的任何缓冲系统。如果您通过 将 PHP 与 Apache 一起使用,并且没有使用 ,则可以调用 flush() 来刷新这些;对于其他后端,它也可能工作,尽管手册在提供保证方面很谨慎:mod_phpmod_gzip

描述

void flush ( void )

刷新 PHP 的写入缓冲区和 PHP 正在使用的任何后端(CGI、Web 服务器等)。这试图将当前输出一直推送到浏览器,但有一些警告。

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

还有几种方法可以让PHP在每次发生任何事情时自动调用(或者执行任何其他回显输出到响应体的事情)。flush()echo

第一种是调用ob_implicit_flush()。。请注意,此函数的命名具有欺骗性;鉴于其前缀,任何理性的人都会期望它会影响“输出缓冲区”,就像,等等。然而,事实并非如此。,如 ,会影响服务器级输出缓冲区,并且不会以任何方式与其他函数控制的输出缓冲区进行交互。ob_ob_startob_flushob_implicit_flush()flush()ob_

第二种方法是通过在 php.ini 中设置 implicit_flush 标志来全局启用隐式刷新。这等效于在每个脚本的开头调用。请注意,手册建议不要这样做,并隐晦地引用了“严重的性能影响”,其中一些我在这个无关紧要的答案中进行了探讨。Onob_implicit_flush()


答案 2

无需禁用输出缓冲,只需在每次读取操作后调用 flush()。这样可以避免弄乱服务器配置,并使脚本更具可移植性。


推荐