【发布时间】:2016-04-18 14:02:29
【问题描述】:
我正在尝试从 PHP 服务器向 Nginx 服务器发出异步 HTTP POST 请求。我阅读了这篇关于这个问题的信息非常丰富的blog post,决定使用pfsockopen 函数的技术,并导入了博客作者编写的code。我包括以下关键部分:
private function createSocket() {
if ($this->socket_failed)
return false;
$protocol = $this->ssl() ? "ssl" : "tcp";
$host = $this->options["host"];
$port = $this->ssl() ? 443 : 80;
$timeout = $this->options["timeout"];
try {
# Open our socket to the API Server.
# Since we're try catch'ing prevent PHP logs.
$socket = @pfsockopen($protocol . "://" . $host, $port, $errno,
$errstr, $timeout);
# If we couldn't open the socket, handle the error.
if (false === $socket) {
...
return false;
}
return $socket;
} catch (Exception $e) {
...
return false;
}
}
private function makeRequest($socket, $req, $retry = true) {
$bytes_written = 0;
$bytes_total = strlen($req);
$closed = false;
# Write the request
while (!$closed && $bytes_written < $bytes_total) {
try {
# Since we're try catch'ing prevent PHP logs.
$written = @fwrite($socket, substr($req, $bytes_written));
} catch (Exception $e) {
...
$closed = true;
}
if (!isset($written) || !$written) {
$closed = true;
} else {
$bytes_written += $written;
}
}
# If the socket has been closed, attempt to retry a single time.
if ($closed) {
fclose($socket);
if ($retry) {
$socket = $this->createSocket();
if ($socket) return $this->makeRequest($socket, $req, false);
}
return false;
}
$success = true;
...
return $success;
}
它大多工作得很好,但我观察到以下问题。通常,在大约 30 秒不活动后,当我以这种方式发送请求时,Nginx 会以 TCP RST(重置)数据包进行响应,I read 表示远程端的套接字已关闭。我还可以看到 netstat -na | grep 8080 的连接状态,到那时,连接 PHP -> Nginx 的状态为 CLOSE_WAIT,而 Nginx -> PHP 的状态为 FIN_WAIT2。 This guide 确认这意味着远程服务器想要关闭连接。
我知道这个问题是由远程端的连接超时设置引起的,因为当我增加它时,netstat 显示的连接会在ESTABLISHED 状态下保持更长时间,如果 PHP 服务器决定重用这样的连接,它工作正常。但是,我不想将连接超时设置为非常高的值,因为我担心端口会用完。
有什么方法可以在 PHP 中检测到连接进入CLOSE_WAIT 状态?我尝试了以下方法,但都不起作用:
- 检查
pfsockopen返回的句柄是否为假。 - 检查由
pfsockopen编写的$errno变量。 - 尝试捕获来自
pfsockopen和fwrite的异常。 - 检查
fwrite($socket)是否返回 false。 - 检查
fflush($socket)是否返回 false。 - 检查
stream_get_meta_data($socket)['timed_out']。
【问题讨论】: