Как отключить буферизацию вывода в PHP

Я написал простое реле скрипт, который подключается к веб-камере и читает из сокета и выводит эти данные с помощью функции печати. Данные-это данные 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.0rnrn");

//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);

5 ответов


tl; dr version

сделать две вещи:

  1. отключите выходной буфер пользовательского пространства...

    • глобально, либо...

      • выключить output_buffering в вашем php.Ини, или
      • выключить output_buffering в конфигурации Apache с помощью

        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(). Вы также можете автоматически запускать все ваши скрипты с буферизацией вывода при использовании output_buffering опция в php.ini.

значение по умолчанию этой опции для производственные версии php.ini равно 4096, что означает, что первые 4096 байт вывода будут буферизованы в выходном буфере, и в этот момент он будет покраснел и буферизация вывода выключено.

вы можете отключить этот уровень буферизации глобально, установив output_buffering to Off в вашем php.ini-файл (или с помощью

php_flag "output_buffering" Off

в конфигурации Apache, если вы используете Apache). Кроме того, вы можете отключить его для одного скрипта, вызвав ob_end_clean() или ob_end_flush() в начале скрипта.

буфер записи и буфер веб-сервера

за пределами выходного буфера находится руководство PHP относится к "буферу записи", а также к любой системе буферизации вашего веб-сервера. Если вы используете PHP с Apache через mod_php, а не с помощью mod_gzip можно назвать flush() чтобы смыть их; с другими бэкэндами это тоже может сработать, хотя руководство уклончиво дает гарантии:

описание

void flush ( void )

сбрасывает буферы записи PHP и все, что использует бэкэнд PHP (CGI, веб-сервер и т. д.). Это попытки нажмите текущий выход полностью в браузер с несколькими предостережениями.

flush () возможно, не удастся переопределить схему буферизации вашего веб-сервера, и это не влияет на буферизацию на стороне клиента в браузере. Это также не влияет на механизм буферизации вывода пользовательского пространства PHP. Это означает, что вам придется назвать как помощью ob_flush() и flush () для очистки выходных буферов ob, если вы используете те.

есть также несколько способов, которыми вы можете сделать PHP автоматически вызывать flush() каждый раз, когда вы echo что-нибудь (или сделайте что-нибудь еще, что Эхо выводит в тело ответа).

первое-позвонить ob_implicit_flush(). Обратите внимание, что эта функция обманчиво названа; учитывая ее ob_ префикс, любой разумный человек ожидал бы, что это повлияет на "выходной буфер", как и ob_start, ob_flush etc. Однако это не так.; ob_implicit_flush(), как flush(), влияет на выходной буфер уровня сервера и никак не взаимодействует с выходным буфером, управляемым другим ob_ функции.

второй-глобально включить неявную промывку, установив implicit_flush флаг On в вашем php.ini. Это эквивалентно вызову 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();

Это должно выключить буферизацию вывода для любого сценария, который следует за ним.