[英]How to disable output buffering in PHP
我编写了一个简单的中继脚本,它连接到网络摄像头并从套接字读取,并使用打印功能输出这些数据。 数据是已设置边界的 MJPG 数据。 我只是输出读取的数据。
问题是 PHP 似乎正在缓冲这些数据。 当我将相机设置为 1 FPS 时,Feed 会冻结 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);
做两件事:
禁用用户空间输出缓冲区,或者...
在全球范围内,无论是...
output_buffering
,或使用以下命令关闭 Apache 配置中的output_buffering
php_flag "output_buffering" Off
或仅针对您关心的脚本,通过...
ob_end_flush()
,或ob_end_clean()
此外,通过以下任一方式,尽可能多地禁用服务器级输出缓冲区:
ob_implicit_flush()
,或echo
语句或其他将输出添加到响应正文的语句之后调用flush()
令人困惑的是,有两层缓冲可能是相关的,PHP 文档在区分这两者方面做得很差。
PHP 文档通常将第一层称为“输出缓冲区”。 这层缓冲只影响输出到 HTTP 响应的正文,而不影响标头。 您可以使用ob_start()
打开输出缓冲,并使用ob_end_flush()
或ob_end_clean()
将其关闭。 您还可以使用 php.ini 中的output_buffering
选项让所有脚本自动启动输出缓冲。
php.ini 的生产版本的这个选项的默认值是 4096,这意味着输出的前 4096 个字节将缓冲在输出缓冲区中,此时它将被刷新并关闭输出缓冲。
您可以通过在 php.ini 文件中将output_buffering
设置为Off
(或使用
php_flag "output_buffering" Off
在您的 Apache 配置中,如果您使用的是 Apache)。 或者,您可以通过在脚本开始时调用ob_end_clean()
或ob_end_flush()
来为单个脚本禁用它。
除了输出缓冲区之外,还有 PHP 手册中称为“写入缓冲区”的内容,以及您的 Web 服务器拥有的任何缓冲系统。 如果您通过mod_php
将 PHP 与 Apache 一起使用,并且没有使用mod_gzip
,则可以调用flush()
来刷新这些; 对于其他后端,它也可能有效,尽管手册对提供保证持谨慎态度:
描述
void flush ( void )
刷新 PHP 的写入缓冲区以及 PHP 正在使用的任何后端(CGI、Web 服务器等)。 这会尝试将当前输出一直推送到浏览器,但有一些警告。
flush()可能无法覆盖您的 Web 服务器的缓冲方案,并且它对浏览器中的任何客户端缓冲都没有影响。 它也不影响 PHP 的用户空间输出缓冲机制。 这意味着如果您正在使用它们,您将必须同时调用ob_flush()和flush()来刷新 ob 输出缓冲区。
还有几种方法可以让 PHP 在每次echo
任何内容时自动调用flush()
(或执行任何其他将输出回显到响应主体的操作)。
第一个是调用ob_implicit_flush()
。 请注意,此函数的名称具有欺骗性; 给定它的ob_
前缀,任何有理智的人都会期望它会影响“输出缓冲区”,就像ob_start
、 ob_flush
等ob_flush
。但是,情况并非如此; ob_implicit_flush()
与flush()
,影响服务器级输出缓冲区,并且不会以任何方式与其他ob_
函数控制的输出缓冲区ob_
。
第二个是通过在 php.ini 中将implicit_flush
标志设置为On
来全局启用隐式刷新。 这相当于在每个脚本开始时调用ob_implicit_flush()
。 请注意,手册建议不要这样做,隐晦地引用了“严重的性能影响” ,其中一些我在这个切线相关的答案中进行了探讨。
您可以在每次读取操作后调用flush()
,而不是禁用输出缓冲。 这避免了必须弄乱服务器配置并使您的脚本更具可移植性。
输出缓冲可以分层,我也遇到过早期代码已经创建多个级别的情况。 这将清除它们。
while (ob_get_level()) ob_end_clean();
// or ob_end_flush() if you want the contents of the buffer.
我们可以在 .htaccess 文件中给出以下代码来禁用 PHP 中的输出缓冲
php_flag "output_buffering" off
我知道这个问题有点老了,但是回到这个问题,您可以在脚本到脚本的基础上关闭输出缓冲,如下所示:
if (ob_get_level())
ob_end_clean();
这应该关闭它后面的任何脚本的所有输出缓冲。
对于 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>
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.