我认为你不能依赖 connection_aborted() 函数:有一些用户报告(https://stackoverflow.com/a/23530884/3908097,Alternative to PHP Function connection_aborted()?)它不可靠。还要考虑发生网络超时的情况。
我建议另一个想法,我认为它更可靠。很简单:向客户端发送 XML 数据,然后等待一段时间
确认:客户端在获取数据时发送另一个带有确认的 HTTP 请求。
确认后,您可以提交交易。如果超时 - 回滚。
这个想法只需要对服务器的 php 和客户端的代码进行微小的修改。
修改了你的php代码(test1.php):
ob_end_clean();
header("Connection: close");
ignore_user_abort(true);
ob_start();
echo $response->asXML();
$size = ob_get_length();
header("Content-Length: $size");
// *** added lines ***
$serial = rand();
$memcache_obj = memcache_connect('localhost', 11211);
$memcache_obj->set("test1.$serial", 0);
header("X-test1-serial: $serial");
// ***
ob_end_flush();
flush();
// *** changed code ***
$ok = 0;
$tmout = 10*1000; // timeout in milliseconds
// wait until confirmation flag changes to 1 or timeout occurs
while (!($ok = intval($memcache_obj->get("test1.$serial"))))
{
if (connection_aborted()) break; // then no wait
if (($tmout = $tmout - 10) <= 0) break;
usleep(10000); // wait 10 milliseconds
}
$memcache_obj->delete("test1.$serial");
if ($ok) {
error_log('User informed');
// commit transaction
} else {
error_log('Timeout');
// rollback transaction
}
处理客户端确认的代码(user_ack.php):
<?php
header("Content-type: text/plain; charset=utf-8");
$serial = intval($_GET['serial']);
$memcache_obj = memcache_connect('localhost', 11211);
$memcache_obj->replace("test1.$serial", 1);
echo $serial;
最后是客户端的代码(test1.html)进行测试:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8"/>
<title>Test connection</title>
<script type="text/javascript">
function user_ack(serial) {
var httpReq = new XMLHttpRequest();
httpReq.open("GET", "user_ack.php?serial=" + encodeURIComponent(serial), true);
httpReq.onreadystatechange = function () {
if (httpReq.readyState == 4) {
if (httpReq.status == 200) {
// confirmation successful
document.getElementById("ack").textContent = httpReq.responseText;
}
}
}
httpReq.send("");
}
function get_xml() {
var httpReq = new XMLHttpRequest();
httpReq.open("POST", "test1.php", true);
httpReq.onreadystatechange = function () {
if (httpReq.readyState == 4) {
if (httpReq.status == 200) {
// send confirmation
var serial = httpReq.getResponseHeader("X-test1-serial");
user_ack(serial);
// ... process XML ...
document.getElementById("xml").textContent = httpReq.responseText;
}
}
}
httpReq.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
httpReq.send("id=abc"); // parameters to test1.php
}
</script>
</head>
<body>
<p>XML from server:</p>
<div id="xml"></div>
<p>Confirmation serial: <span id="ack"></span></p>
<p><input type="button" value="XML download test" onclick="get_xml();"/></p>
</body>
</html>
我使用 memcached 来存储 test1.php 和 user_ack.php 之间通信的确认标志值。这个值的名称对于每个连接必须是唯一的,所以我使用$serial = rand(); 来生成名称:"test1.$serial"。创建时确认标志的值为 0。确认后user_ack.php 将其值更改为 1。
因为user_ack.php 必须知道标志的值名称,所以客户端发送带有$serial 值的确认。 $serial 的值在自定义 HTTP 标头中从服务器获取:X-test1-serial 获取 XML 时。
为了简单起见,我使用内存缓存。 MySQL 表也可以用于此目的。