php 如何提前关闭连接?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/138374/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How do I close a connection early?
提问by Eric_WVGG
I'm attempting to do an AJAX call (via JQuery) that will initiate a fairly long process. I'd like the script to simply send a response indicating that the process has started, but JQuery won't return the response until the PHP script is done running.
我正在尝试执行一个 AJAX 调用(通过 JQuery),它将启动一个相当长的过程。我希望脚本简单地发送一个响应,表明进程已经开始,但 JQuery 不会在 PHP 脚本运行完成之前返回响应。
I've tried this with a "close" header (below), and also with output buffering; neither seems to work. Any guesses? or is this something I need to do in JQuery?
我已经尝试过使用“关闭”标题(如下)以及输出缓冲;两者似乎都不起作用。有什么猜测吗?或者这是我需要在 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.' );
?>
采纳答案by Joeri Sebrechts
The following PHP manual page (incl. user-notes) suggests multiple instructions on how to close the TCP connection to the browser without ending the PHP script:
以下 PHP 手册页(包括用户注释)建议了有关如何在不结束 PHP 脚本的情况下关闭与浏览器的 TCP 连接的多个说明:
Supposedly it requires a bit more than sending a close header.
据说它需要的不仅仅是发送一个关闭标头。
OP then confirms: yup, this did the trick:pointing to user-note #71172 (Nov 2006)copied here:
OP 然后确认:是的,这成功了:指向此处复制的用户注释 #71172(2006 年 11 月):
Closing the users browser connection whilst keeping your php script running has been an issue since [PHP] 4.1, when the behaviour of
register_shutdown_function()was modified so that it would not automatically close the users connection.sts at mail dot xubion dot hu Posted the original solution:
<?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"); ?>Which works fine until you substitute
phpinfo()forecho('text I want user to see');in which case the headers are never sent!The solution is to explicitly turn off output buffering and clear the buffer prior to sending your header information. Example:
<?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'); ?>Just spent 3 hours trying to figure this one out, hope it helps someone :)
Tested in:
- IE 7.5730.11
- Mozilla Firefox 1.81
自 [PHP] 4.1 以来,关闭用户浏览器连接同时保持 php 脚本运行一直是一个问题,
register_shutdown_function()当时修改了 的行为,使其不会自动关闭用户连接。sts at mail dot xubion dot hu 贴出原解:
<?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"); ?>其中,直到你代替工作正常
phpinfo()进行echo('text I want user to see');在这种情况下,头永远不会发送!解决方案是在发送标头信息之前明确关闭输出缓冲并清除缓冲区。例子:
<?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'); ?>刚刚花了 3 个小时试图解决这个问题,希望它可以帮助某人:)
测试于:
- IE 7.5730.11
- 火狐 1.81
Later on in July 2010 in a related answerArctic Firethen linked two further user-notes that were-follow-ups to the one above:
后来在 2010 年 7 月的相关回答中,Arctic Fire将另外两个用户注释链接到了上面一个:
回答by Timbo White
It's necessary to send these 2 headers:
有必要发送这两个标头:
Connection: close
Content-Length: n (n = size of output in bytes )
Since you need know the size of your output, you'll need to buffer your output, then flush it to the browser:
由于您需要知道输出的大小,因此您需要缓冲输出,然后将其刷新到浏览器:
// 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 ********/
Also, if your web server is using automatic gzip compression on the output (ie. Apache with mod_deflate), this won't work because actual size of the output is changed, and the Content-Length is no longer accurate. Disable gzip compression the particular script.
此外,如果您的 Web 服务器在输出上使用自动 gzip 压缩(即 Apache 与 mod_deflate),这将不起作用,因为输出的实际大小已更改,并且内容长度不再准确。禁用特定脚本的 gzip 压缩。
For more details, visit http://www.zulius.com/how-to/close-browser-connection-continue-execution
更多详情,请访问http://www.zulius.com/how-to/close-browser-connection-continue-execution
回答by Jorrit Schippers
You can use Fast-CGI with PHP-FPM to use the fastcgi_end_request()function. In this way, you can continue to do some processing while the response has already been sent to the client.
您可以将 Fast-CGI 与 PHP-FPM 结合使用以使用该fastcgi_end_request()功能。这样,您可以在响应已经发送到客户端时继续进行一些处理。
You find this in the PHP manual here: FastCGI Process Manager (FPM); But that function specifically is not further documented in the manual. Here the excerpt from the PHP-FPM: PHP FastCGI Process Manager Wiki:
您可以在此处的 PHP 手册中找到:FastCGI Process Manager (FPM);但是该功能在手册中没有进一步记录。这是PHP-FPM的摘录:PHP FastCGI Process Manager Wiki:
fastcgi_finish_request()
fastcgi_finish_request()
Scope: php function
适用范围:php函数
Category: Optimization
类别:优化
This feature allows you to speed up implementation of some php queries. Acceleration is possible when there are actions in the process of script execution that do not affect server response. For example, saving the session in memcached can occur after the page has been formed and passed to a web server. fastcgi_finish_request()is a php feature, that stops the response output. Web server immediately starts to transfer response "slowly and sadly" to the client, and php at the same time can do a lot of useful things in the context of a query, such as saving the session, converting the downloaded video, handling all kinds of statistics, etc.
此功能允许您加快某些 php 查询的执行速度。当脚本执行过程中有不影响服务器响应的动作时,可以加速。例如,可以在页面形成并传递到 Web 服务器之后将会话保存在 memcached 中。fastcgi_finish_request()是一个 php 功能,可以停止响应输出。Web服务器立即开始“缓慢而悲伤地”向客户端传输响应,同时php可以在查询的上下文中做很多有用的事情,例如保存会话,转换下载的视频,处理各种统计等
fastcgi_finish_request()can invoke executing shutdown function.
fastcgi_finish_request()可以调用执行关机功能。
Note:fastcgi_finish_request()has a quirkwhere calls to flush, print, or echowill terminate the script early.
注意:fastcgi_finish_request()有一个怪癖,调用flush, print, orecho会提前终止脚本。
To avoid that issue, you can call ignore_user_abort(true)right before or after the fastcgi_finish_requestcall:
为了避免这个问题,你可以在通话ignore_user_abort(true)之前或之后fastcgi_finish_request打电话:
ignore_user_abort(true);
fastcgi_finish_request();
回答by diyism
Complete version:
完整版:
ignore_user_abort(true);//avoid apache to kill the php running
ob_start();//start buffer output
echo "show something to user";
session_write_close();//close session file on server side to avoid blocking other requests
header("Content-Encoding: none");//send header to avoid the browser side to take content as gzip format
header("Content-Length: ".ob_get_length());//send length header
header("Connection: close");//or redirect to some url: header('Location: http://www.google.com');
ob_end_flush();flush();//really send content, can't change the order:1.ob buffer to normal buffer, 2.normal buffer to output
//continue do something on server side
ob_start();
sleep(5);//the user won't wait for the 5 seconds
echo 'for diyism';//user can't see this
file_put_contents('/tmp/process.log', ob_get_contents());
ob_end_clean();
回答by Salman A
A better solution is to fork a background process. It is fairly straight forward on unix/linux:
更好的解决方案是 fork 一个后台进程。在 unix/linux 上相当简单:
<?php
echo "We'll email you as soon as this is done.";
system("php somestuff.php [email protected] >/dev/null &");
?>
You should look at this question for better examples:
您应该查看此问题以获得更好的示例:
回答by Andrew Winter
Here's a modification to Timbo's code that works with gzip compression.
这是对使用 gzip 压缩的 Timbo 代码的修改。
// buffer all upcoming output
if(!ob_start("ob_gzhandler")){
define('NO_GZ_BUFFER', true);
ob_start();
}
echo "We'll email you as soon as this is done.";
//Flush here before getting content length if ob_gzhandler was used.
if(!defined('NO_GZ_BUFFER')){
ob_end_flush();
}
// 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 ********/
回答by Liam
Assuming you have a Linux server and root access, try this. It is the simplest solution I have found.
假设你有一台 Linux 服务器和 root 访问权限,试试这个。这是我找到的最简单的解决方案。
Create a new directory for the following files and give it full permissions. (We can make it more secure later.)
为以下文件创建一个新目录并授予其完全权限。(我们可以稍后使它更安全。)
mkdir test
chmod -R 777 test
cd test
Put this in a file called bgping.
把它放在一个名为bgping.
echo starting bgping
ping -c 15 www.google.com > dump.txt &
echo ending bgping
Note the &. The ping command will run in the background while the current process moves on to the echo command.
It will ping www.google.com 15 times, which will take about 15 seconds.
请注意&. ping 命令将在后台运行,而当前进程继续执行 echo 命令。它将 ping www.google.com 15 次,大约需要 15 秒。
Make it executable.
使其可执行。
chmod 777 bgping
Put this in a file called bgtest.php.
把它放在一个名为bgtest.php.
<?php
echo "start bgtest.php\n";
exec('./bgping', $output, $result)."\n";
echo "output:".print_r($output,true)."\n";
echo "result:".print_r($result,true)."\n";
echo "end bgtest.php\n";
?>
When you request bgtest.php in your browser, you should get the following response quickly, without waiting about 15 seconds for the ping command to complete.
当您在浏览器中请求 bgtest.php 时,您应该很快得到以下响应,而无需等待 ping 命令完成大约 15 秒。
start bgtest.php
output:Array
(
[0] => starting bgping
[1] => ending bgping
)
result:0
end bgtest.php
The ping command should now be running on the server. Instead of the ping command, you could run a PHP script:
ping 命令现在应该在服务器上运行。您可以运行 PHP 脚本而不是 ping 命令:
php -n -f largejob.php > dump.txt &
Hope this helps!
希望这可以帮助!
回答by skibulk
I'm on a shared host and fastcgi_finish_requestis setup to exit scripts completely. I don't like the connection: closesolution either. Using it forces a separate connection for subsequent requests, costing additional server resources. I read the Transfer-Encoding: cunkedWikipedia Articleand learned that 0\r\n\r\nterminates a response. I haven't thoroughly tested this across browsers versions and devices, but it works on all 4 of my current browsers.
我在共享主机上并fastcgi_finish_request设置为完全退出脚本。我也不喜欢这个connection: close解决方案。使用它会强制为后续请求建立单独的连接,从而消耗额外的服务器资源。我阅读了Transfer-Encoding: cunked维基百科文章并了解到0\r\n\r\n终止响应。我还没有在浏览器版本和设备上对此进行彻底的测试,但它适用于我当前的所有 4 个浏览器。
// Disable automatic compression
// @ini_set('zlib.output_compression', 'Off');
// @ini_set('output_buffering', 'Off');
// @ini_set('output_handler', '');
// @apache_setenv('no-gzip', 1);
// Chunked Transfer-Encoding & Gzip Content-Encoding
function ob_chunked_gzhandler($buffer, $phase) {
if (!headers_sent()) header('Transfer-Encoding: chunked');
$buffer = ob_gzhandler($buffer, $phase);
return dechex(strlen($buffer))."\r\n$buffer\r\n";
}
ob_start('ob_chunked_gzhandler');
// First Chunk
echo "Hello World";
ob_flush();
// Second Chunk
echo ", Grand World";
ob_flush();
ob_end_clean();
// Terminating Chunk
echo "\x30\r\n\r\n";
ob_flush();
flush();
// Post Processing should not be displayed
for($i=0; $i<10; $i++) {
print("Post-Processing");
sleep(1);
}
回答by FluorescentGreen5
TL;DR Answer:
TL; DR 答案:
ignore_user_abort(true); //Safety measure so that the user doesn't stop the script too early.
$content = 'Hello World!'; //The content that will be sent to the browser.
header('Content-Length: ' . strlen($content)); //The browser will close the connection when the size of the content reaches "Content-Length", in this case, immediately.
ob_start(); //Content past this point...
echo $content;
//...will be sent to the browser (the output buffer gets flushed) when this code executes.
ob_end_flush();
ob_flush();
flush();
if(session_id())
{
session_write_close(); //Closes writing to the output buffer.
}
//Anything past this point will be ran without involving the browser.
Function Answer:
功能答案:
ignore_user_abort(true);
function sendAndAbort($content)
{
header('Content-Length: ' . strlen($content));
ob_start();
echo $content;
ob_end_flush();
ob_flush();
flush();
}
sendAndAbort('Hello World!');
//Anything past this point will be ran without involving the browser.
回答by paan
You could try to do multithreading.
你可以尝试做多线程。
you could whip up a script that makes a system call ( using shell_exec) that calls the php binary with the script to do your work as the parameter. But I don't think that is the most secure way. Maybe you can thighten stuff up by chrooting the php process and other stuff
您可以创建一个进行系统调用(使用shell_exec)的脚本,该脚本使用脚本调用 php 二进制文件,以将您的工作作为参数。但我认为这不是最安全的方式。也许你可以通过 chroot php 进程和其他东西来增加东西
Alternatively, there's a class at phpclasses that do that http://www.phpclasses.org/browse/package/3953.html. But I don't know the specifics of the implementation
或者,在 phpclasses 有一个类可以这样做http://www.phpclasses.org/browse/package/3953.html。但我不知道实现的细节

