dmz社区

 找回密码
 立即注册

QQ登录

只需一步,快速开始

查看: 9606|回复: 190

[功能实现] PHP实现原生Socket服务器搭建和测试

  [复制链接]
  • TA的每日心情

    2024-11-19 20:46
  • 签到天数: 244 天

    [LV.8]以坛为家I

    4434

    主题

    1459

    帖子

    1万

    积分

    会|员

    Rank: 9Rank: 9Rank: 9

    积分
    10734
    发表于 2018-11-19 23:27:01 | 显示全部楼层 |阅读模式

    本站资源全部免费,回复即可查看下载地址!

    您需要 登录 才可以下载或查看,没有帐号?立即注册

    x
    1.socket服务器搭建思路 1) 目的:理解socket服务器工作机制
    2) 思路:创建socket -> 把socket加入连接池 -> 处理接收信息 -> 握手动作 -> 发送信息

    2.socket服务器代码​ 注:复制到php文件,直接命令行可以运行,不需要其他支持
    ​ 特别注意:为了能传输中文_sendMsg做了json_encode()
    [PHP] 纯文本查看 复制代码
    /**
     * Socket服务器
     * @author wuchangliang 2018/1/17
     */
    class SocketServer
    {
        private $sockets; //连接池
        private $master;
        private $handshake;
    
        /**
         * @param $address
         * @param $port
         */
        public function run($address, $port)
        {
            //配置错误级别、运行时间、刷新缓冲区
            echo iconv('UTF-8', 'GBK', "欢迎来到PHP Socket测试服务。 ");
            error_reporting(0);
            set_time_limit(0);
            ob_implicit_flush();
    
            //创建socket
            $this->master = $this->_connect($address, $port);
            $this->sockets[] = $this->master;
    
            //运行socket
            while (true) {
                $sockets = $this->sockets;
                $write = NULL;
                $except = NULL;
                socket_select($sockets, $write, $except, NULL); //$write,$except传引用
                foreach ($sockets as $socket) {
                    if ($socket == $this->master) {
                        $client = socket_accept($socket);
                        $this->handshake = false;
                        if ($client) {
                            $this->sockets[] = $client; //加入连接池
                        }
                    } else {
                        //接收信息
                        $bytes = @socket_recv($socket, $buffer, 2048, 0);
                        if ($bytes <= 6) {
                            $this->_disConnect($socket);
                            continue;
                        };
    
                        //处理信息
                        if (!$this->handshake) {
                            $this->_handshake($socket, $buffer);
                        } else {
                            $buffer = $this->_decode($buffer);
                            $this->_sendMsg($buffer, $socket);
                        }
                    }
    
                }
            }
        }
    
        /**
         * 创建socket连接
         * @param $address
         * @param $port
         * @return resource
         */
        private function _connect($address, $port)
        {
            //创建socket
            $master = socket_create(AF_INET, SOCK_STREAM, SOL_TCP)
            or die("socket_create() failed: reason: " . socket_strerror(socket_last_error()) . "");
            socket_bind($master, $address, $port)
            or die("socket_bind() failed: reason: " . socket_strerror(socket_last_error($master)) . "");
            socket_listen($master, 5)
            or die("socket_listen() failed: reason: " . socket_strerror(socket_last_error($master)) . "");
            return $master;
        }
    
        /**
         * 握手动作
         * @param $socket
         * @param $buffer
         */
        private function _handshake($socket, $buffer)
        {
            //握手动作信息
            $buf = substr($buffer, strpos($buffer, 'Sec-WebSocket-Key:') + 18);
            $key = trim(substr($buf, 0, strpos($buf, "")));
            $new_key = base64_encode(sha1($key . "258EAFA5-E914-47DA-95CA-C5AB0DC85B11", true));
            $new_message = "HTTP/1.1 101 Switching Protocols";
            $new_message .= "Upgrade: websocket";
            $new_message .= "Sec-WebSocket-Version: 13";
            $new_message .= "Connection: Upgrade";
            $new_message .= "Sec-WebSocket-Accept: " . $new_key . "";
    
            //记录握手动作
            socket_write($socket, $new_message, strlen($new_message));
            $this->handshake = true;
        }
    
        /**
         * 断开socket连接
         * @param $socket
         */
        private function _disConnect($socket)
        {
            $index = array_search($socket, $this->sockets);
            socket_close($socket);
            if ($index >= 0) {
                array_splice($this->sockets, $index, 1);
            }
        }
    
        /**
         * 发送信息
         * @param $buffer
         * @param $client
         */
        private function _sendMsg($buffer, $client)
        {
            $send_buffer = $this->_frame(json_encode($buffer));
            foreach ($this->sockets as $socket) {
                if ($socket != $this->master && $socket != $client) { //广播发送(除了自己)
                    socket_write($socket, $send_buffer, strlen($send_buffer));
                }
            }
        }
    
        /**
         * 解析数据帧
         * @param $buffer
         * @return null|string
         */
        private function _decode($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 $buffer
         * @return string
         */
        private function _frame($buffer)
        {
            $len = strlen($buffer);
            if ($len <= 125) {
                return "" . chr($len) . $buffer;
            } else if ($len <= 65535) {
                return "" . chr(126) . pack("n", $len) . $buffer;
            } else {
                return "" . char(127) . pack("xxxxN", $len) . $buffer;
            }
        }
    }
    
    $sc = new SocketServer();
    $sc->run('127.0.0.1', 2046);

    3.客户端代码​ 注:直接复制到html,和上面的php文件在同一文件夹即可,特别注意onmessage解析的两层 parse

    [PHP] 纯文本查看 复制代码
    <!DOCTYPE html>
    <html>
    <head>
        <meta charset="utf-8" />
        <title>WebSocket Test</title>
        <script language="javascript"type="text/javascript">
            websocket = new WebSocket('ws://127.0.0.1:2046/');
            websocket.onopen = function(evt) {
                console.log('connect');
                websocket.send('{"data":"您好,世界!"}');
            };
            websocket.onclose = function(evt) {
                console.log('onclose');
                console.log(evt);
            };
            websocket.onmessage = function(evt) {
                console.log('onmessage');
                if (evt.data) {
                    console.log(JSON.parse(JSON.parse(evt.data)));
                }
            };
            websocket.onerror = function(evt) {
                console.log('onerror');
                console.log(evt);
            };
            function sendMsg(){
                var sendData = { 'data': document.getElementById('name').value};
                websocket.send(JSON.stringify(sendData));
            }
        </script>
    </head>
    <body>
        <h2>WebSocket Test</h2>
        <input type="text" name="name" id="name" />
        <a href="javascript:;">点击发送</a>
    </body>
    </html>

    4.测试示例

    1491106-20180915141040765-613752885.png



    回复

    使用道具 举报

    该用户从未签到

    34

    主题

    7843

    帖子

    1025

    积分

    终身会员[A]

    Rank: 7Rank: 7Rank: 7

    积分
    1025

    发表于 2018-11-19 23:37:27 | 显示全部楼层
    有道理。。。
    回复 支持 反对

    使用道具 举报

    该用户从未签到

    27

    主题

    7861

    帖子

    1048

    积分

    终身会员[A]

    Rank: 7Rank: 7Rank: 7

    积分
    1048

    发表于 2018-11-19 23:44:59 | 显示全部楼层
    有竞争才有进步嘛
    回复 支持 反对

    使用道具 举报

    该用户从未签到

    20

    主题

    7854

    帖子

    996

    积分

    终身会员[A]

    Rank: 7Rank: 7Rank: 7

    积分
    996

    发表于 2018-11-20 03:21:31 | 显示全部楼层
    看帖回帖是美德!:lol
    回复 支持 反对

    使用道具 举报

    该用户从未签到

    18

    主题

    7781

    帖子

    928

    积分

    终身会员[A]

    Rank: 7Rank: 7Rank: 7

    积分
    928

    发表于 2018-11-20 04:10:38 | 显示全部楼层
    有竞争才有进步嘛
    回复 支持 反对

    使用道具 举报

    该用户从未签到

    23

    主题

    7985

    帖子

    1042

    积分

    终身会员[A]

    Rank: 7Rank: 7Rank: 7

    积分
    1042

    发表于 2018-11-20 08:14:45 | 显示全部楼层
    正需要,支持楼主大人了!
    回复 支持 反对

    使用道具 举报

  • TA的每日心情
    开心
    2024-5-11 15:58
  • 签到天数: 145 天

    [LV.7]常住居民III

    0

    主题

    469

    帖子

    1304

    积分

    一代宗师

    Rank: 7Rank: 7Rank: 7

    积分
    1304

    发表于 2018-11-20 08:46:15 | 显示全部楼层
    确实是难得好帖啊,顶先
    回复 支持 反对

    使用道具 举报

    该用户从未签到

    24

    主题

    7796

    帖子

    953

    积分

    终身会员[A]

    Rank: 7Rank: 7Rank: 7

    积分
    953

    发表于 2018-11-21 22:59:00 | 显示全部楼层
    支持一下:lol
    回复 支持 反对

    使用道具 举报

    该用户从未签到

    26

    主题

    7950

    帖子

    902

    积分

    终身会员[A]

    Rank: 7Rank: 7Rank: 7

    积分
    902

    发表于 2018-11-22 15:19:46 | 显示全部楼层
    正需要,支持楼主大人了!
    回复 支持 反对

    使用道具 举报

    该用户从未签到

    28

    主题

    7920

    帖子

    1033

    积分

    终身会员[A]

    Rank: 7Rank: 7Rank: 7

    积分
    1033

    发表于 2018-11-22 17:19:44 | 显示全部楼层
    么有分,谁能送我点积分啊::>_<::
    回复 支持 反对

    使用道具 举报

    您需要登录后才可以回帖 登录 | 立即注册

    本版积分规则

    QQ|Archiver|小黑屋|本站代理|dmz社区

    GMT+8, 2024-12-23 23:42 , Processed in 2.107185 second(s), 45 queries .

    Powered by Discuz! X3.4 Licensed

    Copyright © 2001-2021, Tencent Cloud.

    快速回复 返回顶部 返回列表