【问题标题】:Having problems adding a timeout to socket_connect()向 socket_connect() 添加超时时遇到问题
【发布时间】:2014-05-18 02:53:00
【问题描述】:

如果之前有人问过这个问题,我很抱歉。我到处寻找,但找不到任何解决我的问题的方法。

我已经为我的一个项目编写了一个套接字客户端函数,我想给它添加一个超时,这样如果它失败就不会永远加载。

我已经尝试了很多文档和 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。我就是想不通。任何帮助将不胜感激。

谢谢!

【问题讨论】:

    标签: php sockets timeout client


    【解决方案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');
    

    您甚至可以在 while 循环中获取当前的“socket_last_error”命令。 http://www.php.net/manual/en/function.socket-last-error.php

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

    【讨论】:

    • 感谢您的回答,但我仍然得到与其他所有内容相同的 20 秒超时。不知道为什么会这样。这不仅仅是我本地的 WAMP 安装。在我的 VPS 上也是如此。
    • while(!socket_connect($socket, $host, $port)) 应该是while循环。 (错过了!)。如果它不在 IF 语句中,你应该“继续”它,只需继续; .
    • 您必须使用非阻塞套接字才能使其工作。而且,您最好检查错误代码(如建议的那样)并使用 socket_select 而不是忙循环,请参阅其他答案。
    • @SteffenUllrich 是的,好点。我添加了非阻塞功能。
    • 感谢你们的帮助!它现在可以在我的 VPS 上运行,但不能在我的本地 WAMP 安装上运行。
    【解决方案2】:

    我没有使用 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)
    • 如果连接返回 EINPROGRESS 或 EALREADY,请使用 select(在 PHP 中的 socket_select 中)等待套接字可写。在这里你可以给一个超时。
    • 如果选择因超时而结束,则连接在超时内失败
    • 如果选择成功(例如没有超时)使用socket_last_error检查套接字错误,如果连接成功应该没有错误
    • 再次设置套接字阻塞,至少在您的其余代码需要阻塞套接字的情况下

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

    【讨论】:

      猜你喜欢
      • 2019-10-12
      • 1970-01-01
      • 2011-06-10
      • 2017-09-09
      • 1970-01-01
      • 2013-05-17
      • 1970-01-01
      • 2019-04-28
      • 2019-06-29
      相关资源
      最近更新 更多