小程序 + PHP 使用 Swoole 实现实时聊天功能(Tp5框架)

4 篇文章 0 订阅
1 篇文章 0 订阅

配置

1,小程序域名必须是https

2,小程序的话证书必须要设置,因为是https的

(如果用的宝塔免费证书:证书路径是 /www/server/panel/vhost/ssl/站点名称/证书。如果前面路径跟我不一样的话必须找站点下面的那个证书,否则不能使呀)

3,阿里云安全组需要放行你用的端口(我开的是9501 - 9502),如果用的宝塔面板,宝塔上也需要在安全里面放行9501 - 9502端口

代码

1,放一下代码,tp5.1我在项目根目录放了一个这样文件(这样直接访问这个文件就可以运行服务了)

Socket.php(名字随便)

<?php

// [ 应用入口文件 ]
namespace think;

// 加载基础文件
require __DIR__ . '/../thinkphp/base.php';

// 支持事先使用静态方法设置Request对象和Config对象

// 绑定到index模块 执行应用并响应
Container::get('app')->bind('socket/Notice')->run()->send();

5,Server代码(注释都在里面):

app/socket/controller/Index.php

<?php
namespace app\Socket\controller;
use think\Db;

class Index
{
    private $server;
    private $redis;
    public function __construct()
    {
        // 创建服务,因为是小程序所以设置成,0.0.0.0 都可以访问的
        $this->server = new \swoole_websocket_server("0.0.0.0", 9502, SWOOLE_PROCESS, SWOOLE_SOCK_TCP | SWOOLE_SSL);
        // 小程序需要设置证书
        $this->server->set([
            'ssl_cert_file'=>'/www/xxxxxxxxxxx/fullchain.pem',
            'ssl_key_file'=>'/www/xxxxxxxxxxxxx/privkey.pem',
        ]);
        // 建立连接
        $this->server->on('Open', [$this, 'onOpen']);
        // 发送消息
        $this->server->on('Message', [$this, 'onMessage']);
        // 关闭连接
        $this->server->on('Close', [$this, 'onClose']);

        $this->server->start();
    }

    /**
     * 客户端连接
     * @param  [type] $server 服务端
     * @param  [type] $req    请求数据
     * @return [type]         [description]
     */
    public function onOpen($server, $req)
    {
        // 使用 redis 存一下客户端ID,用来推送消息
        // 可能有人要问为啥没有把实例化放在构造函数呢?
        //              这个构造函数只是在开启服务的时候执行一次,如果放在构造函数的话时间稍微长一点,下面使用redis就会出现断开的情况。
        //              不过可以改成redis长连接
        $this->redis->connect('127.0.0.1', 6379);

        // var_dump($req);
        // 让前端打开连接的时候存一个 用户ID => 客户端ID,这样就可以推送
        $id = $req->get['uid'];
        $this->redis->hSet('clientId', $id, $req->fd);

        echo "客户端:{$req->fd} 已连接 \n";
    }

    /**
     * 消息推送
     * @param  [type] $server 服务端
     * @param  [type] $frame  请求数据
     * @return [type]         [description]
     */
    public function onMessage($server, $frame)
    {
        // 数据解析成对象
        $data = json_decode($frame->data);
        
        // 内容名字和带哪些参数(比如:用户ID,对方的用户ID,头像,等等。。)是由前端决定的。最主要的就是自己的ID,对方的ID,和聊天内容
        var_dump($data);
        // uid => 用户ID
        // tid => 对方用户ID
        // cont => 聊天内容

        //  判断用户是否在聊天页面上:  在  :推送内容,并将内容加入数据库
        //                            不在:直接将内容加入数据库,设置未读
        $rr = $this->redis->hExists('clientId', $data->tid);
        if($rr) $server->push($this->redis->hGet('clientId', $data->tid), $frame->data);

        // 数据插入数据库
        $this->chatInsert($data);

        // 推送给自己
        $server->push($frame->fd, $frame->data);

        // 如果 A 和 B 在一个页面聊天,C 又给 A 发了一条消息,这样 A 和 B 的聊天列表里面,A 也会收到 C 发送的消息,因为 A 满足了'在线'的要求,所以在前端接收消息那里需要判断推送给自己的消息 tid 不等于 对方ID 的时候不显示,(uid == 自己登录的uid && tid == 对方的ID)  

        // 这样就知道只要你在聊天页面就能收到所有人的消息,就可以实现一个你在聊天的过程中,顶部弹出一个提示框 【孙某人给您发了一条消息】
    }

    /**
     * 客户端关闭
     * @param  [type] $server [description]
     * @param  [type] $fd     [description]
     * @return [type]         [description]
     */
    public function onClose($server, $fd)
    {
        // 删除 redis 用户的客户端ID,百度了一下 hash 不能通过值获取键名所以全部获取循环获取键名吧。
        $all = $this->redis->hGetAll('clientId');
        if($all)
        {
            foreach ($all as $k => $v) {
                if($v == $fd){
                    $this->redis->hDel('clientId', $k);
                    break;
                }
            }
        }

        echo "客户端:{$fd} 已关闭 \n";
    }

    /**
     * 聊天记录插入数据库,并且更新聊天列表最后一次聊天
     * @param [type] $data [description]
     */
    public function chatInsert($data)
    {
        // 插入聊天记录和聊天列表,逻辑用自己的吧
        // ——————————————————————————————————————————————————————
        // 放我一个例子。
        $time = time();
        // 插入聊天记录
        $insert = [
            'cl_uid'    => $data->uid,
            'cl_tid'    => $data->tid,
            'cl_cont'   => $data->cont,
            'cl_time'   => $time
        ];
        Db::name('chats_log')->insert($insert);

        // 插入聊天列表,更新最后一条消息用于显示在列表
        Db::name('msg_log')
            ->where("(ml_tid = $data->tid AND ml_uid = $data->uid) OR (ml_tid = $data->uid AND ml_uid = $data->tid)")
            ->update(['ml_cont' => $data->cont, 'ml_time' => $time]);

        // 
        // 比如用户表是 users,这样的联查就可以实现聊天列表了(users表里有用户头像,昵称,等等。。), 不过会有点慢
        // us_id 是用户ID(亲测能用)
        // $list = Db::name('msg_log')
        //     ->alias('a')
        //     ->join('users b', '(a.ml_uid <> 971 AND a.ml_uid = b.us_id) OR (a.ml_tid <> 971 AND a.ml_tid = b.us_id)')
        //     ->where('ml_uid', 971)
        //     ->whereOr('ml_pid', 971)
        //     ->select();

    }
}
// 启动服务器
$server = new Index();

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值