【问题标题】:How to detect PHP pfsockopen being closed by remote server?如何检测 PHP pfsockopen 被远程服务器关闭?
【发布时间】: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_WAIT2This guide 确认这意味着远程服务器想要关闭连接。

我知道这个问题是由远程端的连接超时设置引起的,因为当我增加它时,netstat 显示的连接会在ESTABLISHED 状态下保持更长时间,如果 PHP 服务器决定重用这样的连接,它工作正常。但是,我不想将连接超时设置为非常高的值,因为我担心端口会用完。

有什么方法可以在 PHP 中检测到连接进入CLOSE_WAIT 状态?我尝试了以下方法,但都不起作用:

  • 检查pfsockopen返回的句柄是否为假。
  • 检查由pfsockopen编写的$errno变量。
  • 尝试捕获来自pfsockopenfwrite 的异常。
  • 检查 fwrite($socket) 是否返回 false。
  • 检查 fflush($socket) 是否返回 false。
  • 检查stream_get_meta_data($socket)['timed_out']

【问题讨论】:

    标签: php sockets nginx tcp


    【解决方案1】:

    这是一百万个问题,从我读到的唯一方法来检查是否正在执行fread($socket),如果返回值为 false,则存在错误,这可能是数百万个问题,但如果远程服务器已关闭连接,fread 将始终返回 false。抱歉,如果这让您失望了,但我认为没有其他办法。

    【讨论】:

    • 去一次,去两次,卖了! :) 谢谢。
    【解决方案2】:

    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...
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2021-04-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-09-15
      • 2010-09-09
      • 1970-01-01
      相关资源
      最近更新 更多