Tp6中运用swoole增加Room聊天室等功能

文章介绍了如何配置Swoole的WebSocket服务,包括启用WebSocket、设置处理器和解析器,以及`table`和`redis`两种数据处理方式。此外,文章详细讲解了创建和定义加入房间、离开房间及聊天事件的监听器,以及如何在`swoole.websocket`配置中设置监听事件。同时,提到了使用Redis扩展的情况,并展示了监听器和订阅类的代码示例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

config.swoole.php


'websocket'  => [        
        'enable'        => true,
        'handler'       => Handler::class,
        'parser'        => Parser::class,
        'ping_interval' => 25000,
        'ping_timeout'  => 60000,
        'room'          => [
            'type'  => 'table',
            'table' => [
                'room_rows'   => 4096,
                'room_size'   => 1024 * 100,
                'client_rows' => 8192,
                'client_size' => 1024,
            ],
            'redis' => [
                'host'          => '127.0.0.1',
                'port'          => 6379,
                'max_active'    => 3,
                'max_wait_time' => 5,
            ],
        ],
        'listen'        => [],
        'subscribe'     => [],
    ],

其中有 room 配置项,里面的 type 表示使用哪种数据处理方式,下面有两种,“table” 和“redis”,table 是可以直接拿来使用的,而 redis 则需要我们的系统和项目中安装了 redis 扩展。table 是一种高性能、跨进程的内存处理服务,不同进程间可以共享数据。

创建事件

在项目根目录输入如下命令,分别创建加入房间事件、离开房间事件和房间的聊天事件:

php think make:listener WsJoin
php think make:listener WsLeave
php think make:listener WsRoom

然后在 app/event.php 中定义事件:


[
    ],
    'listen'    => [
        'AppInit'  => [],
        'HttpRun'  => [],
        'HttpEnd'  => [],
        'LogLevel' => [],
        'LogWrite' => [],
        //监听连接,swoole 事件必须以 swoole 开头
        'swoole.websocket.Connect' => [
            app\listener\WsConnect::class
        ],
        //监听关闭
        'swoole.websocket.Close' => [
            \app\listener\WsClose::class
        ],
        //监听 Test 场景
        'swoole.websocket.Test' => [
            \app\listener\WsTest::class
        ],
        //加入房间事件
        'swoole.websocket.Join' => [
            \app\listener\WsJoin::class
        ],
        //离开房间事件
        'swoole.websocket.Leave' => [
            \app\listener\WsLeave::class
        ],
        //处理聊天室消息
        'swoole.websocket.WsRoom' => [
            \app\listener\WsRoom::class
        ],
    ],
    'subscribe' => [
    ],
];

上述的 Join、Leave和WsRoom等名称都是自定义的,要和前端发送消息的场景值对应。

当然,事件定义一样可以在 config/swoole.php 配置文件的 websocket listen 进行配置,也可以在subscribe直接定义一个类统一管理:


    'websocket'  => [
        //...跟上面代码一样,
        'subscribe'     => [
            app\subscribe\WsSubscribe::class
        ],
    ],

app/subscribe/WsSubscribe.php


<?php
declare (strict_types = 1);

namespace app\subscribe;
use think\Container;
use think\swoole\Websocket;

class WsSubscribe
{
    protected $webscoket = null;
    protected $db = null;

    public function __construct(Container $container)
    {
        $this->webscoket = $container->make(Websocket::class);
    }

    public function onConnect($event){
        $idcode = input('idcode');
        // echo date("Y-m-d H:i:s")." onConnect".PHP_EOL;
        // $redis = new Redis();
        // $redis->set('user_'.$uid,$this->webscoket->getSender());
        // echo 'ChatRoom_'.$idcode.PHP_EOL;
        if($idcode){
            $this->webscoket->join('ChatRoom_'.$idcode);
        }
        
        // echo $this->webscoket->getSender()."onConnect";
    }

    public function onMessage($event){
        // echo date("Y-m-d H:i:s")." onMessage".PHP_EOL;
        $this->webscoket->emit('pingcallback','pong');
        // $token = input('token');
        // [$state, $info, $uuid] = $this->checkToken($token);
        // if (empty($state)) { 
        //     $this->webscoket->emit('callback',$info);
        // }else{
        //     $redis = new Redis();
        //     $fd = $redis->get('user_'.$event['uuid']);
        //     if($fd){
        //         $json = [
        //             'from' => $uuid,
        //             'message' => $event['message']
        //         ];
        //         // $this->webscoket->to(intval($fd))->emit('messagecallback',$json);
        //         // $this->webscoket->to('testRoom')->emit('messagecallbackRoom',$json);
        //         // echo "message:".$event['message'].PHP_EOL;
        //     }
        // }
    }

