做一个php socket 向 websocket 传送数据的简单程序,想了解核心,在帧处理这里我搞不懂,麻烦解释一下

有三个疑问:

1.连接成功后,我想服务器发送消息,为什么只有第一次服务器会返回信息
2.第一次向服务器发送信息,服务器会返回信息,但是返回的信息是125个字符的,并不是一次返回全部,如图image.png
3.为什么发汉字多了会报错,服务器直接关闭,发几个字不会image.png
image.png

> 这是服务器代码,php的

<?php
set_time_limit(0);
$address = '127.0.0.1';
$port = '231';
if( ($sock = socket_create( AF_INET, SOCK_STREAM, SOL_TCP )) === false ) die( "套接字创建失败:" . socket_strerror(socket_last_error( )) );
if ( ($ok = socket_bind( $sock , $address , $port )) === false ) die( "服务器绑定失败:" . mb_convert_encoding(socket_strerror( socket_last_error( $sock )) , "utf-8","GBK") );
if ( socket_listen($sock, 4) === false )  die( "服务器监听失败:" . socket_strerror( socket_last_error( $sock ) ) );
 print_r('服务器开启成功');
while (true){
    if (($client = socket_accept($sock)) === false) die( "套接字接收客户端创建失败:" . socket_strerror(socket_last_error( $sock )) );
    $buf = socket_read($client, 8192);    //读取请求
    $response = hand_shake($buf);    //跟websocket握手
    socket_write($client,$response,strlen($response));//发送响应
    //正式开始通信...
    $msg = socket_read($client, 8192);
    $msg = frame(decode($msg));
    socket_write($client, $msg, strlen($msg));
    //$msg = code($msg);
    //if( socket_write($client,$msg,strlen($msg)) === false ) die("socket_write() failed: reason: " . socket_strerror(socket_last_error()) ."/n"); //发送数据
    //else echo'发送信息成功';
    //socket_close($client);  
}
//socket_close($sock);  

function hand_shake($buf){
    $buf  = substr($buf,strpos($buf,'Sec-WebSocket-Key:')+18);
    $key  = trim(substr($buf,0,strpos($buf,"\r\n")));
    $new_key = base64_encode(sha1($key."258EAFA5-E914-47DA-95CA-C5AB0DC85B11",true));
    $new_message = "HTTP/1.1 101 Switching Protocols\r\n";
    $new_message .= "Upgrade: websocket\r\n";
    $new_message .= "Sec-WebSocket-Version: 13\r\n";
    $new_message .= "Connection: Upgrade\r\n";
    $new_message .= "Sec-WebSocket-Accept: " . $new_key . "\r\n\r\n";
    return $new_message;
}

// 解析数据帧
function decode($buffer)  {
    $len = $masks = $data = $decoded = null;
    $len = ord($buffer[1]) & 127;

    if ($len === 126)  {
        $masks = substr($buffer, 4, 4);
        $data = substr($buffer, 8);
    } else if ($len === 127)  {
        $masks = substr($buffer, 10, 4);
        $data = substr($buffer, 14);
    } else  {
        $masks = substr($buffer, 2, 4);
        $data = substr($buffer, 6);
    }
    for ($index = 0; $index < strlen($data); $index++) {
        $decoded .= $data[$index] ^ $masks[$index % 4];
    }
    return $decoded;
}
// 返回帧信息处理
function frame($s) {
    $a = str_split($s, 125);
    if (count($a) == 1) {
        return "\x81" . chr(strlen($a[0])) . $a[0];
    }
    $ns = "";
    foreach ($a as $o) {
        $ns .= "\x81" . chr(strlen($o)) . $o;
    }
    return $ns;
}

// 返回数据
function send($client, $msg){
    $msg = $this->frame($msg);
    socket_write($client, $msg, strlen($msg));
}

?>
前端代码

<!DOCTYPE html>
<html>
<head lang="en">
    <meta charset="UTF-8">
</head>
<body>
<div style="width:410px;height:500px;margin:0 auto;">
<h2>超迷你简易网页聊天</h2>
<textarea  id="history" style="width:400px;height:300px;padidng:10px;font:16px/24px '宋体';display:block;margin-bottom:20px;" readonly="readonly"></textarea>
<select name="" style="width:60px;height:35px;"><option value="妈妈" selected>妈妈</option><option value="爸爸">爸爸</option></select><input  id="cont" type="text" style="width:260px;height:30px;" placeholder="输入内容">
<input  id="send" type="button" value="发送" style="width:70px;height:35px;">
</div>
<script>
function $(ele){
    return document.getElementById(ele);
}
var ws = new WebSocket("ws://127.0.0.1:231");
ws.onopen = function(){   log("连接服务器成功");        }
ws.onerror = function(){    log("连接服务器失败");        }
ws.onclose = function(){    log("服务器已关闭");        }
$('send').onclick = function(){
    ws.send( $('cont').value );
    ws.onmessage = function(e){
        log('新消息:'+ e.data);
      }
}
function log(html){    $('history').value += '->'+html+'\n' ;        }
</script>
</body>
阅读 236
评论
    1 个回答
    • 10.4k

    以下内容仅为查阅相关资料后得出的结论,如果错误请指出。

    仅作为学习参考,如需用于生产,请选择已有的一些解决方案。

    图片.png
    问题1:
    在完成握手后, socket_read 开始阻塞接受输入,输入完成后发给浏览器后,就又回到了上面的 接受客户端连接,所以 你就只能发送一条消息,如果要想一直发送,就需要把 正式开始通信那一部分放进 while true 里面,让他一直阻塞接受输入。就可以一直发送消息了。

    图片.png

    问题2:
    关于第二个问题问题,详见RFC 6455

    Payload length: 7 bits, 7+16 bits, or 7+64 bits
      The length of the "Payload data", in bytes: if 0-125, that is the
      payload length.  If 126, the following 2 bytes interpreted as a
      16-bit unsigned integer are the payload length.  If 127, the
      following 8 bytes interpreted as a 64-bit unsigned integer (the
      most significant bit MUST be 0) are the payload length.  Multibyte
      length quantities are expressed in network byte order.  Note that
      in all cases, the minimal number of bytes MUST be used to encode
      the length, for example, the length of a 124-byte-long string
      can't be encoded as the sequence 126, 0, 124.  The payload length
      is the length of the "Extension data" + the length of the
      "Application data".  The length of the "Extension data" may be
      zero, in which case the payload length is the length of the
      "Application data".
    
    

    问题3:
    发送中文在发送时是对的,但是浏览器在接受到响应后会发生 Could not decode a text frame as UTF-8. 错误。这是服务器在接收数据时被拆分成了几个帧(通常是发送的数据太长,在 socket_read 时被当成了几个帧接收(见2),此时是按照帧进行回复的,因为读取并不一定恰好是一个完整的汉字,就造成了 utf-8 编码错误。)

    关于 socket 相关例子请查阅

      撰写回答

      登录后参与交流、获取后续更新提醒

      相似问题
      推荐文章