• 企业400电话
  • 微网小程序
  • AI电话机器人
  • 电商代运营
  • 全 部 栏 目

    企业400电话 网络优化推广 AI电话机器人 呼叫中心 网站建设 商标✡知产 微网小程序 电商运营 彩铃•短信 增值拓展业务
    PHP实现WebSocket实例详解

    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内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

    您可能感兴趣的文章:
    • php+websocket 实现的聊天室功能详解
    • PHP 实现 WebSocket 协议原理与应用详解
    • PHP框架实现WebSocket在线聊天通讯系统
    • PHP用swoole+websocket和redis实现web一对一聊天
    • ThinkPHP5.0框架结合Swoole开发实现WebSocket在线聊天案例详解
    • PHP实现websocket通信的方法示例
    • php redis 处理websocket聊天记录的实例代码
    上一篇:PHP创建简单RPC服务案例详解
    下一篇:PHP之Swoole学习安装教程
  • 相关文章
  • 

    © 2016-2020 巨人网络通讯 版权所有

    《增值电信业务经营许可证》 苏ICP备15040257号-8

    PHP实现WebSocket实例详解 PHP,实现,WebSocket,实例,详解,