swoole创建websocket服务并且支持https服务,同时监听tcp,udp端口

 '0.0.0.0', // 监听地址
        'port' => 9501, // 监听端口
        'secret_key' => '123456', //允许推送消息的秘钥
        'mode' => "SWOOLE_PROCESS", // 运行模式 默认为SWOOLE_PROCESS
        'sock_type' => ["SWOOLE_SOCK_TCP", "SWOOLE_SSL"], //数组 sock type 默认为SWOOLE_SOCK_TCP | SWOOLE_SSL
        'isInsertMysql' => true, //发送消息后是否写入到mysql数据库

        // 分布式服务器通道 //允许哪些ip +秘钥连接到与本机接收推送消息
        "distributed" => [

            'local' => [
                // 本机配置
                "host" => '27.1.0.1', //公网ip
                "ip" => '192.168.0.150', //本机ip局域组网,分布式接口ip
                "secret_key" => "123456",
                "port" => 9501,
            ],
            // 搭档服务器配置
            "websocket_group" => [
                [
                    "ip" => "192.168.0.150", //ip组网,分布式连接ip
                    "secret_key" => "123456", //组网,分布式连接秘钥
                    "port" => 9502, //服务端开启服务的端口号
                ], [
                    "ip" => "192.168.0.150",
                    "secret_key" => "123456",
                    "port" => 9502, //
                ],
            ],
        ],
        'set' => [
            'dispatch_mode' => 2, //绑定uid=5
            'daemonize' => false, //守护进程化。

            'heartbeat_check_interval' => 10, //间隔多少秒进行心跳检测
            'heartbeat_idle_time' => 60, //间隔多少秒无状态 踢下线

            'worker_num' => 1, //开启进程数量

            'insert_db_turns_time' => 5, //轮流读取redis缓存写入数据库。间隔秒,一般对数据库实时要求并不高,时间可以设置在5-10秒,读取redis缓存消息堆,批量写入数据库,减少数据库操作频率。提升写入性能
            'insert_db_number' => 1000, //间隔秒,读取redis缓存消息堆数量,批量写入数据库,如果单台redis主机建议每秒吞吐量2000以内。
        ],
    ];

    // 自动注册属性 防止连接丢失
    public function __get($name) {

        switch ($name) {

        // case 'Arr':
        //  // 连接Arr工具类
        //  return $this->$name = new Arr;
        //  break;
        default:
            # code...
            break;
        }
    }
    public function __set($name, $vue) {
        $this->$name = $vue;
    }
    public function __construct($config = []) {
        if (!empty($config) && is_array($config)) {
            $websocketConfig = array_merge($this->websocketConfig, $config);
        } else {
            $websocketConfig = array_merge($this->websocketConfig, App::config('websocket'));
        }
        $isSsl = false;
        if (isset($websocketConfig['set']['ssl_cert_file']) && isset($websocketConfig['set']['ssl_key_file']) && !empty($websocketConfig['set']['ssl_cert_file'])) {
            $websocketConfig['set']['ssl_cert_file'] = App::getRootPath() . '/ssl/' . $websocketConfig['set']['ssl_cert_file'];
            $websocketConfig['set']['ssl_key_file'] = App::getRootPath() . '/ssl/' . $websocketConfig['set']['ssl_key_file'];
            $isSsl = true;
        }

        echo "serve\websocket\chat.php isSsl:$isSsl:\n";
        // ws服务
        // print_r($websocketConfig);
        if (isset($websocketConfig['sock_type'][1]) && $isSsl) {

            $server = new Server($websocketConfig['ip'], $websocketConfig['port'], constant($websocketConfig['mode']), constant($websocketConfig['sock_type'][0]) | constant($websocketConfig['sock_type'][1]));
            echo "开启wss服务\n";
        } else {
            $server = new Server($websocketConfig['ip'], $websocketConfig['port'], constant($websocketConfig['mode']), constant($websocketConfig['sock_type'][0]));
        }
        // $this->distributed = $this->websocketConfig['distributed'];
        $local = $websocketConfig['distributed']['local']; //本机ip配置
        $websocket_group = $websocketConfig['distributed']['websocket_group'] ?? null; //联机ip配置

        // $websocketConfig['set']['dispatch_func'] = function ($serv, $fd, $type, $data) use ($websocket_group) {
        //  // echo $worker_num = $serv->setting['worker_num'] . "\n";
        //  // $type数据的类型,0表示来自客户端的数据发送,4表示客户端连接关闭,5表示客户端连接建立
        //  if ($worker_num > 1 && !empty($websocket_group)) {
        //      $clientInfo = $serv->getClientInfo($fd);
        //      // $clientInfo['remote_ip'];
        //      foreach ($websocket_group as $key => $value) {
        //          if ($value['ip'] === $clientInfo['remote_ip']) {
        //              return 0;
        //          }
        //      }
        //      $worker = ($fd % ($worker_num - 1)) + 1;

        //      return $worker;
        //  } else {
        //      return 0;
        //  }

        //  return intval($data[0]);
        // };
        // $this->websocketConfig = $websocketConfig;
        // $websocketConfig['set']['config'] = $this->websocketConfig;
        $server->websocketConfig = $websocketConfig;
        // print_r($websocket_group);
        // return;
        //配置参数
        $server->set($websocketConfig['set']);
        // $onManagerStart = new \serve\websocket\managerstart\Message($server); //onMeanagerStert 绑定
        // 当管理进程启动时调用它
        $server->on('ManagerStart', [$this, 'onManagerStart']);
        // $server->on('ManagerStart', [$onManagerStart, 'onManagerStart']);
        // $server->set(array("open_websocket_close_frame" => true));
        //启动每个worker进程会触发
        $server->on('WorkerStart', [$this, 'onWorkerStart']);
        //建立连接触发
        // $server->on('open', [$this, 'onOpen']);
        $onOpen = new \serve\websocket\open\Message($server); //onOpen 绑定类
        $onMessage = new \serve\websocket\message\Message($server); //onMessage绑定
        // $message->config = $websocketConfig;
        $onTask = new \serve\websocket\task\Message($server); //onTask绑定
        //接收到消息
        // $server->on('message', [$message, 'onMessage']);
        // $server->on('task', [$this, 'onTask']); //接收到消息
        // $server->on('finish', [$this, 'onFinish']); //接收到消息
        // $server->on('receive', [$this, 'onReceive']); //接收到消息
        //关闭连接触发
        $server->on('close', [$this, 'onClose']);

        //创建Server对象,监听 127.0.0.1:9502端口,类型为SWOOLE_SOCK_UDP
        $udp_server = $server->listen('192.168.0.150', 9502, SWOOLE_SOCK_UDP);

        // udp服务器组
        $udpsocket_group = $websocketConfig['distributed']['udpsocket_group'] ?? null;
        //监听数据接收事件
        $udp_server->on('packet', function ($serv, $data, $clientInfo) use ($server, $udpsocket_group) {
            // print_r();
            $isAuth = false;
            if ($clientInfo['address'] === '127.0.0.1') {
                $isAuth = true;
            } else {
                $data = json_decode($data, true);
                foreach ($udpsocket_group as $key => $value) {
                    if ($value['ip'] === $clientInfo['address'] && $value['secret_key'] === $data['sk']) {
                        $isAuth = true;
                        break;
                    }
                }
            }
            if ($isAuth === true) {
                $data = [
                    'message' => 'ok',
                    'code' => 200,
                    'data' => $data,
                ];

            } else {
                $data = [
                    'message' => '失败',
                    'code' => 400,
                    'data' => $data,
                ];
            }
            $data = json_encode($data, JSON_UNESCAPED_UNICODE);
            $serv->sendto($clientInfo['address'], $clientInfo['port'], "Server " . $data);

            // var_dump($clientInfo);
        });

        //......设置各个回调......
        //多监听一个tcp端口,对外开启tcp服务,并设置tcp服务器的回调
        $tcp_server = $server->listen('0.0.0.0', 9503, SWOOLE_SOCK_TCP);
        //需要调用 set 方法覆盖主服务器的设置
        $tcp_server->set([
            // "ssl_cert_file" => App::getRootPath() . '/ssl/' . $websocketConfig['set']['ssl_cert_file'],
            // "ssl_key_file" => App::getRootPath() . '/ssl/' . $websocketConfig['set']['ssl_key_file'],
            // 'open_mqtt_protocol' => true,
            'open_http_protocol' => false, // 设置这个端口关闭http协议功能

        ]);

        // $udp_server = $server->listen('0.0.0.0', 9502, SWOOLE_SOCK_UDP);
        // //默认新监听的端口 9999 会继承主服务器的设置,也是 Http 协议
        // //需要调用 set 方法覆盖主服务器的设置
        // $udp_server->set([
        //  'open_http_protocol' => false, // 设置这个端口关闭http协议功能
        // ]);
        // $udp_server->on("receive", function ($serv, $fd, $threadId, $data) use ($server) {
        //  print_r($data);
        //  // $serv->push($fd, $data);
        // });
        $tcp_server->on("message", function ($serv, $data) use ($server) {
            echo "tcp_server";
            echo $data;
            // $serv->push($fd, $data);
        });

        $tcp_server->on('connect', function ($serv, $fd) {
            echo "Client:Connect.\n";
            $serv->send($fd, '{"fd":"' . $fd . '"}' . "\n");
        });

        $tcp_server->on('receive', function ($serv, $fd, $from_id, $data) {

            $clientInfo = $serv->getClientInfo($fd);
            // print_r($clientInfo);
            $distributed = $this->websocketConfig['distributed'];
            $source = $this->websocketConfig['source'];
            $socket_group = $distributed['socket_group'];

            // 验证ip+fd是否合法
            if (isset($GLOBALS['client'][$fd]) && $GLOBALS['client'][$fd]['isServer'] == true) {
                echo "验证通过$fd\n";
                print_r($data);
                $data = json_decode($data, true);
                $serv->send($fd, json_encode($data, JSON_UNESCAPED_UNICODE) . "\n");

                # code...
            } else {
                // 验证服务器是否合法
                // if ($clientInfo['remote_ip'] == $addrIp) {
                $data = json_decode($data, true);

                // if (isset($data['sk']) && $data['sk'] == $sk) {
                // $serv->$addrIp = $fd;

                // 分布服务器认证授权
                echo "\n组网服务器正则进行连接验证fd:{$fd} ip:{$clientInfo['remote_ip']} sk:{$data['sk']}\n";
                // $distributed = $this->distributed;
                // $websocket_group = $server->websocketConfig['websocket_group'];

                if (isset($data['sk']) && !empty($socket_group) && is_array($socket_group)) {
                    $ip = $clientInfo['remote_ip']; //获取客户端ip
                    foreach ($socket_group as $key => $value) {

                        if ($value['ip'] === $ip && $value['secret_key'] === $data['sk']) {
                            echo "\n组网服务器已连接fd:{$fd} ip:{$ip} sk:{$data['sk']}\n";
                            // ip +密码验证通过 创建授权服务器的联网许可证明,保存到redis缓存服务器
                            // $fdinfo = $server->getClientInfo($request->fd);
                            // $ip = $fdinfo['remote_ip'];

                            $uid = intval(ip2long($distributed['local']['ip']) . $distributed['local']['port'] . ip2long($ip));
                            // print_r($this->distributed['local']);
                            $ClientInfo = [
                                'fd' => $fd,
                                // 'uid' => $uid,
                                "port" => $distributed['local']['port'],
                                'ip' => $distributed['local']['ip'],
                                "time" => time(),
                            ];
                            $data['source'] = 'pc';
                            // $server->bind($request->fd, 12355464110647986);
                            // $fdinfo = $server->getClientInfo($request->fd);
                            $fdInfo = [
                                'uid' => $uid,
                                'isServer' => true,
                                "source" => $data['source'],
                                'time' => time(),
                            ];
                            $GLOBALS['client'][$fd] = $fdInfo;
                            //uid缓存到本机缓存池, key=本机ip

                            $oldClient = $this->redis()->hGet("ws_clientInfo", $uid);
                            is_array($oldClient) or $oldClient = [];
                            $oldClient[$data['source']] = $ClientInfo;
                            $this->redis()->hSet("ws_clientInfo", $uid, $oldClient);
//———————————————————————————————验证通过 存入redis——fdInfo=hGet(ws_fdInfo.local->ip,fd)————clinetInfo= hGet(ws_clientInfo,uid)————————————————————————————————————

                            $returnMsg = [
                                'message' => '授权成功',
                                'code' => 200,
                                'data' => [],
                            ];
                            // $server->push($fd, json_encode($returnMsg));
                            echo "验证通过$fd\n";
                            $serv->send($fd, json_encode($returnMsg, JSON_UNESCAPED_UNICODE) . "\n");

                            return;

                        }
                    }

                }

                $serv->close($fd);
            }

        });

        $tcp_server->on('close', function ($serv, $fd) {
            echo "Client: Close.\n";
        });

        $tcp_server->on('packet', function ($serv, $data, $addr) {
            var_dump($data, $addr);
        });
        // $onRequest = new \serve\websocket\request\Message($server); //onRequest绑定
        $server->on('request', function ($request, $response) use ($server) {
            print_r('http\n');
            $client = new \swoole_client(SWOOLE_SOCK_TCP);
            if ($client->connect('127.0.0.1', 9503, 1)) {
                $client->send("data");
                echo $client->recv();
                $client->close();
            } else {
                echo "connect failed.";
            }

            $response->end('fdsfdsf');
        });
        // echo "\n已经启动server->start\n";
        $server->start();
        // $this->insertDb();
    }
    public function onManagerStart($server) {
        // 开启轮询读取redis缓存数据堆,执行批量写入数据库
        // swoole_timer_tick(6000, function ($timer_id) {
        //  $this->insertDb();
        //  echo "tick-4000ms\n";

        // });
        echo "\n管理进程启动server->start,onManagerStart\n";
    }

    // 启动worker进程触发
    public function onWorkerStart($server, $worker_id) {
        echo "启动worker_id:$worker_id\n";
        // if ($server->worker_id == 0) {
        // }
    }

    public function onFinish($server, $task_id, $data) {
        // print_r([$data, $task_id]);
        echo "onFinish\n";
        // print_r($response);
    }

    // 断开连接触发
    public function onClose($server, $fd) {
        echo "\nclient {$fd} closed.\n";
        // if ($server->exist($fd)) {
        //  $server->push($fd, 0);
        // }
        $distributed = $server->websocketConfig['distributed'];
        $fdInfo = $GLOBALS['client'][$fd];

        if (is_array($fdInfo) && isset($fdInfo['uid'])) {
            $clientInfo = $this->redis()->hGet("ws_clientInfo", $fdInfo['uid']);

            echo "删除前\n";
            print_r($fdInfo);
            print_r($clientInfo);

            if (!empty($clientInfo)) {
                $source = $clientInfo['source'] ?? "app";
                unset($clientInfo[$source]);
                if (empty($clientInfo)) {
                    // 没有在线的用户 清空缓存
                    $this->redis()->hDel("ws_clientInfo", $fdInfo['uid']);
                } else {
                    // 存在其他设备在线用户,只删除当前下线的用户缓存
                    $this->redis()->hSet("ws_clientInfo", $fdInfo['uid'], $clientInfo);
                }

            }

            // $this->redis()->hDel("ws_fdInfo:" . $distributed['local']['ip'] . $distributed['local']['port'], $fd);

        }
        unset($GLOBALS['client'][$fd]);
        echo "删除后\n";
        print_r($this->redis()->hGet("ws_fdInfo:" . $distributed['local']['ip'] . $distributed['local']['port'], $fd));
        print_r($this->redis()->hGet("ws_clientInfo", $fdInfo['uid']));
    }

    public function start() {
        $this->server->start();
    }

}

你可能感兴趣的:(swoole创建websocket服务并且支持https服务,同时监听tcp,udp端口)