Как отключить буферизацию вывода в PHP - PullRequest
28 голосов
/ 16 января 2012

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

Ответы [ 5 ]

48 голосов
/ 08 июня 2014

tl; dr version

Сделайте две вещи:

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

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

      • Отключение output_buffering в вашем php.ini, либо
      • Отключение 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 в 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 и т. д. Однако это не так;ob_implicit_flush(), как и flush(), влияет на выходной буфер уровня сервера и никак не взаимодействует с выходным буфером, управляемым другими функциями ob_.

Вторым является глобальное включение неявной очистки, установив флаг implicit_flush на On в вашем php.ini.Это эквивалентно вызову ob_implicit_flush() в начале каждого скрипта.Обратите внимание, что руководство не советует этого, загадочно цитируя «серьезные последствия для производительности» , некоторые из которых я исследую в этом тангенциально связанном ответе .

17 голосов
/ 16 января 2012

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

7 голосов
/ 26 октября 2016

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

while (ob_get_level()) ob_end_clean(); 
// or ob_end_flush() if you want the contents of the buffer.
2 голосов
/ 05 июня 2013

Мы можем дать приведенный ниже код в файле .htaccess для отключения буферизации вывода в PHP

php_flag "output_buffering" off
0 голосов
/ 30 апреля 2014

Я знаю, что этот вопрос немного устарел, но, возвращаясь к этому вопросу, вы можете отключить буферизацию вывода от сценария к сценарию, например так:

if (ob_get_level())
   ob_end_clean();

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

...