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

时间:2017-11-18 10:35:48

标签: javascript php sockets websocket

我试图用php制作简单的套接字应用程序。好的,原则是处理这些房间内的n个聊天室和多个功能,这些功能涉及通过套接字进行客户端到客户端的通信。所以这就是我到目前为止所做的:

  1. 客户端连接,与标头的握手通过ok

  2. 套接字脚本将客户端存储在数组中,处理新连接和断开连接,跟踪哪个客户端具有哪些房间(n:n)等

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

  4. 我尝试过解码,编码,二进制转换,尝试使用流和套接字,但似乎没有任何效果。

    我是套接字的新手,但不是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);
    

    &GT;

1 个答案:

答案 0 :(得分:0)

我找到了解决方案,您需要取消屏蔽客户端到服务器数据然后重新编码,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;
}