PHP的套接字和流,客户端数据在握手后服务器端无法读取

问题描述:

我试图用php做简单套接字应用程序。好的,原则是处理这些房间内的n个聊天室和多个功能,这些功能涉及通过套接字进行客户端到客户端的通信。因此,这里是我到目前为止有:PHP的套接字和流,客户端数据在握手后服务器端无法读取

  1. 客户端连接,用头握手经过确定

  2. 套接字脚本存储客户阵列,处理新的连接和断开,跟踪哪些客户有哪些房间(n:n)等

  3. 客户端我使用自己的语法如何控制套接字服务器例如加入房间 - >“delimiteraction:joindelimiterid:2delimiter”,但问题在这里上升。握手后,我无法从套接字获取任何人类可读数据。我使用putty来监视套接字脚本,并且它显示了大多数白色条,数据应该也是数据似乎每次都不同(相同的客户端消息但不同的套接字数据),但长度大多数是相同的阻塞已经尝试阻塞问题)

我试图解码,编码,二进制转换,试图用两个流和插座,但似乎没有任何工作。

我是新来的套接字,但不是PHP。 这里的客户端代码:

var delimiter = "maybeencodesomeofit"; 
var connection = new WebSocket('ws://myip:myport'); 
connection.onopen = function(e){//define handshake 
    alert("connection established"); 

    var message = delimiter+"action:join"+delimiter+"userid:1"+"EOD_MARK"; 
    connection.send("somegood10lobbyid:1somegood10body:"+message+"\r"); 
} 

connection.onerror = function (error){ 
    alert("connection errored"); 
    console.log(error); 
} 

connection.onmessage = function(e){ 
    alert("connection message"); 
} 
connection.onclose = function(e){ 
    alert("closed connection"); 
} 

这里的服务器代码:

<?php 

set_time_limit(0); 

// create a streaming socket, of type TCP/IP 
$sock = stream_socket_server("tcp://myip:myport", $errno, $errstr); 
stream_set_blocking($sock,TRUE); 
// start listen for connections 
// create a list of all the clients that will be connected to us.. 
// add the listening socket to this list 
$clients = array($sock); 
$incomplete_data = array(); 
$end_of_data = "EOD_MARK"; 
$clients_lobbies = array(); 
$received_header = array(); 
$handshakes = array(); 
echo phpversion(); 
//define all the lobbies to direct messages to, 
$lobbies = array(); 
echo "Sockets initialized"; 
while (true) { 
    // create a copy, so $clients doesn't get modified by socket_select() 
    $read = $clients; 
    foreach($received_header as $key => $header){ 
     if($header['time']+1 < microtime(true)){ 
      $headers = []; 
      echo $header['data']; 
      $lines = preg_split("/\r\n/", $header['data']); 
      foreach($lines as $line) { 
       $line = chop($line); 
       if(preg_match('/\A(\S+): (.*)\z/', $line, $matches)){ 
        $headers[$matches[1]] = $matches[2]; 
       } 
      } 
      $secKey  = $headers['Sec-WebSocket-Key']; 
      $secAccept = base64_encode(pack('H*', sha1($secKey . '258EAFA5-E914-47DA-95CA-C5AB0DC85B11'))); 

      // create handshake header 
      $upgrade = "HTTP/1.1 101 Web Socket Protocol Handshake\r\n" . 
      "Upgrade: websocket\r\n" . 
      "Connection: Upgrade\r\n" . 
      "Sec-WebSocket-Accept: $secAccept\r\n\r\n"; 

      // send handshake packet 
      fwrite($clients[$key], $upgrade); 
      $handshakes[$key] = true; 
      $unset_key = $key; 
     } 
    } 
    if(isset($unset_key)){ 
     unset($received_header[$key]); 
     unset($unset_key); 
    } 

    //check incomplete data and delete the data after 5 seconds 
    $unset_keys = array(); 
    foreach($incomplete_data as $key => $array){ 
     if($array['last_update']+5 < microtime(true)){ 
      $unset_keys[$key] = true; 
     } 
    } 
    foreach($unset_keys as $key => $arr){ 
     unset($incomplete_data[$key]); 
    } 
    unset($unset_keys); 


    // get a list of all the clients that have data to be read from 
    // if there are no clients with data, go to next iteration 
    if (stream_select($read, $write = NULL, $except = NULL, 0) === FALSE) 
     continue; 

    // check if there is a client trying to connect 
    if (in_array($sock, $read)) { 
     // accept the client, and add him to the $clients array 
     $clients[] = $newsock = stream_socket_accept($sock); 
     $key = array_search($sock, $read); 
     // remove the listening socket from the clients-with-data array 

     unset($read[$key]); 
    } 

    // loop through all the clients that have data to read from 
    foreach ($read as $read_sock) { 
     // read until newline or 1024 bytes 
     $key = array_search($read_sock, $clients); 
     //echo "Data incoming"; 
     // socket_read while show errors when the client is disconnected, so silence the error messages 
     $data = fread($read_sock, 4096); 
     //get the headers for the first handshake 
     if(!isset($handshakes[$key])){ 
      //we need to handshake it first before we continue with normal operation 
      $received_header[$key]['data'] .= $data; 
      $received_header[$key]['time'] = microtime(true); 
      continue; 
     } 
     // check if the client is disconnected 
     if ($data === false) { 
      echo "Client disconnected"; 
      // remove client from $clients array 
      $key = array_search($read_sock, $clients); 
      fclose($read_sock); 
      unset($clients[$key]); 
      //remove client from lobbies 
      if(isset($clients_lobbies[$key])){ 
       foreach($clients_lobbies[$key] as $lobbyid => $boolean){ 
        unset($lobbies[$lobbyid]['clients'][$key]); 
       } 
      } 
      unset($received_header[$key]); 
      unset($handshakes[$key]); 
      //remove clients indexed lobbies 
      unset($clients_lobbies[$key]); 
      unset($incomplete_data[$key]); 
      // continue to the next client to read from, if any 
      continue; 
     } 

     // trim off the trailing/beginning white spaces 
     //$data = trim($data); 

     // check if there is any data after trimming off the spaces 
     if (!empty($data)) { 
      $parsing_data = array(); 

      echo $data; 
      //PARSE DATA HERE, Direct the messages into the lobbies according to headers, headers will tell if the user wants to join another lobby, what lobby the message is from etc, 
      if($key !== false && isset($handshakes[$key])){ 
       if(isset($incomplete_data[$key])){ 
        echo "incomplete data"; 
        $data = $incomplete_data[$key]['data'].$data; 
        $incomplete_data[$key]['data'] = $data; 
        $incomplete_data[$key]['last_update'] = microtime(true); 
       }else{ 
        $incomplete_data[$key]['data'] = $data; 
        $incomplete_data[$key]['last_update'] = microtime(true); 
       } 
       if(substr_compare($data,$end_of_data,-strlen($end_of_data) !== 0)){//check if data has arrived completely 
        $incomplete_data[$key]['data'] = $data; 
        $incomplete_data[$key]['last_update'] = microtime(true); 
        continue; 
       }else{ 
        echo "Data is here.".$data; 
        //whole data is here 
        unset($incomplete_data[$key]); 
        $delimiter = substr($data,0,10);//get first 10 characters as delimiter; 
        $parsing_data = explode($delimiter,$data); 
        if(count($parsing_data) === 1){//something went wrong and there's no headers 

        } 
        //we can start doing magic here 
        $headers = array(); 
        $body = ""; 
        foreach($parsing_data as $header){ //parse headers last one will be with key "body" and contains the message to be written to participant sockets 
         $element = explode(":",$header); 
         if(count($element) === 2 && $element[0] !== 'body'){ 
          $headers[strval($element[0])] = strval($element[1]); 
         }elseif($element[0] === 'body'){ 
          $body = strval($element[1]); 
         } 
        } 
        if(isset($headers['lobbyid'])){//there's defined lobbyid to send message to 
         //forward the message 
         if(!isset($lobbies[$headers['lobbyid']])){ 
          //create the lobby 
          $lobbies[$headers['lobbyid']] = array(); 
          //join the lobby 
          $lobbies[$headers['lobbyid']]['clients'][$key] = $read_sock; 
         } 
         $clients_lobbies[$key][$headers['lobbyid']] = true; 
         // lobby exists -> forward the message to all, including the sender 
         foreach($lobbies[$headers['lobbyid']]['clients'] as $client_keys => $receivers){ 
          if(isset($clients[$client_keys])){ 
           fwrite($clients[$client_keys], $body); 
          } 
         } 
        } 
       } 
      } 
     } 
    } // end of reading foreach 
} 

