donglu1973 2018-07-14 11:50 采纳率: 100%
浏览 341

PHP-Websocket:socket_recv()和socket_getpeername()的问题

I'm using PHP Websocket and sometimes, I get the following both warnings.
Warning-1:

socket_recv(): An existing connection was forcibly closed by the remote host

Warning-2:

socket_getpeername(): unable to retrieve peer name [107]: Transport endpoint is not connected

I send messages to the Websocket by another PHP-Script. This PHP-Script sends a message and if it's finised, the Socket-Call ends.
So this Warning is probably correct, because the "endpoint" (= the PHP-Script) is no loger connected.
But this Warning is not beautiful..

So this is the following Code for Websocket, which is received the messages:

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);

The command, which produces the Warning-1

socket_recv(): An existing connection was forcibly closed by the remote host

is:

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

In the PHP-Manual there is mentioned to get the return-value of socket_recv(). But in the examples there is only shown to execute socket_recv() once, by using if().
But I use socket_recv() within the loop-header.
So my question:
How do I check return-value in my case - by using socket_recv() within a loop-header?

Warning-2

socket_getpeername(): unable to retrieve peer name [107]: Transport endpoint is not connected

was produced with the following code:

socket_getpeername($newSocketArrayResource, $client_ip_address);

My question is:
What have I to do in order not to execute socket_getpeername(), if the client is no longer connected?

Maybe someone can help me to fix these Warnings. It's very difficult do watch, because these Warings do not always appear..

Bye, Chris

  • 写回答

1条回答 默认 最新

  • douhan8892 2018-07-14 14:04
    关注

    How do I check return-value in my case - by using socket_recv() within a loop-header?

    you can capture the return value of socket_recv and check it's value, but it's important that you do it in the correct order, and you can dictate in which order php does it, by using ()'s, eg:

    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
    

    (however, note, if you place the ()'s wrong, it will instead become the bool result of >=, for example, this is wrong: while($bytes_recieved=(socket_recv($newSocketArrayResource, $socketData, 1024, 0) >= 1)) , with this, $bytes_recieved would instead become the bool result of >=.)

    What have I to do in order not to execute socket_getpeername(), if the client is no longer connected?

    well, if you apply the above patch, i think you can check if $bytes_recieved is true-ish, eg

        if (!!$bytes_recieved) { 
            socket_getpeername($newSocketArrayResource, $client_ip_address);
            $newSocketIndex = array_search($newSocketArrayResource, $clientSocketArray);
            unset($clientSocketArray[$newSocketIndex]);         
        }
    
    • doing !! will convert it to bool(true-ish), and it's probably true-ish if the client is still connected, and probably false-ish otherwise. - although, this may get a false positive if the client didn't send anything any bytes a while, so it might be safer to check if it's directly false instead of false-ish, which you can check by using ===, eg

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

    ... btw, i don't know for sure, but it looks like your code here just randomly selects a client sharing $newSocketArrayResource 's ip address, instead of choosing $newSocketArrayResource specifically. if your server never receive more than 1 connection per ip address, i guess this is OK (still a shitty design choice, imo, but not directly bugged), but i think you should select clients by the resource id, rather than the ip address, then your server would be able to support more than 1 connection per ip address. i think you should set the key to the resource id, and instead do:

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

    ... and lastly, looks like you have a resource leak here, just unsetting it will leave you with memory leaks (and handle leaks), i think you want:

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

    that will leave you without memory/handle leaks.

    评论

报告相同问题?

悬赏问题

  • ¥50 汇编语言除法溢出问题
  • ¥65 C++实现删除N个数据列表共有的元素
  • ¥15 Visual Studio问题
  • ¥15 state显示变量是字符串形式,但是仍然红色,无法引用,并显示类型不匹配
  • ¥20 求一个html代码,有偿
  • ¥100 关于使用MATLAB中copularnd函数的问题
  • ¥20 在虚拟机的pycharm上
  • ¥15 jupyterthemes 设置完毕后没有效果
  • ¥15 matlab图像高斯低通滤波
  • ¥15 针对曲面部件的制孔路径规划,大家有什么思路吗