【问题标题】:PHP socket server, check if client is alivePHP套接字服务器,检查客户端是否还活着
【发布时间】:2012-04-05 07:39:54
【问题描述】:

我有一个 php 服务器正在监听 1 个 c# 客户端。

连接建立后,它会一直保持活动状态,直到客户端发送命令“quit”杀死 PHP 服务器。

但是当 c# 客户端在没有“退出”命令的情况下断开连接时(即:单击 windows 窗体中的关闭 (x) 按钮),服务器只会继续侦听,并且无法接收来自该客户端的任何其他连接。

有没有办法从服务器端(PHP)检查与客户端的连接是否仍然存在?

我的php服务器代码基于http://php.net/manual/en/sockets.examples.php的example1

如果有人有兴趣重现错误/错误行为,请粘贴示例 1 中的代码:http://php.net/manual/en/sockets.examples.php,通过 telnet 从局域网中的远程客户端连接,拔下客户端线... php 服务器将永远挂起,没有新连接被接受。

【问题讨论】:

    标签: php sockets listener keep-alive


    【解决方案1】:

    在您的循环中,您需要检查socket_read() 的返回值。如果它返回 FALSE,则存在读取错误(可能是由远程主机关闭连接引起的)。您提供的链接中的示例代码涵盖了这种情况。

    如果您需要优雅地处理某些错误状态,您可以随时使用socket_last_error() 检查套接字错误代码——this note 描述了可能的代码。

    编辑:

    当使用 putty 进行远程登录时,如果我用 X 按钮关闭,PHP 中的连接会正确关闭,但如果我拔掉 putty 机器的以太网线,PHP 服务器就会挂起。

    杀死PuTTY时连接关闭的原因是PuTTY在退出时关闭了其打开的连接。这会导致socket_read() 返回错误代码(我相信ECONNRESET)。如果你拉网线,它就没有机会这样做了。

    根据您的网络配置方式,TCP 连接应该最终会失败。您可以尝试通过设置SO_RCVTIMEOsocket_set_option() 来控制超时,但这并不总是适用于所有平台(我在看着你,WinSock)

    或者,您可以使用socket_select() 滚动您自己的轮询循环,并设置合理的超时时间。如果超时后所有连接的套接字都没有要发送的数据,则终止服务器。

    【讨论】:

    • 更多更新。当使用 putty 进行远程登录时,如果我用 X 按钮关闭,PHP 中的连接会正确关闭,但如果我拔掉 putty 机器的以太网线,PHP 服务器就会挂起。
    • 当您单击腻子上的 X 按钮时,即正在运行连接的关闭或关闭事件,如果您过早断开连接,它永远不会收到该响应,因此它预计很快会发送更多数据。
    【解决方案2】:

    我通过检查 socket_select() $read 数组是否包含相关连接但 socket_read() 数据为空(两次)来做到这一点。

    似乎socket_select() 将断开连接的客户端添加到$read 数组中,而socket_read() 在尝试读取断开连接的客户端数据时给出了一个空字符串''

    【讨论】:

      【解决方案3】:

      以下代码将显示连接状态。

        $address='example.com';
          $port = '9065';
          if (isset($port) && ($socket=socket_create(AF_INET, SOCK_STREAM, SOL_TCP))
           && (socket_connect($socket, $address, $port)))  {
          $text="Connection successful on IP $address, port $port";
          socket_close($socket);
          }
          else  {
          $text='Unable to connect<pre>'.socket_strerror(socket_last_error()).'</pre>';
          }
          echo $text;
      

      【讨论】:

        【解决方案4】:

        正如 Michael Dodd 所说,使用 socket_select() 可以拥有所有已接收数据的套接字以及已关闭的套接字。您也可以让所有那些他们的缓冲区可以接受数据进行传输,和/或那些引发一些异常的人。确实有此信息,套接字数组的(单独)副本必须放置在函数的第二个和/第三个参数中。 (如果不需要,这个函数必须有 $null 作为一个等于 null 的变量,而不仅仅是 null)

        以下示例显示了如何读取数据并在套接字仍处于打开状态时对其进行测试。我正在将此代码用于由 socket_accept() 在侦听套接字上创建的套接字,并且它可以正常工作。不使用socket_recv,可以使用socket_read。

        //all sockets created have been inserted to an array $socketArray
        //get a copy of the sockets array
        $tempArray = $socketArray;
        //this command will remove from $tempArray all sockets that have no data and are alive, with a timeout of 0 sec, 100 msec
        socket_select($tempArray, $null, $null, 0, 100);
        if (count($tempArray)) {
            //if we have some sockets in the array
            foreach($tempArray as $socket) {
                //read some data
                $count = socket_recv($socket, $socketData, 1024, 0);
                if ($count) {
                    //your code to do what you want with $socketData
                } else {
                    //find socket position in initial socket array
                    $index = array_search($socket, $socketArray);
                    //if found, remove it from array and close the socket
                    if ($index !== false) {
                        array_splice($socketArray, $index, 1);
                        socket_close($socket);
                    }
                }
            }
        }
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2012-04-29
          • 1970-01-01
          • 1970-01-01
          • 2016-12-27
          • 2021-09-11
          • 2014-03-10
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多