socket 的recv函数返回0应该怎样处理

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了socket 的recv函数返回0应该怎样处理相关的知识,希望对你有一定的参考价值。

参考技术A 楼主有个基本概念的错误. 对于TCP, 传输0字节数据的报文是正常的, 比如窗口探测报文, 纯ACK报文等. recv是用户user/TCP层面的调用, 而网络传输是TCP/lower network层面的行为. 网络传输了0字节数据的报文, 和recv到0字节的数据是两个完全无关的概念.

PHP Websocket 服务器 - 在 socket_recv 中处理更多帧

【中文标题】PHP Websocket 服务器 - 在 socket_recv 中处理更多帧【英文标题】:PHP Websocket server - Handle more frames in socket_recv 【发布时间】:2015-08-25 06:41:05 【问题描述】:

希望这个问题以前没有在这里解决,但我真的在努力寻找答案!

我在 PHP 服务器中通过 socket_recv 接收帧时遇到问题。当我一次从客户端发送超过 1 条消息时出现问题(例如在循环中)。通过流长度,它匹配帧数,但我无法正确取消屏蔽帧,并且我的取消屏蔽函数仅返回第一帧。我正在使用我在此处找到的 php 服务器的一部分,并且对于一条消息它工作正常,但是当我一次收到更多消息时,它并没有全部屏蔽。

我尝试添加循环以遍历所有帧,但我无法找到帧结尾并继续到其他帧。

这是服务器的主要部分:

while (true)      
//manage multipal connections     
$changed = $clients;
//returns the socket resources in $changed array
socket_select($changed, $null, $null, 0, 10);

//check for new socket
if (in_array($socket, $changed))                       
    $socket_new = socket_accept($socket); //accpet new socket
    $clients[] = $socket_new; //add socket to client array

    $header = socket_read($socket_new, 10240); //read data sent by the socket
    perform_handshaking($header, $socket_new, $host, $port); //perform websocket handshake

    socket_getpeername($socket_new, $ip); //get ip address of connected socket
    $response = mask(json_encode(array('type' => 'system', 'status' => true, "id" => "SRV_CONNECTED", 'message' => $ip . ' connected'))); //prepare json data
    send_message($response); //notify all users about new connection
    //make room for new socket
    $found_socket = array_search($socket, $changed);
    unset($changed[$found_socket]);


foreach ($changed as $changed_socket)                    
    while (@socket_recv($changed_socket, $buf, 1024, 0) >= 1)                               
        $received_text = unmask($buf); //unmask data                
        $tst_msg = json_decode($received_text, true); //json decode                
        $response_text = parse_msg($tst_msg, $changed_socket);                                    
        break 2; //exit this loop
        


    $buf = @socket_read($changed_socket, 10240, PHP_NORMAL_READ);           
    if ($buf === false)  // check disconnected client
        // remove client for $clients array
        $found_socket = array_search($changed_socket, $clients);
        socket_getpeername($changed_socket, $ip);
        unset($clients[$found_socket]);

        //notify all users about disconnected connection
        $response = mask(json_encode(array('type' => 'system', 'message' => $ip . ' disconnected')));
        send_message($response);
    

我需要通过 @socket_recv 中接收到的帧计数来调用 parse_msg,这应该由 unmask 函数返回。

这里是取消屏蔽功能:

function unmask($payload)  
$decMessages = Array();  
do  // This should be running until all frames are unmasked and added to $decMessages Array 
  $length = ord($payload[1]) & 127;

  if($length == 126) 
      $masks = substr($payload, 4, 4);
      $data = substr($payload, 8);
      $len = (ord($payload[2]) << 8) + ord($payload[3]);
  elseif($length == 127) 
      $masks = substr($payload, 10, 4);
      $data = substr($payload, 14);
      $len = (ord($payload[2]) << 56) + (ord($payload[3]) << 48) +
          (ord($payload[4]) << 40) + (ord($payload[5]) << 32) +
          (ord($payload[6]) << 24) +(ord($payload[7]) << 16) +
          (ord($payload[8]) << 8) + ord($payload[9]);
  else         
      $masks = substr($payload, 2, 4);
      $data = substr($payload, 6);
      $len = $length;
  

  $text = '';
  for ($i = 0; $i < $len; ++$i)     
      $text .= $data[$i] ^ $masks[$i%4];
  

  $decMessages[] = $text;

  // Here is problem. It doesn't put correct substr to $payload, so it could run again on stream shorted by last message     
  $payload = substr($payload, $length, strlen($payload));     
while (($len < strlen($data)) and $countert < 10);

return $decMessages;

我想我的问题在这里: $payload = substr($payload, $length, strlen($payload));我在哪里错误地放置了流的其余部分?

所以我的问题是: 为什么@socket_recv 返回不止一帧 如何更改我的取消屏蔽功能以通过所有帧,而不仅仅是第一个帧

对于任何帮助,我都会非常感激,因为我为此付出的努力超出了我的预期。

提前致谢 克拉拉!

【问题讨论】:

【参考方案1】:

所以,我找到了解决方案。正如我所想,问题是一次接收中有多个帧。我需要编辑我的取消屏蔽功能以检查流中是否仍有帧。

基本上,我所拥有的是正确的,但 $length in

$payload = substr($payload, $length, strlen($payload)); 

错了。我使用 $len + 8 代替 $length,14 od 6 取决于 $length。我不知道如何描述为什么会这样,但我相信有人会。就这样。现在它可以正常工作了!

谢谢!

【讨论】:

以上是关于socket 的recv函数返回0应该怎样处理的主要内容,如果未能解决你的问题,请参考以下文章

recv函数返回啥值?

windows socket编程阻塞模式下,recv函数返回0?

socket编程·send和recv

linux 下socket的recv函数返回值问题

linux socket阻塞recv怎么返回

linux recv函数返回值分析