zoukankan      html  css  js  c++  java
  • 细说websocket

    下面我画了一个图演示 client 和 server 之间建立 websocket 连接时握手部分,这个部分在 node 中可以十分轻松的完成,因为 node 提供的 net 模块已经对 socket 套接字做了封装处理,开发者使用的时候只需要考虑数据的交互而不用处理连接的建立。而 php 没有,从 socket 的连接、建立、绑定、监听等,这些都需要我们自己去操作,所以有必要拿出来再说一说。(php有了 swoole就可以)

        +--------+    1.发送Sec-WebSocket-Key        +---------+
        |        | --------------------------------> |        |
        |        |    2.加密返回Sec-WebSocket-Accept  |        |
        | client | <-------------------------------- | server |
        |        |    3.本地校验                      |        |
        |        | --------------------------------> |        |
        +--------+                                   +--------+

    看了我写的上一篇文章的同学应该是对上图有了比较全面的理解。① 和 ② 实际上就是一个 HTTP 的请求和响应,只不过我们在处理的过程中我们拿到的是没有经过解析的字符串。如:

    GET /chat HTTP/1.1
    Host: server.example.com
    Origin: http://example.com

    我们往常看到的请求是这个样子,当这东西到了服务器端,我们可以通过一些代码库直接拿到这些信息。

    一、php 中处理 websocket

    WebSocket 连接是由客户端主动发起的,所以一切要从客户端出发。第一步是要解析拿到客户端发过来的 Sec-WebSocket-Key 字符串。

    GET /chat HTTP/1.1
    Host: server.example.com
    Upgrade: websocket
    Connection: Upgrade
    Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
    Origin: http://example.com
    Sec-WebSocket-Protocol: chat, superchat
    Sec-WebSocket-Version: 13

    前文中也提到了 client 请求的格式(如上),首先 php 建立一个 socket 连接,监听端口的信息。

    1. socket 连接的建立

    关于 socket 套接字的建立,相信很多大学修过计算机网络的人都知道了,下面是一张连接建立的过程:

    // 建立一个 socket 套接字
    $master = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
    socket_set_option($master, SOL_SOCKET, SO_REUSEADDR, 1);
    socket_bind($master, $address, $port);
    socket_listen($master);

    相比 node,这个地方的处理实在是太麻烦了,上面几行代码并未建立连接,只不过这些代码是建立一个 socket 套接字必须要写的东西。由于处理过程稍微有复杂,所以我把各种处理写进了一个类中,方便管理和调用。

    //demo.php
    Class WS {
        var $master;  // 连接 server 的 client
        var $sockets = array(); // 不同状态的 socket 管理
        var $handshake = false; // 判断是否握手
    
        function __construct($address, $port){
            // 建立一个 socket 套接字
            $this->master = socket_create(AF_INET, SOCK_STREAM, SOL_TCP)   
                or die("socket_create() failed");
            socket_set_option($this->master, SOL_SOCKET, SO_REUSEADDR, 1)  
                or die("socket_option() failed");
            socket_bind($this->master, $address, $port)                    
                or die("socket_bind() failed");
            socket_listen($this->master, 2)                               
                or die("socket_listen() failed");
    
            $this->sockets[] = $this->master;
    
            // debug
            echo("Master socket  : ".$this->master."
    ");
    
            while(true) {
                //自动选择来消息的 socket 如果是握手 自动选择主机
                $write = NULL;
                $except = NULL;
                socket_select($this->sockets, $write, $except, NULL);
    
                foreach ($this->sockets as $socket) {
                    //连接主机的 client 
                    if ($socket == $this->master){
                        $client = socket_accept($this->master);
                        if ($client < 0) {
                            // debug
                            echo "socket_accept() failed";
                            continue;
                        } else {
                            //connect($client);
                            array_push($this->sockets, $client);
                            echo "connect client
    ";
                        }
                    } else {
                        $bytes = @socket_recv($socket,$buffer,2048,0);
                        if($bytes == 0) return;
                        if (!$this->handshake) {
                            // 如果没有握手,先握手回应
                            //doHandShake($socket, $buffer);
                            echo "shakeHands
    ";
                        } else {
                            // 如果已经握手,直接接受数据,并处理
                            $buffer = decode($buffer);
                            //process($socket, $buffer); 
                            echo "send file
    ";
                        }
                    }
                }
            }
        }
    }

    上面这段代码是经过我调试了的,没太大的问题,如果想测试的话,可以在 cmd 命令行中键入 php /path/to/demo.php;当然,上面只是一个类,如果要测试的话,还得新建一个实例。

    $ws = new WS('localhost', 4000);

    客户端代码可以稍微简单点:

    var ws = new WebSocket("ws://localhost:4000");
    ws.onopen = function(){
        console.log("握手成功");
    };
    ws.onerror = function(){
        console.log("error");
    };

    运行服务器代码,当客户端连接的时候,我们可以看到:

    通过上面的代码可以清晰的看到整个交流的过程。首先是建立连接,node 中这一步已经封装到了 net 和 http 模块,然后判断是否握手,如果没有的话,就 shakeHands。这里的握手我直接就 echo 了一个单词,表示进行了这个东西,前文我们提到过握手算法,这里就直接写了。

    2. 提取 Sec-WebSocket-Key 信息

    转 https://www.cnblogs.com/hustskyking/p/websocket-with-php.html

  • 相关阅读:
    MySQL语法
    Linux常用命令大全
    触发器使用UTL_SMTP包发送邮件
    MySQL——触发器的创建和使用总结
    MySQL数据库备份
    Nginx配置文件(nginx.conf)配置详解
    JS弹出框,打开文件,保存文件,另存为。。。。
    java excel两种操作方式
    Zookeeper的优缺点
    activemq linux安装
  • 原文地址:https://www.cnblogs.com/fps2tao/p/7863054.html
Copyright © 2011-2022 走看看