websocket 是什么?

摘抄网上的一些解释:

websocket 协议是基于 tcp 的一种新的网络协议。它实现了浏览器与服务器全双工(full-duplex)通信——允许服务器主动发送信息给客户端。

websocket 通信协议于2011年被 ietf 定为标准 rfc 6455,并被 rfc7936 所补充规范。

—— 百度百科

websocket 是一个持久化的协议,这是相对于 http 非持久化来说的。

举个简单的例子,http1.0 的生命周期是以 request 作为界定的,也就是一个 request,一个 response,对于 http 来说,本次 client 与 server 的会话到此结束;而在 http1.1 中,稍微有所改进,即添加了 keep-alive,也就是在一个 http 连接中可以进行多个 request 请求和多个 response 接受操作。然而在实时通信中,并没有多大的作用,http 只能由 client 发起请求,server 才能返回信息,即 server 不能主动向 client 推送信息,无法满足实时通信的要求。而 websocket 可以进行持久化连接,即 client 只需进行一次握手,成功后即可持续进行数据通信,值得关注的是 websocket 实现 client 与 server 之间全双工通信,即 server 端有数据更新时可以主动推送给 client 端。

上图是一个演示client和server之间建立websocket连接时握手部分

client 建立 websocket 时向服务器端请求的信息

get /chat http/1.1 
  host: server.example.com 
  upgrade: websocket //告诉服务器现在发送的是websocket协议
  connection: upgrade 
  sec-websocket-key: x3jjhmbdl1ezlkh9gbhxdw== //是一个base64 encode的值,这个是浏览器随机生成的,用于验证服务器端返回数据是否是websocket助理
  sec-websocket-protocol: chat, superchat 
  sec-websocket-version: 13 
  origin: http://example.com

服务器获取到 client 请求的信息后,根据 websocket 协议对数据进行处理并返回,其中要对 sec-websocket-key 进行加密等操作

http/1.1 101 switching protocols 
  upgrade: websocket //依然是固定的,告诉客户端即将升级的是websocket协议,而不是mozillasocket,lurnarsocket或者shitsocket
  connection: upgrade 
  sec-websocket-accept: hsmrc0smlyukagmm5oppg2hagwk= //这个则是经过服务器确认,并且加密过后的 sec-websocket-key,也就是client要求建立websocket验证的凭证
  sec-websocket-protocol: chat

php 服务端

<?php

if(($socket = socket_create(af_inet,sock_stream,sol_tcp)) < 0) {
    echo "socket_create() 失败的原因是:".socket_strerror($sock)."\n";
}
if(($ret = socket_bind($socket,'127.0.0.1','9090')) < 0) {
    echo "socket_bind() 失败的原因是:".socket_strerror($ret)."\n";
}
if(($ret = socket_listen($socket,3)) < 0) {
    echo "socket_listen() 失败的原因是:".socket_strerror($ret)."\n";
}

$all_sockets = [$socket];    // socket 集合

do {
    $copy_sockets = $all_sockets;   // 单独拷贝一份

    // 因为客户端是长连接,如果客户端非正常断开,服务端会在 socket_accept 阻塞,现在使用 select 非阻塞模式 socket
    if(socket_select($copy_sockets, $write, $except, 0) === false)
        exit('sosket_select error!');

    // 接收第一次 socket 连入,连入后移除服务端 socket
    if(in_array($socket, $copy_sockets)) {
        $client = socket_accept($socket);
        if($client) {
            $buf = socket_read($client, 1024);
            echo $buf;

            // 匹配 sec-websocket-key 标识
            if (preg_match("/sec-websocket-key: (.*)\r\n/i",$buf,$match)) {
                // 需要将 sec-websocket-key 值累加字符串,并依次进行 sha-1 加密和 base64 加密
                $key = base64_encode(sha1($match[1] . '258eafa5-e914-47da-95ca-c5ab0dc85b11',true));
                // 拼凑响应内容
                $res= "http/1.1 101 switching protocol".php_eol
                    ."upgrade: websocket".php_eol
                    ."connection: upgrade".php_eol
                    ."websocket-location: ws://127.0.0.1:9090".php_eol
                    ."sec-websocket-accept: " . $key .php_eol.php_eol;  // 注意这里,需要两个换行
                // 向客户端应答 sec-websocket-accept
                socket_write($client, $res, strlen($res));
                // 向客户端发送消息
                socket_write($client, buildmsg('socket ok'), 1024);

                // 加入客户端 socket
                $all_sockets[] = $client;
            }
            // 移除服务端 socket
            $key = array_search($socket, $copy_sockets);
            unset($copy_sockets[$key]);

            // socket_close($client);
        }
    }

    // 循环所有客户端 sockets
    foreach ($copy_sockets as $s) {
        // 获取客户端发给服务端的内容
        $buf = socket_read($s, 8024);
        echo strlen($buf).'---'.php_eol;
        // 代表客户端主动关闭
        if(strlen($buf) < 9) {
            $key = array_search($s, $all_sockets);
            unset($all_sockets[$key]);
            socket_close($s);
            continue;
        }
        // 输出
        echo getmsg($buf).php_eol;
    }

}while(true);
socket_close($socket);

// 编码服务端向客户端发送的内容
function buildmsg($msg) {
    $frame = [];
    $frame[0] = '81';
    $len = strlen($msg);
    if ($len < 126) {
        $frame[1] = $len < 16 ? '0' . dechex($len) : dechex($len);
    } else if ($len < 65025) {
        $s = dechex($len);
        $frame[1] = '7e' . str_repeat('0', 4 - strlen($s)) . $s;
    } else {
        $s = dechex($len);
        $frame[1] = '7f' . str_repeat('0', 16 - strlen($s)) . $s;
    }
    $data = '';
    $l = strlen($msg);
    for ($i = 0; $i < $l; $i++) {
        $data .= dechex(ord($msg{$i}));
    }
    $frame[2] = $data;
    $data = implode('', $frame);
    return pack("h*", $data);
}

// 解析客户端向服务端发送的内容
function getmsg($buffer) {
    $res = '';
    $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++) {
        $res .= $data[$index] ^ $masks[$index % 4];
    }
    return $res;
}

客户端

<!doctype html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <title>title</title>
    <script>
        // 创建一个socket实例
        var socket = new websocket('ws://localhost:9090');

        // 打开socket
        socket.onopen = function(event) {
            // 发送一个初始化消息
            socket.send("init msg");

        };
        socket.onmessage = function(event) {
            console.log('收到消息',event);

        };

        // 监听socket的关闭
        socket.onclose = function(event) {
            console.log('关闭监听',event);
        };

        function  send()
        {
            socket.send("client msg");
        }
    </script>
</head>
<body>
<button onclick="send()">发送消息</button>
</body>
</html>

运行测试:

client

server

到此这篇关于php实现websocket实例详解的文章就介绍到这了,更多相关php实现websocket内容请搜索www.887551.com以前的文章或继续浏览下面的相关文章希望大家以后多多支持www.887551.com!