// close the listening socket 
socket_close($sock); 

>

我找到了解决办法,你需要揭露的客户端到服务器的数据,然后重新编码, unmask函数需要能够处理多个帧(nagle的算法)。下面的代码将相应揭露多个框架 代码如下:

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) { 
      $payload_len = 8; 
      $masks = substr($payload, 4, 4); 
      $data = substr($payload, 8); 
      $len = (ord($payload[2]) << 8) + ord($payload[3]); 
     }elseif($length == 127) { 
      $payload_len = 14; 
      $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{  
      $payload_len = 6; 
      $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; 

     $payload = substr($payload, $payload_len+$len, strlen($payload));  
    }while (($len < strlen($data)) and $countert < 10); 
     return $decMessages; 
} 

function encode($message){ 
    $length = strlen($message); 
    $bytesHeader = []; 
    $bytesHeader[0] = 129; // 0x1 text frame (FIN + opcode) 

    if ($length <= 125) { 
     $bytesHeader[1] = $length; 
    }else if($length >= 126 && $length <= 65535) { 
     $bytesHeader[1] = 126; 
     $bytesHeader[2] = ($length >> 8) & 255; 
     $bytesHeader[3] = ($length  ) & 255; 
    }else{ 
     $bytesHeader[1] = 127; 
     $bytesHeader[2] = ($length >> 56) & 255; 
     $bytesHeader[3] = ($length >> 48) & 255; 
     $bytesHeader[4] = ($length >> 40) & 255; 
     $bytesHeader[5] = ($length >> 32) & 255; 
     $bytesHeader[6] = ($length >> 24) & 255; 
     $bytesHeader[7] = ($length >> 16) & 255; 
     $bytesHeader[8] = ($length >> 8) & 255; 
     $bytesHeader[9] = ($length  ) & 255; 
    } 

    $str = implode(array_map("chr", $bytesHeader)) . $message; 
    return $str; 
}