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

  • 相关阅读:
    HDU5418.Victor and World(状压DP)
    POJ2686 Traveling by Stagecoach(状压DP)
    POJ3254Corn Fields(状压DP)
    HDU5407.CRB and Candies(数论)
    CodeForces 352D. Jeff and Furik
    CodeForces 352C. Jeff and Rounding(贪心)
    LightOj 1282 Leading and Trailing
    Ural 1057. Amount of Degrees(数位DP)
    HDU 2089 不要62 (数位DP)
    HDU5366 The mook jong (DP)
  • 原文地址:https://www.cnblogs.com/fps2tao/p/7863054.html
Copyright © 2011-2022 走看看