原创TP6下workerman实现WebSocket及wss连接(安装SSL证书),并在线实现简单聊天实例

ThinkPHP 104 0 2024-07-08

TP6下workerman实现WebSocket及wss连接(安装SSL证书),并在线实现简单聊天实例

一、安装workerman

命令运行:

composer require topthink/think-worker (或 composer require workerman/workerman)

二、配置config/worker_server.php文件里面的worker_class参数(注意:反斜杠不能写错),如下图所示:

三、在上图worker_class参数路径创建文件worker.php,如下图所示:

文件内容如下:

<?php

namespace app\index\controller;

use think\facade\Db;
use think\worker\Server;
use Workerman\Lib\Timer;

//define('HEARTBEAT_TIME', 20); // 心跳间隔

class Worker extends Server
{
protected $socket = 'websocket://0.0.0.0:2345/wss'; //设置地址

protected $online_count = 0;  //统计在线人数


    /* 此处为实现SSL证书wss连接
    protected $context = [
        'ssl' => [
            'local_cert' => 'C:/Apache24/ssl/api_hilo8_com.crt', //证书文件的存放路径(请改成自己的路径)
            'local_pk' => 'C:/Apache24/ssl/api_hilo8_com.key', //证书私钥的存放路径(请改成自己的路径)
            'verify_peer' => false,
            //'allow_self_signed' => true, //如果是自签名证书需要开启此选项
        ],
    ];

    protected $option = [
        'transport' => 'ssl',   //设置transport开启ssl,启用wss://
    ];
    */

    /**
     * 收到信息
     * @param $connection
     * @param $data
     */
    public function onMessage($connection, $data)
    {
        $origin = json_decode($data, true);
        switch ($origin['type']) {
                //绑定用户
            case 'login':
                $connection->uid = $origin['uid'];
                $connection->uname = $origin['uname'];
                $connection->login_time = time();
                $this->online_count++;
                $this->onlineUid();
                $send_content = [
                    'type' => 'text',
                    'content' => '用户(' . $connection->uid . '=' . $connection->uname . ')已上线!' . $connection->login_time
                ];
                $this->sendMessage($send_content);
                break;
                //收到消息,并回复
            case 'text':
                $user = [];
                $user['from_uid'] = $origin['from_uid']; // 发送方用户uid
                $user['from_uname'] = $origin['from_uname']; //发送方用户名称
                $user['to_uid'] = $origin['to_uid']; // 接收方用户uid
                $user['to_uname'] = $origin['to_uname']; //接收方用户名称
                $content = $origin['content']; // 需要发送到对方的内容
                $send_content = [
                    'type' => 'text',
                    'user' => $user,
                    'content' => $content
                ];
                $this->sendMessage($send_content, $origin['to_uid']);
                break;
        }
        echo ("收到消息:{$data}\n");
    }

    /**
     * 当连接建立时触发的回调函数
     * @param $connection
     */
    public function onConnect($connection)
    {
        $link_time = time();
        echo "连接时间:{$link_time}\n";
        $send_content = [
            'type' => 'connect',
            'content' => '连接成功:' . $link_time
        ];
        $connection->send(json_encode($send_content, JSON_UNESCAPED_UNICODE));
    }

    /**
     * 当连接断开时触发的回调函数
     * @param $connection
     */
    public function onClose($connection)
    {
        if (isset($connection->uid)) {
            echo "连接关闭 " . $connection->uid . "=" . $connection->uname . " (time:" . time() . ")\n";
            $this->online_count--;
            $send_content = [
                'type' => 'text',
                'content' => '用户(' . $connection->uid . '=' . $connection->uname . ')已下线!' . time()
            ];
            $this->sendMessage($send_content);
        }
    }

    /**
     * 当客户端的连接上发生错误时触发
     * @param $connection
     * @param $code
     * @param $msg
     */
    public function onError($connection, $code, $msg)
    {
        echo "error $code $msg\n";
    }

