我正在尝试从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 - &gt; Nginx处于状态CLOSE_WAIT
,而Nginx - > PHP位于FIN_WAIT2
。 This guide确认这意味着远程服务器想要关闭连接。
我知道问题是由远程端的连接超时设置引起的,因为当我增加它时,netstat
显示的连接状态ESTABLISHED
保持更长时间,如果PHP服务器决定重用这种连接,它工作正常。但是,我不想将连接超时设置为非常高的值,因为我担心端口耗尽。
我有什么方法可以在PHP中检测到连接进入CLOSE_WAIT
状态?我尝试了以下操作,但没有一个有效:
pfsockopen
返回的句柄是否为false。$errno
撰写的pfsockopen
变量。pfsockopen
和fwrite
的例外情况。fwrite($socket)
是否返回false。fflush($socket)
是否返回false。stream_get_meta_data($socket)['timed_out']
。答案 0 :(得分:1)
这是一百万个问题,从我读过的唯一方法来检查是在做fread($socket)
如果返回的值是假的,就会出现错误,这可能是数以百万计的事情,但如果远程服务器已关闭连接,fread
将始终返回false。对不起,如果这令你失望,但我认为别无他法。
答案 1 :(得分:0)
AFAIK最好的方法是选择读取,如果可读,则执行fread()。
// $socket was opened via pfsockopen(...)
stream_set_blocking($socket,0);
while(1){
// the do..while(0) is a trick to break out
do{
$rs = array($socket); $ws = NULL; $es = NULL;
$ok = stream_select($rs,$ws,$es,0,100000);
if ( $ok === false ) break; // select err, close and reconn
if ( $ok <= 0 ) break 2; // select tout, conn is up/ok
$rply = fread($socket,1024); // make one/two fread()
if ( $rply === false ) break; // read err
if ( strlen($rply) == 0 ) break; // conn reset by peer
break 2; // conn is up/ok
}while(0);
// here i must reconn
fclose($socket);
$socket = pfsockopen(...);
if ( $socket ) stream_set_blocking($socket,0);
else return false;
break;
}//endwhile1
// here the conn is up/ok
// check if some in $rply to be handled...