• PHP Socket 编程基础入门——实例WebSocket 通信演示


    目前的代码只适合一个人用,逻辑中排斥多用户
    1、服务端

    
    
    /**
     * php /data/www/g-mall/test.gsj.com/websocket/socket_server.php
     * 浏览器打开 http://test.gsj.com/websocket/socket_client.html
     * Class SocketService
     */
    class SocketService
    {
        private $address;//ip地址
        private $port;//端口
        private $_sockets; //对象
    
        public function __construct($address = '', $port = '')
        {
           
            if (!empty($address)) {
                $this->address = $address;
            }
            if (!empty($port)) {
                $this->port = $port;
            }
        }
        //创建对象
        public function service()
        {
            //获取tcp协议号码。
            $tcp = getprotobyname("tcp");
            //创建协议
            $sock = socket_create(AF_INET, SOCK_STREAM, $tcp);
            //socket_set_option ( resource $socket , int $level , int $optname , mixed $optval )用法说明:
            //函数将 optname 参数在指定的 protocollevel 上指定的选项设置为该套接字的 optval 参数所指向的值
            socket_set_option($sock, SOL_SOCKET, SO_REUSEADDR, 1);
            if ($sock < 0) {
                throw new Exception("failed to create socket: " . socket_strerror($sock) . "\n");
            }
            //绑定端口和ip
            socket_bind($sock, $this->address, $this->port);
            //在具体的地址下监听 socket 资源的收发操作
            socket_listen($sock, $this->port);
            
            // echo "listen on $this->address $this->port ... \n";
            $this->_sockets = $sock;
        }
        //运行
        public function run()
        {
            //创建
            $this->service();
            //获取客户端
            $clients[] = $this->_sockets;
         
            while (true) {
                $changes = $clients;
                $write = NULL;
                $except = NULL;
            
                //在指定的超时时间内对给定的套接字数组运行select()系统调用
                socket_select($changes, $write, $except, NULL);
                //
                foreach ($changes as $key => $_sock) {
                        echo $_sock;
                    if ($this->_sockets == $_sock) { //判断是不是新接入的socket
                        //socket_accept 接收一个新的 socket 资源 函数原型
                        if (($newClient = socket_accept($_sock)) === false) {
                            die('failed to accept socket: ' . socket_strerror($_sock) . "\n");
                        }
                        //socket_read 获取传送的数据 函数原型$newClient 1024长度
                        $line = trim(socket_read($newClient, 1024));
                        //握手处理
                        $this->handshaking($newClient, $line);
                        //获取client ip 查询给定套接字的远程端,可能返回主机端口或者Unix文件系统路径(取决于socket_create设置的类型)
                        socket_getpeername($newClient, $ip);
                        $clients[$ip] = $newClient;
              
                    } else {
                        //函数 socket_recv() 从 socket 中接收长度为 length 字节的数据,并保存在 data 中
                        socket_recv($_sock, $buffer, 2048, 0);
                        //解析接收数据
                        $msg = $this->message($buffer);
                        //在这里业务代码
                        echo "{$key} clinet msg:", $msg, "\n";
                        // fwrite(STDOUT, 'Please input a argument:');
                        // $response = trim(fgets(STDIN));
                        $response="接受了";
                        //发送
                        $this->send($_sock, $response);
                        echo "{$key} response to Client:" . $response, "\n";
                    }
                }
            }
        }
    
        /**
         * 握手处理
         * @param $newClient socket
         * @return int  接收到的信息
         */
        public function handshaking($newClient, $line)
        {
    
            $headers = array();
            //$line消息
            $lines = preg_split("/\r\n/", $line);
            foreach ($lines as $line) {
                $line = chop($line);
                
                if (preg_match('/\A(\S+): (.*)\z/', $line, $matches)) {
                    $headers[$matches[1]] = $matches[2];
                }
            }
            //首先, Sec-WebSocket-Key 
            //是一个 Base64 的值,这个是浏览器随机生成的,告诉服务器:  ,我要验证尼是不是真的是Websocket助理
            $secKey = $headers['Sec-WebSocket-Key']; 
            $secAccept = base64_encode(pack('H*', sha1($secKey . '258EAFA5-E914-47DA-95CA-C5AB0DC85B11'))); 
            $upgrade = "HTTP/1.1 101 Web Socket Protocol Handshake\r\n" .
                "Upgrade: websocket\r\n" .
                "Connection: Upgrade\r\n" .
                "WebSocket-Origin: $this->address\r\n" .
                "WebSocket-Location: ws://$this->address:$this->port/websocket/websocket\r\n" .
                "Sec-WebSocket-Accept:$secAccept\r\n\r\n";
            //返回
            return socket_write($newClient, $upgrade, strlen($upgrade));
        }
    
        /**
         * 解析接收数据
         * @param $buffer
         * @return null|string
         */
        public function message($buffer)
        {
            $len = $masks = $data = $decoded = null;
            $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++) {
                $decoded .= $data[$index] ^ $masks[$index % 4];
            }
            return $decoded;
        }
    
        /**
         * 发送数据
         * @param $newClinet 新接入的socket
         * @param $msg   要发送的数据
         * @return int|string
         */
        public function send($newClinet, $msg)
        {
            $msg = $this->frame($msg);
            socket_write($newClinet, $msg, strlen($msg));
        }
        //转义处理
        public function frame($s)
        {
            $a = str_split($s, 125);
            if (count($a) == 1) {
                return "\x81" . chr(strlen($a[0])) . $a[0];
            }
            $ns = "";
            foreach ($a as $o) {
                $ns .= "\x81" . chr(strlen($o)) . $o;
            }
            return $ns;
        }
    
        /**
         * 关闭socket
         */
        public function close()
        {
            return socket_close($this->_sockets);
        }
    }
    
    $sock = new SocketService('192.168.31.20', '9000');
    $sock->run();
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170
    • 171
    • 172
    • 173
    • 174
    • 175
    • 176
    • 177
    • 178
    • 179
    • 180
    • 181
    • 182
    • 183
    • 184
    • 185
    • 186
    • 187

    命令行运行服务端;
    2、客户端

    doctype html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width,initial-scale=1, maximum-scale=1, user-scalable=no">
        <title>websockettitle>
    head>
    <body>
    <input id="text" value="">
    <input type="submit" value="send" onclick="start()">
    <input type="submit" value="close" onclick="close()">
    <div id="msg">div>
    <script>
        /**
         *0:未连接
         *1:连接成功,可通讯
         *2:正在关闭
         *3:连接已关闭或无法打开
         */
            //创建一个webSocket 实例
        var webSocket = new WebSocket("ws://192.168.31.20:9000");
    
    
        webSocket.onerror = function (event) {
            onError(event);
        };
    
        // 打开websocket
        webSocket.onopen = function (event) {
            onOpen(event);
        };
    
        //监听消息
        webSocket.onmessage = function (event) {
            onMessage(event);
        };
    
    
        webSocket.onclose = function (event) {
            onClose(event);
        }
    
        //关闭监听websocket
        function onError(event) {
            document.getElementById("msg").innerHTML = "

    close

    "
    ; console.log("error" + event.data); }; function onOpen(event) { console.log("open:" + sockState()); document.getElementById("msg").innerHTML = "

    Connect to Service

    "
    ; }; function onMessage(event) { console.log("onMessage"); document.getElementById("msg").innerHTML += "

    response:" + event.data + "

    "
    }; function onClose(event) { document.getElementById("msg").innerHTML = "

    close

    "
    ; console.log("close:" + sockState()); webSocket.close(); } function sockState() { var status = ['未连接', '连接成功,可通讯', '正在关闭', '连接已关闭或无法打开']; return status[webSocket.readyState]; } function start(event) { console.log(webSocket); var msg = document.getElementById('text').value; document.getElementById('text').value = ''; console.log("send:" + sockState()); console.log("msg=" + msg); webSocket.send("msg=" + msg); document.getElementById("msg").innerHTML += "

    request" + msg + "

    "
    }; function close(event) { webSocket.close(); }
    script> body> html>
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
  • 相关阅读:
    求二叉树中最大的二叉搜索子树的头节点
    Python爬虫——Urllib库-2
    心理学杂文
    [附源码]Python计算机毕业设计Django的个人理财系统
    从维基百科通过关键字爬取指定文本内容
    同学苹果ios的ipa文件应用企业代签选择签名商看看这篇文章你再去吧
    从0实战一个 vue3+ ts+element-plus 项目
    手撕Vue-数据驱动界面改变下
    HBase表数据的读、写操作与综合操作
    vuex中getter的使用
  • 原文地址:https://blog.csdn.net/wangzhae/article/details/127922777