• php使用WebSocket详细教程之建立连接(一)


    本次教程需要理解的内容:

    1. 什么是WebSocket?
    2. WebSocket可以用来干什么?
    3. 什么是WebSocket握手?
    4. php使用WebSocket的流程?
    5. php中WebSocket相关函数的作用?

    (一)什么是WebSocket?
      WebSocket是一种在单个TCP连接上进行全双工通信的协议。WebSocket通信协议于2011年被IETF定为标准RFC 6455,并由RFC7936补充规范。WebSocket API也被W3C定为标准。WebSocket使得客户端和服务器之间的数据交换变得更加简单,允许服务端主动向客户端推送数据。在WebSocket API中,浏览器和服务器只需要完成一次握手,两者之间就直接可以创建持久性的连接,并进行双向数据传输。

    (二)WebSocket的作用?
    WebSock其实在平常使用,我们是时常见到的,用于实时通讯,例如我们常用的实时聊天、服务端向客户端消息推送、也可以实现踢用户下线功能。实时弹幕功能等等。

    (三)什么是握手?
    为了创建Websocket连接,需要通过浏览器发出请求,之后服务器进行回应,这个过程通常称为“握手”(handshaking)。

    这是比较正式的理解,在接下来使用方式中会在介绍到握手的实际含义。

    (四)php使用WebSocket的流程及相关函数的意义
    这里代码注释都会进行逐一解释,所以就直接上代码,有什么不懂欢迎提出来。

    <?php
        //设置应该报告何种 PHP 错误
        error_reporting(E_ALL^E_NOTICE);
        //设置脚本最大执行时间,0则为不限制
        set_time_limit(0);
        //打开或关闭绝对(隐式)刷送
        ob_implicit_flush();
        //设置创建socket服务器的ip
        $address="127.0.0.1";
        //设置socket监听的端口
        $port=10000;
        //socket的resource,即前期初始化socket时返回的socket资源
        $master;
        //用来存储连接进来的用户信息的数组
        $users;
        //socket的连接池,即client连接进来的socket标志,一个数组
        $sockets;
        /**
         * 以下socket_?()方法都为创建一个socket必须的,且顺序不能乱,缺一不可
         */
        //创建一个socket
        $master=socket_create(AF_INET,SOCK_STREAM,SOL_TCP);
        //设置socket选项,1表示接受所有的数据包
        socket_set_option($master,SOL_SOCKET,SO_REUSEADDR,1);
        //绑定socket到指定ip与端口
        socket_bind($master,$address,$port);
        //监听已连接的socket
        socket_listen($master);
        //初始化sockets连接池
        $sockets=array($master);
        //对一些必要信息的输出记录
        echo "socket已连接,时间:".date("Y-m-d H:i:s")."
    ";
        echo "监听中:".$address.":".$port."
    ";
        //设置循环使脚本持续运行处理消息
        while(true){
            //用来检测是否有变化的数组(就是有新消息到或者有客户端连接/断开)
            $changes=$sockets;
            $write=NULL;
            $except=NULL;
            /**
             * 对于个人理解,这个函数的作用为阻塞程序往下执行,它会不停的检验$changes是否有变化,没有变化就将阻断程序往下执行。
             * 只有出现$changes出现变化(有新消息到或者有客户端连接/断开)才会对继续执行程序。
             * 很重要的一个函数。有的说法是同时接受多个连接的关键
             * @param array $write是监听是否有客户端写数据,传入NULL是不关心是否有写变化。
             * @param array $except是$sockets里面要被排除的元素,传入NULL是”监听”全部。
             * @param int 最后一个参数是超时时间
             * 如果为0:则立即结束
             * 如果为n>1: 则最多在n秒后结束,如遇某一个连接有新动态,则提前返回
             * 如果为null:如遇某一个连接有新动态,则返回
             */
            socket_select($changes,$write,$except,NULL);
            //这里遍历检测出出现何种变化,然后进行处理
            foreach($changes as $sock){
                //当下列条件的满足时,表示有新用户连接进来
                if($sock==$master){
                    //接受该用户的连接
                    $client=socket_accept($master);
                    //给这个用户生成一个独一无二的id,用与获取该用户的信息的唯一标识。
                    $key=uniqid();
                    //将新用户存入socke连接池
                    $sockets[]=$client;
                    //记录用户连接的信息,为了方便能对指定用户发送消息。其中handshake代表服务器与客户端握手与否,socket的另外一个重要的操作
     
                    $users[$key]=array(
                        "socket"=>$client,
                        "handshake"=>false,
                    );
                    echo "分配id为".$key."的用户连接
    ";
                }
                // 剩下的为用户断开连接或者用户向服务端发送信息
                else{
                    $len=0;//收到数据的长度
                    $buffer='';//收到的数据
                    /**
                     * socket_recv( resource $socket, string &$buf, int $len, int $flags) : int
                     * 函数 socket_recv() 从 socket 中接受长度为 最大为$len 字节的数据,并保存在 buf 中,$l返回的为实际读取数据的长度。 
                     * socket_recv() 用于从已连接的socket中接收数据。除此之外,可以设定一个或多个 flags 来控制函数的具体行为。 
                     */
                    //通过循环的方式读取全部数据$len可根据自身设置
                    do{
                        $l=socket_recv($sock,$buf,1000,0);
                        $len+=$l;
                        $buffer.=$buf;
                    }while($l==1000);
     
                    $tmpk;//获取操作用户的key,即一开始分配的唯一标识id
                    foreach($users as $k=>$v){//$k为键,$v为值
                        if($sock==$v['socket']){
                            //获取连接的用户数组users,当users里存在有只返回该用户被分配的唯一id
                            $tmpk=$k;
                        }
                    }
     
                    // 如果数据长度小于7为断开连接
                    if($len<7){      
                        socket_close($users[$k]['socket']);//关闭该用户连接,可以写成socket_close($sock),这种写法是封装后的写法,为了容易看懂不进行封装;
                        unset($users[$tmpk]);//销毁指定的users的某个用户信息
                        $sockets=array($master);//可以理解为初始化sockets连接池
                        //遍历users数组,将连接的信息存入$sockets中
                        foreach($users as $v){
                            $sockets[]=$v['socket'];
                        }
                        echo "id为".$tmpk."用户断开连接
    ";
                        continue;
                    }
                    //服务端与用户握手
                    //如果没有与客户端握手,数据交换都会错误。
                    //一旦服务器发送了以下头文件,握手就完成了,我们就可以交换数据了,可以理解为检验身份差不多的意思
                    if(!$users[$tmpk]['handshake']){
                        //截取客户端请求时发送给服务端Sec-WebSocket-Key的值并加密,其中$key后面的一部分258EAFA5-E914-47DA-95CA-C5AB0DC85B11字符串应该是固定的
                        $buf = substr($buffer,strpos($buffer,'Sec-WebSocket-Key:')+18);
                        $key=trim(substr($buf,0,strpos($buf,"
    ")));//前两步可以直接替换为trim(substr($buffer,strpos($buffer,'Sec-WebSocket-Key:')+16))
                        $new_key=base64_encode(sha1($key."258EAFA5-E914-47DA-95CA-C5AB0DC85B11",true));
                        //向客户端返回该信息,也就是所说的握手。
                        $hand_message="HTTP/1.1 101 Switching Protocols
    "
                                        ."Upgrade: websocket
    "
                                        ."Sec-Websocket-Version: 13
    "
                                        ."Connection: Upgrade
    "
                                        ."Sec-Websocket-Accept: ".$new_key."
    
    ";
                        /**
                         * writes to the socket from the given buffer
                         * 向指定的socket发送信息
                         * 这里向用户发送握手信息
                         */
                        $status=socket_write($users[$tmpk]['socket'],$hand_message,strlen($hand_message));
                        if($status){
                            echo "与用户id".$tmpk."握手成功
    ";
                            echo $hand_message."
    ";
                        }
                    }
                    // 最后剩下的就为用户发送消息,做接收操作,由于需要包含二进制数据的转换,需了解websocket的数据收发协议,下一篇将更新接下来数据的处理
                    else{
                        //接收数据处理操作
                    }
                }
            }
        }
    ?>
    

      结语:由于接下来数据的接收与发送,会涉及到数据的解码与编码,下一篇内容将会介绍数据的发送与接收,对各个操作都详细的解释。

      自己学习过程中没看到叫详细的教程,就写个专题关于WebSocket的使用,当然也可以使用workman等开源通讯框架,少去很多麻烦,在这里也是为了构造自己的通讯方式,自己编写。
    ————————————————
    原文链接:https://blog.csdn.net/Vae_sun/article/details/90318326

  • 相关阅读:
    python 快速排序详述
    GitHub 小试牛刀(踩坑记录)
    python 内部类
    Django1.11搭建一个简易上传显示图片的后台
    Ubuntu16.04卸载opencv2.4.9并安装opencv3.2.0+contrib
    OpenCV4Android背景建模(MOG、MOG2)
    CentOS7.3安装NVIDIA-1080ti驱动、cuda、cudnn、TensorFlow
    TensorFlow Object Detection API(Windows下测试)
    关于git常见的一些问题
    聊聊Java中的反射(一)
  • 原文地址:https://www.cnblogs.com/7qin/p/13299522.html
Copyright © 2020-2023  润新知