在socket_connect()中添加超时时遇到问题

时间:2014-04-05 19:29:38

标签: php sockets timeout client

如果之前有人问我,我很抱歉。我到处寻找,但无法找到解决问题的方法。

我已经为我的项目编写了一个套接字客户端函数,我想为它添加一个超时,以便在失败时不需要永久加载。

我已经尝试过StackOverflow上的文档和其他答案提出的一些建议,但到目前为止还没有任何工作。

这是我的功能;

    public function send($cmd, $host, $port){

        if(!($socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP)))
        {
            $errorcode = socket_last_error();
            $errormsg = socket_strerror($errorcode);

            die("Error (#300)");
        }

        if(!socket_connect($socket, $host, $port))
        {
            $errorcode = socket_last_error();
            $errormsg = socket_strerror($errorcode);

            die("Error (#400)");
        }

        if(!socket_write($socket, $cmd, strlen($cmd)))
        {
            $errorcode = socket_last_error();
            $errormsg = socket_strerror($errorcode);

            die("Error (#301)");
        }

        $reply = socket_read($socket, 4096)
                or die("Error (#302)");

        socket_close($socket);
        return $reply;
    }

我尝试了以下事项;

  • set_time_limit() - 不起作用。加载需要20秒,但显示“最长执行时间......”
  • ini_set("default_socket_timeout") - 仍需要20秒才能加载。
  • socket_set_nonblock() - 这样可行,但会导致socket_connect()失败。
  • https://stackoverflow.com/a/16939666/3457242 - 与上述相同。 socket_set_nonblock()使socket_connect()失败。
  • socket_set_option() - 需要20秒才能加载。似乎不起作用。

唯一可行的是socket_set_nonblock()函数,但它总是使socket_connect()返回false。我只是想不出来。任何帮助将不胜感激。

谢谢!

2 个答案:

答案 0 :(得分:1)

你可以做的是一个while循环。

例如:

socket_set_nonblock($socket)
    or die("not able to set socket to nonblock");
$timeout = 5;
$startTime = time();
while(!socket_connect($socket, $host, $port))
{
    if ((time() - $startTime ) >= $timeout)
    { 
        die('timeout');

你甚至可以获得当前的" socket_last_error" while循环中的命令。 http://www.php.net/manual/en/function.socket-last-error.php

然后,在时间检查IF语句之前,您可能会收到错误。如果错误是114或115,它应该执行时间检查错误,否则您可以使用错误类型立即终止它。例如,100:网络已关闭。

答案 1 :(得分:0)

我没有PHP经验,但我已经在C / Perl级别编写了足够的非阻塞代码。看起来PHP没有一种简单的方法(比如使用Perl)来做你想做的事情,所以你必须像C一样自己编写代码。来自connect(2)的linux手册页:

   EINPROGRESS
    The socket is nonblocking and the connection cannot be completed immediately.  
    It is possible to select(2) or poll(2) for completion by selecting the socket for 
    writing. After  select(2)  indicates  writability,  use getsockopt(2)  to read the 
    SO_ERROR option at level SOL_SOCKET to determine whether connect() completed 
    successfully (SO_ERROR is zero) or unsuccessfully (SO_ERROR is one of the usual 
    error codes listed here, explaining the reason for the failure).

翻译成PHP,这将是:

  • 设置套接字非阻塞
  • 尝试连接(例如PHP socket_connect)
  • 如果connect返回EINPROGRESS或EALREADY,则使用select(在PHP socket_select中)等待套接字可写。在这里你可以暂停。
  • 如果选择因超时而结束,则连接在超时内失败
  • 如果select成功(例如没有超时)用socket_last_error检查套接字错误,如果连接成功则应该没有错误
  • 再次设置套接字阻塞,至少如果其余代码需要阻塞套接字

有各种各样的例子可以工作但不使用socket_select。它们要么以固定的时间睡眠/睡眠(因此需要更多的时间来完成连接),要么进行繁忙的循环(并且只是烧掉CPU时间,这可能会影响其他应用程序)。