    public function onReceive($event){
        echo date("Y-m-d H:i:s")." onReceive".PHP_EOL;
    }

    public function onJoin($event){
        if(isset($event['idcode'])){
            $idcode = $event['idcode'];
            $this->webscoket->join('ChatRoom_'.$idcode);
        }
        echo date("Y-m-d H:i:s")." onJoin".PHP_EOL;
    }

    public function onTest($event){
        if(isset($event['idcode'])){
            // echo 'ChatRoom_'.($event['idcode']).PHP_EOL;
            $idcode = $event['idcode'];
            $this->webscoket->to('ChatRoom_'.$idcode)->emit('callback',$event);
        }
    }

    public function onRequest($event){
        echo date("Y-m-d H:i:s")." onRequest".PHP_EOL;
    }

    public function onStart($event){
        echo date("Y-m-d H:i:s")." onSTART".PHP_EOL;
    }

    public function onClose($event){
        if(isset($event['idcode'])){
            // $idcode = $event['idcode'];
            // echo "{$idcode} Closed".PHP_EOL;
            // $this->webscoket->leave('ChatRoom_'.$idcode);
        }
    }

}

注意,正如前文所说,前端发送的必须跟后端对应,比如join,前端的代码应该如下


        ws.send(JSON.stringify(['join',{
            //这里可以自己定义事件
            idcode:"{$dev.idcode}"
        }])); //发送的数据必须是 "['test',数据]" 这种格式

加入房间的方式有很多,你也可以单独直接写一个test监听器,然后根据上传的类型判断发送消息所需要的方法,个人不觉得这几种方法有什么区别,实际按照个人习惯吧,懒得的折腾对代码没有强迫症的,完全可以整在一个监听器里面处理多种不同的运用场景。另外以下是单独的监听器代码,这里就不在多阐述了,代码如下

app/listener/WsJoin.php


<?php
declare (strict_types = 1);
namespace app\listener;
class WsJoin
{
    /**
     * 事件监听处理
     *
     * @return mixed
     */
    public function handle($event)
    {
        $ws = app('think\swoole\Websocket');
        $roomobj = app('think\swoole\websocket\Room');
        //当前客户端加入指定 Room
        $ws -> join($event['room']);
        //同时加入多个房间
        //$ws -> join(['room1','room2']);
        //指定客户端加入指定 room
        //$ws -> setSender(2) -> join($event['room']);
        //获取当前房间所有的 fd
        $getAllFdInRoom = $roomobj -> getClients($event['room']);
        //获取指定 fd 加入哪些房间
        $getAllRoom = $roomobj -> getRooms($ws -> getSender());
        var_dump('当前房间所有 fd:',$getAllFdInRoom);
        var_dump('当前 fd 加入的所有房间:',$getAllRoom);
        var_dump('当前请求数据:',$event);
        $ws -> emit('joincallback','房间加入成功');
    }
}

app/listener/WsLeave.php


<?php
declare (strict_types = 1);
namespace app\listener;
class WsLeave
{
    /**
     * 事件监听处理
     *
     * @return mixed
     */
    public function handle($event)
{
        $ws = app('think\swoole\Websocket');
        $roomobj = app('think\swoole\websocket\Room');
        // 当前客户端离开指定 room
        $ws -> leave($event['room']);
        // 同时离开多个 room
        //$ws -> leave(['one','two']);
        // 指定客户端离开指定 room
        //$ws -> setSender(2) -> leave($event['room']);
        // 获取指定 room 中的所有客户端 fd
        $getAllFdInRoom = $roomobj -> getClients($event['room']);
        var_dump('当前房间还剩 fd:',$getAllFdInRoom);
        $ws -> emit('leavecallback','房间离开成功');
    }
}

app/listener/WsRoom.php


<?php
declare (strict_types = 1);
namespace app\listener;
class WsRoom
{
    /**
     * 事件监听处理
     *
     * @return mixed
     */
    public function handle($event)
    {
        var_dump($event);
        $ws = app('think\swoole\Websocket');
        //给指定的 room 内所有 fd 发送消息,包括当前客户端,当前客户端没有加入该 room 也可发送
        $ws -> to($event['room']) -> emit('roomtestcallback',$event['message']);
        //指定多个 room 发送消息
        //$ws -> to(['one','two']) -> emit('客户端场景值',$event['message']);
    }
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值