PHP 根本没有检测到连接中止
我已阅读并深刻理解以下内容:http://www.php.net/manual/en/features.connection-handling.phphttp://www.php.net/manual/en/function.register-shutdown-function.php
I have read and deeply understood these: http://www.php.net/manual/en/features.connection-handling.php http://www.php.net/manual/en/function.register-shutdown-function.php
但是,我已经测试了 PHP 5.1.6 和 5.3,但事情并没有像那里描述的那样工作.我观察到的是:
However, I have tested both PHP 5.1.6 and 5.3 and things DON'T work as described there. What I observe is:
- connection_status() 始终返回 true,即使在客户端关闭连接之后也是如此.
- 在客户端关闭连接后脚本继续执行,即使 ignore_user_abort 为 0
- 使用 register_shutdown_function() 注册的函数在脚本结束之前不会运行.当客户端中止连接时,脚本不会被中断(因此函数不会被调用).
所以基本上 PHP 根本不会检测到客户端的断开连接.
So basically PHP just doesn't detect the client's disconnection AT ALL.
请注意,这并不是将 ignore_user_abort 设置为 1:如果是这种情况,则 connection_status() 将返回 1,即使脚本将继续运行并且直到结束才会调用关闭函数.事实并非如此.
Note that this is NOT as if ignore_user_abort was set to 1: if that was the case then connection_status() would return 1 even though the script would keep running and the shutdown function would not be called until the end. That is not the case.
ini_get("ignore_user_abort") 按预期返回 0.
ini_get("ignore_user_abort") returns 0, as expected.
这是 PHP 中的错误,还是由于某些 Apache 设置?
Is this a bug in PHP, or may this be due to some Apache setting?
如何让 PHP 按照上述文档中的说明工作?
How do I get PHP to work as described in the abovementioned documentation?
测试脚本:
<?php
function myShutdown() {
error_log("myShutdown ".connection_status()." ".ini_get("ignore_user_abort"));
}
register_shutdown_function(myShutdown);
echo "Hi!";
error_log(" *** test/test *** ");
for ($i=0; $i<10; $i++) {
sleep(1);
error_log(".");
echo ".";
}
?>
重现步骤:- 访问脚本的 url- 在 10 秒内终止客户端上的连接(例如,点击浏览器中的停止按钮)
Steps to reproduce: - visit the url of the script - abort the connection on the client before 10 seconds have elapsed (e.g. hit the stop button in the browser)
预期/期望的行为:日志应显示少于 10 个点,并以myShutdown 1 0"结尾(如果您实时观看日志,则 myShutDown 应在客户端断开连接时立即出现)
Expected/Desired behavior: The logs should show less than 10 dots, and at the end "myShutdown 1 0" (if you watch the log in real time, the myShutDown should appear immediately when the client disconnects)
观察到的/当前的行为:日志总是精确显示 10 个点,最后显示myShutdown 0 0"(如果您实时观看,无论客户端何时断开连接,它都会持续 10 秒).
Observed/current behavior: The logs show always exactly 10 dots, and at the end "myShutdown 0 0" (if you watch it in realtime, it goes on for 10 seconds no matter when the client disconnects).
推荐答案
首先,我也未能让它工作,使用基本的 ubuntu 12.04 LAMP 安装(php5.3).但是我有一些信息,希望对您有所帮助.任何意见或编辑表示赞赏!:)
First, I also failed to get it to work, using the basic ubuntu 12.04 LAMP installation (php5.3). But I've some information and hope that it is helpful. Any comments or edits appreciated! :)
我发现您的代码存在两个问题.第一个是语法错误.在调用 register_shutdown_function()
时,您缺少围绕 myShutdown
的单引号.将行更改为:
I see two problems with your code. The first is a syntax error. You are missing the single quotes around myShutdown
when calling register_shutdown_function()
. Change the line to:
register_shutdown_function('myShutdown');
我看到的第二个问题是缺少 flush()
在 echo
s 之后调用.文档说:
The second problem I see is the missing flush()
call after echo
s. The documentation says:
在尝试向客户端发送信息之前,PHP 不会检测到用户已中止连接.仅使用 echo 语句并不能保证发送信息,请参阅 flush().
PHP will not detect that the user has aborted the connection until an attempt is made to send information to the client. Simply using an echo statement does not guarantee that information is sent, see flush().
但即使是 flush()
在任何情况下都无济于事.来自 flush()
的文档:
But even flush()
will not help in any case. From the documentation of flush()
:
flush() 可能无法覆盖 Web 服务器的缓冲方案,并且它对浏览器中的任何客户端缓冲都没有影响.它也不会影响 PHP 的用户空间输出缓冲机制.这意味着如果您正在使用 ob_flush() 和 flush() 来刷新 ob 输出缓冲区,则必须调用它们.
flush() may not be able to override the buffering scheme of your web server and it has no effect on any client-side buffering in the browser. It also doesn't affect PHP's userspace output buffering mechanism. This means you will have to call both ob_flush() and flush() to flush the ob output buffers if you are using those.
一些服务器,尤其是在 Win32 上,仍然会缓冲脚本的输出,直到它在将结果传输到浏览器之前终止.
Several servers, especially on Win32, will still buffer the output from your script until it terminates before transmitting the results to the browser.
像 mod_gzip 这样的 Apache 服务器模块可能会自己做缓冲,这会导致 flush() 不会导致数据立即发送到客户端.
Server modules for Apache like mod_gzip may do buffering of their own that will cause flush() to not result in data being sent immediately to the client.
甚至浏览器也可以在显示之前缓冲它的输入.例如,Netscape 会缓冲文本,直到它接收到行尾或标记的开头,并且在看到最外层表格的标记之前不会呈现表格.
Even the browser may buffer its input before displaying it. Netscape, for example, buffers text until it receives an end-of-line or the beginning of a tag, and it won't render tables until the tag of the outermost table is seen.
某些版本的 Microsoft Internet Explorer 仅在收到 256 字节的输出后才开始显示页面,因此您可能需要在刷新之前发送额外的空格以使这些浏览器显示页面.
Some versions of Microsoft Internet Explorer will only start to display the page after they have received 256 bytes of output, so you may need to send extra whitespace before flushing to get those browsers to display the page.
在该页面的评论中,建议设置多个标头和 apache 配置:
In the comments of the that page there is an advice to set several headers and apache configs:
apache_setenv('no-gzip', 1);
ini_set('zlib.output_compression', 0);
ini_set('implicit_flush', 1);
但是,即使这对我也不起作用.我使用wiresharek对此进行了调查,虽然网络服务器在0.0037秒后发送内容('Hi'),但网络浏览器正在缓冲页面.
however, even this didn't work for me. I've investigated this using wiresharek, although the web server sends content ('Hi') after 0.0037 seconds, the web browser was buffering the page.
相关文章