    /**
     * 每个进程启动
     * @param $worker
     */
    public function onWorkerStart($worker)
    {
        Timer::add(10, function () use ($worker) {
            if ($this->online_count > 0) {
                $this->onlineUid();
            }
        });
    }

    //统计在线用户
    private function onlineUid()
    {
        $online_uid = $online_data = [];
        foreach ($this->worker->connections as $connection) {
            if (isset($connection->uid)) {
                //array_push($online_uid, $connection->uid . '=' . $connection->uname);
                $online_data[] = ['uid' => $connection->uid, 'uname' => $connection->uname];
            }
        }
        $send_content = [
            'type' => 'online',
            'count' => $this->online_count,
            'content' => $online_data
        ];
        $this->sendMessage($send_content);
    }

    //发送消息
    private function sendMessage($content = [], $uid = 0)
    {
        $message = json_encode($content, JSON_UNESCAPED_UNICODE);
        if (is_numeric($uid) && $uid > 0) {
            //单发消息
            foreach ($this->worker->connections as $connection) {
                if (isset($connection->uid) && $connection->uid == $uid) {
                    $connection->send($message);
                }
            }
        } else {
            //群发消息
            foreach ($this->worker->connections as $connection) {
                $connection->send($message);
            }
        }
    }
}

说明:虽然上面可以实现SSL证书wss连接,但不建议这样做,建议以服务器代理方式实现。

四、启动WebSocket,命令:php think worker:server

五、客户端连接,代码如下:

<!DOCTYPE html>
<html>

<head>
    <meta charset="utf-8">
    <meta name="renderer" content="webkit">
    <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>WebSocket聊天</title>
    <script type="text/javascript" src="https://www.hilo8.com/js/jquery.js"></script>
    <style>
        html,
        body {
            height: 100%;
            margin: 0;
            padding: 0;
        }

        .container {
            position: relative;
            height: 100%;
            overflow: auto;
        }

        .online-box {
            position: absolute;
            width: 150px;
            height: 100%;
            background-color: antiquewhite;
        }

        .online-box h4 {
            text-align: center;
        }

        .online-box ul li {
            cursor: pointer;
            padding: 5px 0px;
            font-size: 14px;
        }

        .online-box ul li:hover {
            color: #c00;
        }

        .main-box {
            margin-left: 150px;
        }

        #msg {
            color: #999;
            font-size: 12px;
            background-color: #f6f6f6;
            padding: 10px;
        }

        #msg p {
            margin: 10px 0px 0px 0px;
        }

        #msg p span {
            border: 1px solid #ddd;
            border-radius: 4px;
            padding: 3px 5px;
            display: block;
            font-size: 14px;
            color: #333;
            background-color: #fff;
            float: left;
        }

        #msg p span.self {
            background-color: #090;
            color: #fff;
            float: right;
        }

        #msg font{font-size: 12px; color: #999; display: block;}

        .clear {
            clear: both;
            *zoom: 1
        }

        .clear:after {
            content: '\20';
            clear: both;
            *zoom: 1;
            display: block;
            height: 0
        }
    </style>
</head>

