¿Cómo cierro una conexión antes de tiempo?

Resuelto Eric_WVGG asked hace 54 años • 20 respuestas

Estoy intentando realizar una llamada AJAX (a través de JQuery) que iniciará un proceso bastante largo. Me gustaría que el script simplemente envíe una respuesta indicando que el proceso ha comenzado, pero JQuery no devolverá la respuesta hasta que el script PHP termine de ejecutarse.

Probé esto con un encabezado "cerrado" (abajo) y también con almacenamiento en búfer de salida; ninguno parece funcionar. ¿Alguna suposición? ¿O es algo que necesito hacer en JQuery?

<?php

echo( "We'll email you as soon as this is done." );

header( "Connection: Close" );

// do some stuff that will take a while

mail( '[email protected]', "okay I'm done", 'Yup, all done.' );

?>
Eric_WVGG avatar Jan 01 '70 08:01 Eric_WVGG
Aceptado

La siguiente página del manual de PHP (incluidas las notas de usuario) sugiere múltiples instrucciones sobre cómo cerrar la conexión TCP al navegador sin finalizar el script PHP:

  • Manejo de conexiones Documentos

Supuestamente requiere algo más que enviar un encabezado cercano.


OP luego confirma: sí, esto funcionó: señalando la nota de usuario n.° 71172 (noviembre de 2006) copiada aquí:

Cerrar la conexión del navegador de los usuarios mientras se mantiene el script php en ejecución ha sido un problema desde [PHP] 4.1, cuando se register_shutdown_function()modificó el comportamiento para que no cerrara automáticamente la conexión de los usuarios.

sts en mail dot xubion dot hu Publicó la solución original:

<?php
header("Connection: close");
ob_start();
phpinfo();
$size = ob_get_length();
header("Content-Length: $size");
ob_end_flush();
flush();
sleep(13);
error_log("do something in the background");
?>

¡Lo cual funciona bien hasta que lo sustituyas phpinfo(), echo('text I want user to see');en cuyo caso los encabezados nunca se envían!

La solución es desactivar explícitamente el almacenamiento en búfer de salida y borrarlo antes de enviar la información del encabezado. Ejemplo:

<?php
ob_end_clean();
header("Connection: close");
ignore_user_abort(true); // just to be safe
ob_start();
echo('Text the user will see');
$size = ob_get_length();
header("Content-Length: $size");
ob_end_flush(); // Strange behaviour, will not work
flush(); // Unless both are called !
// Do processing here 
sleep(30);
echo('Text user will never see');
?>

Acabo de pasar 3 horas tratando de resolver esto, espero que ayude a alguien :)

Probado en:

  • Es decir, 7.5730.11
  • Mozilla Firefox 1.81

Más tarde, en julio de 2010, en una respuesta relacionada, Arctic Fire vinculó dos notas de usuario más que eran seguimiento de la anterior:

  • Nota de usuario sobre manejo de conexiones n.º 89177 (febrero de 2009)
  • Nota de usuario sobre manejo de conexiones n.º 93441 (septiembre de 2009)
Joeri Sebrechts avatar Sep 26 '2008 18:09 Joeri Sebrechts

Es necesario enviar estos 2 encabezados:

Connection: close
Content-Length: n (n = size of output in bytes )

Como necesita saber el tamaño de su salida, deberá almacenarla en un buffer y luego descargarla en el navegador:

// buffer all upcoming output
ob_start();
echo 'We\'ll email you as soon as this is done.';

// get the size of the output
$size = ob_get_length();

// send headers to tell the browser to close the connection
header('Content-Length: '.$size);
header('Connection: close');

// flush all output
ob_end_flush();
ob_flush();
flush();

// if you're using sessions, this prevents subsequent requests
// from hanging while the background process executes
if (session_id()) {session_write_close();}

/******** background process starts here ********/

Además, si su servidor web utiliza compresión gzip automática en la salida (es decir, Apache con mod_deflate), esto no funcionará porque el tamaño real de la salida cambia y la longitud del contenido ya no es precisa. Deshabilite la compresión gzip del script en particular.

Para obtener más detalles, visite http://www.zulius.com/how-to/close-browser-connection-continue-execution

Timbo White avatar Nov 20 '2009 21:11 Timbo White

Puedes usar Fast-CGI con PHP-FPM para usar la fastcgi_end_request()función . De esta manera, puede continuar realizando algún procesamiento mientras la respuesta ya se haya enviado al cliente.

  • ejemplo de cómo utilizar fastcgi_finish_request() (noviembre de 2010)

Puede encontrar esto en el manual de PHP aquí: FastCGI Process Manager (FPM) ; Pero esa función específicamente no está más documentada en el manual. Aquí el extracto de PHP-FPM: PHP FastCGI Process Manager Wiki :


fastcgi_finish_request()

Alcance: función php

Categoría: Optimización

Esta característica le permite acelerar la implementación de algunas consultas PHP. La aceleración es posible cuando hay acciones en el proceso de ejecución del script que no afectan la respuesta del servidor. Por ejemplo, guardar la sesión en Memcached puede ocurrir después de que la página se haya formado y pasado a un servidor web. fastcgi_finish_request()es una característica de php que detiene la salida de la respuesta. El servidor web inmediatamente comienza a transferir la respuesta "lenta y tristemente" al cliente, y php al mismo tiempo puede hacer muchas cosas útiles en el contexto de una consulta, como guardar la sesión, convertir el video descargado, manejar todo tipo de estadísticas, etcétera.

fastcgi_finish_request()puede invocar la ejecución de la función de apagado.


Nota: fastcgi_finish_request() tiene una peculiaridad por la que las llamadas a flush, printo echofinalizarán el script antes de tiempo.

Para evitar ese problema, puedes llamar ignore_user_abort(true)justo antes o después de la fastcgi_finish_requestllamada:

ignore_user_abort(true);
fastcgi_finish_request();
Jorrit Schippers avatar Apr 11 '2012 07:04 Jorrit Schippers