PHP-Websocket:socket_recv()和socket_getpeername()

时间:2018-07-14 11:50:13

标签: php sockets websocket

我正在使用PHP Websocket,有时会同时收到以下两个警告。
警告-1:

  

socket_recv():远程主机强行关闭了现有连接

警告2:

  

socket_getpeername():无法检索对等名称[107]:传输端点未连接

我通过另一个PHP脚本将消息发送到Websocket。该PHP脚本发送一条消息,如果确定,则套接字调用结束。
因此,此警告可能是正确的,因为未连接“端点”(= PHP脚本)。
但是这个警告并不漂亮。

因此,这是下面的Websocket代码,它收到了以下消息:

define('HOST_NAME',$host_ip); 
define('PORT',$socket_port);

$null = NULL;

$socketHandler = new SocketHandler();

$socketResource = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
socket_set_option($socketResource, SOL_SOCKET, SO_REUSEADDR, 1);
socket_bind($socketResource, 0, PORT);
socket_listen($socketResource);

$clientSocketArray = array($socketResource);
while (true) {

    $newSocketArray = $clientSocketArray;
    socket_select($newSocketArray, $null, $null, 0, 10);

    if (in_array($socketResource, $newSocketArray)) {
        $newSocket = socket_accept($socketResource);
        $clientSocketArray[] = $newSocket;

        $header = socket_read($newSocket, 1024);
        $socketHandler->doHandshake($header, $newSocket, HOST_NAME, PORT);

        socket_getpeername($newSocket, $client_ip_address);

        $newSocketIndex = array_search($socketResource, $newSocketArray);
        unset($newSocketArray[$newSocketIndex]);
    }
    foreach ($newSocketArray as $newSocketArrayResource) {  
    while(socket_recv($newSocketArrayResource, $socketData, 1024, 0) >= 1){

            $socketMessage = $socketHandler->unseal($socketData);
            $messageObj = json_decode($socketMessage);

            break 2;
        }
        $socketData = @socket_read($newSocketArrayResource, 1024, PHP_NORMAL_READ);

        if ($socketData === false) { 
            socket_getpeername($newSocketArrayResource, $client_ip_address);
            $newSocketIndex = array_search($newSocketArrayResource, $clientSocketArray);
            unset($clientSocketArray[$newSocketIndex]);         
        }
    }
}
socket_close($socketResource);

产生警告1的命令

  

socket_recv():远程主机强行关闭了现有连接

是:

  

while(socket_recv($ newSocketArrayResource,$ socketData,1024,0)> = 1)

PHP-Manual中提到要获取socket_recv()的返回值。但是在示例中,仅显示通过使用if()执行一次socket_recv()。
但是我在循环头中使用了socket_recv()。
所以我的问题是:
我该如何检查返回值-通过在循环头中使用socket_recv()?

警告2

  

socket_getpeername():无法检索对等名称[107]:传输端点未连接

是使用以下代码生成的:

  

socket_getpeername($ newSocketArrayResource,$ client_ip_address);

我的问题是:
如果客户端不再连接,我该怎么办才能不执行socket_getpeername()?

也许有人可以帮助我修复这些警告。观看非常困难,因为这些警告并不总是出现。

再见, 克里斯

1 个答案:

答案 0 :(得分:0)

  

我该如何检查返回值-通过在循环头中使用socket_recv()?

您可以捕获socket_recv的返回值并检查它的值,但是按照正确的顺序进行操作很重要,并且可以使用()来指示php以哪个顺序进行操作,例如:

while(($bytes_recieved=socket_recv($newSocketArrayResource, $socketData, 1024, 0)) >= 1)
{
   // now you can check it inside the loop, it's $bytes_recieved
}
// you can also check it outside the loop, it's still in $bytes_recieved

(但是,请注意,如果您将()的位置错误,它将变成> =的布尔结果,例如,这是错误的:while($bytes_recieved=(socket_recv($newSocketArrayResource, $socketData, 1024, 0) >= 1)),与此相反,$ bytes_recieved会变成>=的布尔结果。)

  

如果客户端不再连接,我该怎么办才能不执行socket_getpeername()?

好吧,如果您应用上述补丁,我想您可以检查$ bytes_recieved是否为真,例如

    if (!!$bytes_recieved) { 
        socket_getpeername($newSocketArrayResource, $client_ip_address);
        $newSocketIndex = array_search($newSocketArrayResource, $clientSocketArray);
        unset($clientSocketArray[$newSocketIndex]);         
    }
  • 执行!!会将其转换为bool(true-ish),如果客户端仍处于连接状态,则可能为true-ish,否则可能为false-ish。 -尽管,如果客户端一段时间不发送任何字节,这可能会导致误报,因此检查它是否直接为假(而不是假冒)可能更安全,您可以使用===进行检查,例如

    if ($bytes_recieved === false) { 
        socket_getpeername($newSocketArrayResource, $client_ip_address);
        $newSocketIndex = array_search($newSocketArrayResource, $clientSocketArray);
        unset($clientSocketArray[$newSocketIndex]);         
    }
    

...顺便说一句,我不确定,但是看起来您的代码只是随机选择一个共享$ newSocketArrayResource ip地址的客户端,而不是专门选择$ newSocketArrayResource。如果您的服务器每个IP地址永远不会收到超过1个连接,我想这是可以的(仍然是一个糟糕的设计选择,imo,但不是直接出错),但是我认为您应该通过资源ID(而不是IP地址)选择客户端,则您的服务器将能够为每个IP地址支持多个1个连接。我认为您应该将密钥设置为资源ID,而是这样做:

    if ($bytes_recieved === false) { 
        unset($clientSocketArray[(int)$newSocketArrayResource]);
    }

...最后,看起来您这里有资源泄漏,只是取消设置它就会留下内存泄漏(和处理泄漏),我想您想要的是

    if ($bytes_recieved === false) {
        unset($clientSocketArray[(int)$newSocketArrayResource]);
        socket_close($clientSocketArray);
    }

这将使您没有内存/句柄泄漏。