<body>
    <div class="container">
        <div class="online-box">
            <h4>在线用户列表</h4>
            <ul>
                <li data-user="0,所有人">所有人</li>
            </ul>
        </div>
        <div class="main-box">
            <h1>WebSocket示例</h1>
            <p>
                <input type="text" id="uname" placeholder="请输入您的名称" />
                <button type="button">连接</button>
                <button type="button">关闭</button>
            </p>
            <p>
                接收人: <span id="to-uname-span">所有人</span><br>
                <input type="hidden" id="to-uid" value="0"><input type="hidden" id="to-uname" value="所有人">
                <input type="text" id="content" placeholder="请输入要发送的内容" />
                <button>发送</button>
            </p>
            <hr>
            <div id="msg" class="clear"></div>
        </div>

    </div>

    <script>
        $(function(){
            var socket;
            var uid = Math.floor(Math.random() * 10000) + 1; //随机生成用户id号
            var uname = ''; //用户名称
            /* 连接状态 */
            var state = 0;
            var sockState = function () {
                let status = ['未连接', '连接成功,可通信', '正在关闭', '连接已关闭或无法打开'];
                return status[state];
            }
            //显示服务器返回消息
            var showMsg = function (message) {
                $("#msg").append('<p class="clear">' + message + '</p>');
            }

            /* 打开连接事件 */
            $("button:eq(0)").click(function () {
                uname = $('#uname').val();
                if (uname == '') {
                    alert('请输入您的名称再连接');
                    return;
                }
                try {
                    /* 连接 */
                    socket = new WebSocket("ws://api.shiyunkj.com:2345/wss");

                    /* 绑定事件 */
                    /* 监听连接状态 */
                    socket.onopen = function () {
                        state = socket.readyState;
                        socket.send(JSON.stringify({ type: 'login', uid: uid, uname: uname }));
                    };

                    /* 接收消息 */
                    socket.onmessage = function (e) {
                        let res = JSON.parse(e.data);
                        switch (res.type) {
                            case 'online':
                                let online_count = $('.online-box ul li').length - 1;
                                let lis = '<li data-user="0,所有人">所有人</li>';
                                if (online_count != res.count) {
                                    $.each(res.content, function (key, val) {
                                        lis += '<li data-user="' + val.uid + ',' + val.uname + '">' + val.uname + '</li>';
                                    })
                                    $('.online-box ul').html(lis);
                                }
                                break;
                            case 'connect':
                            case 'text':
                                let str = '';
                                if (res.user != undefined) {
                                    let user = res.user.from_uname || '';
                                    if (user != '') { str = '<font>'+user+'</font>'; }
                                }
                                showMsg(str+"<span>" + res.content + '</span>');
                                break;
                            default:
                                showMsg("接收信息出错...");
                        }
                        //showMsg("<br>" + res.content);
                    };

                    /* 关闭连接 */
                    socket.onclose = function () {
                        state = socket.readyState;
                        showMsg("连接已关闭或无法连接服务器..." + state);
                    };
                } catch (exception) {
                    showMsg("有错误发生...");
                }
            });

            /* 发送数据事件 */
            $("button:eq(2)").click(function () {
                if (state != 1) {
                    alert(sockState());
                    return;
                } else if ($("#content").val() == "") {
                    alert("请输入发送内容!");
                    return;
                }

                try {
                    showMsg('<span class="self">' + $("#content").val() + '</span>');
                    let data = { type: "text", from_uid: uid, from_uname: uname, to_uid: $("#to-uid").val(), to_uname: $("#to-uname").val(), content: $("#content").val() };
                    socket.send(JSON.stringify(data));
                } catch (exception) {
                    showMsg("发送数据出错...");
                }

                /* 清空文本框 */
                $("#content").val("");
            });

            /* 断开连接 */
            $("button:eq(1)").click(function () {
                try {
                    showMsg("关闭连接...");
                    socket.close();
                } catch (exception) {
                    alert(sockState());
                }
            });

            //选择用户
            $('.online-box ul').on('click', 'li', function () {
                let arr = $(this).data('user').split(',');
                $('#to-uid').val(arr[0]);
                $('#to-uname').val(arr[1]);
                $('#to-uname-span').html(arr[1]);
            });

        });
    </script>
</body>

</html>


可以多人在线聊天,如下图演示:

上一篇:TP6 生成Qrcode二维码和Barcode条形码的方法

下一篇:TP6连接SQLite数据库及常用字段数据类型

讨论数量:0

请先登录再发表讨论。 2024-09-08

天涯网魂
3 杠 5 星
TA 的文章
TA 的随言
TA 的资源链