¿Cómo cierro una conexión antes de tiempo?
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.' );
?>
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)
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
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
, print
o echo
finalizará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_request
llamada:
ignore_user_abort(true);
fastcgi_finish_request();