千家信息网

怎么用swoole + js + redis实现简易聊天室

发表于:2025-02-03 作者:千家信息网编辑
千家信息网最后更新 2025年02月03日,这篇文章主要介绍"怎么用swoole + js + redis实现简易聊天室",在日常操作中,相信很多人在怎么用swoole + js + redis实现简易聊天室问题上存在疑惑,小编查阅了各式资料,
千家信息网最后更新 2025年02月03日怎么用swoole + js + redis实现简易聊天室

这篇文章主要介绍"怎么用swoole + js + redis实现简易聊天室",在日常操作中,相信很多人在怎么用swoole + js + redis实现简易聊天室问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答"怎么用swoole + js + redis实现简易聊天室"的疑惑有所帮助!接下来,请跟着小编一起来学习吧!

公司需要用到在线聊天功能,先写了个demo出来展示效果。

主要用到了swoole的websocket,task和redis的string,hash,set,zet等。

聊天室分为10个频道,可以切换频道,频道计数等。

目前还没做聊天内容的加密。

按需求,聊天内容可能会每个频道保留最近一百条,聊天加密的话考虑aes,也有可能不加。后续看情况了。

目前还没做异常处理。回头继续完善之后可能会再进行更新。

先上代码吧,有疑问可以留言交流。这里是html代码 可以自己引入jq地址。

        WebSocketTestPage    

服务端代码

  ['uid','gid'],        1       =>  ['new_room','old_room'],        2       =>  ['level','data','room_id','uid','name','head'],        4       =>  ['level','data','gid','uid','name','head'],    ];    public function __construct() {        if(!isset($this->redis)){            $this->redis = $this->_getRedis();            $this->redis->select(10);            $this->redis->flushDB();        }        $this->_resetChatNum();        if(!isset($this->ws)){            $this->ws = new swoole_websocket_server( HOST, PORT);            $this->ws->set(                [                    'worker_num'                => WORKER,                    'task_worker_num'          => TASK,                ]            );            $this->ws->on("open", [$this,"onOpen"]);            $this->ws->on("message", [$this,"onMessage"]);            $this->ws->on("close", [$this,"onClose"]);            $this->ws->on("task", [$this,"onTask"]);            $this->ws->on("finish", [$this,"onFinish"]);            $this->ws->start();        }    }    public function onOpen($ws, $request) {        $msg = array(            'msg_type' => 0,        );        $this->_pushSysNotice($request->fd);        $ws->push($request->fd, json_encode($msg));    }    private function _pushOldMsg($fd, $channel, $guild = false){        $m = $this->redis->lRange(CL . $channel,0,CL_MAX_SIZE);        $msg = [];        foreach($m as $v){            $msg[] = json_decode($v,true);        }        if(!empty($msg))            $this->ws->task(['fds'=>[$fd],'msg'=>$msg]);        $this->redis->lTrim(CL . $channel,0,CL_MAX_SIZE);        if($guild){            $g_m = $this->redis->lRange(GCL . $guild,0,CL_MAX_SIZE);            $g_msg = [];            foreach($g_m as $v){                $g_msg[] = json_decode($v,true);            }            if(!empty($g_msg))                $this->ws->task(['fds'=>[$fd],'msg'=>$g_msg]);            $this->redis->lTrim(GCL . $guild,0,CL_MAX_SIZE);        }    }    private function _pushSysNotice($fd){        $this->redis->select(0);        $keys = $this->redis->keys(SM . '*');        $msg = array(            'msg_type' => 3,        );        if(!empty($keys)){            foreach($keys as $v){                $s_msg = $this->redis->get($v);                $msg['data'] = $s_msg;                $this->ws->push($fd, json_encode($msg));            }        }        $this->redis->select(10);    }    public function onMessage($ws, $frame) {        $data = json_decode($frame->data,true);        $ret = [];        switch($data['msg_type']){            case 0://初始化                if($this->_checkParam($this->in_param[0], $data)){                    $ret = $this->_setConnectInfo($frame->fd, $data);                    if($data['gid'] > 0)                        $this->_pushOldMsg($frame->fd,$ret['room_id'],$data['gid']);                }else{                    $ret = ['msg_type'=>'no param'];                }                break;            case 1://切换频道                if($this->_checkParam($this->in_param[1], $data)){                    $ret = $this->_changeChannel($frame->fd, $data);                }else{                    $ret = ['msg_type'=>'no param'];                }                break;            case 2://聊天                if($this->_checkParam($this->in_param[2], $data)){                    $ret = $this->_pushChatInfo($frame->fd, $data);                }else{                    $ret = ['msg_type'=>'no param'];                }                break;            case 3:                break;            case 4://公会聊天                if($this->_checkParam($this->in_param[4], $data)){                    $ret = $this->_pushChatInfo($frame->fd, $data,true);                }else{                    $ret = ['msg_type'=>'no param'];                }                break;            default:                $ws->push($frame->fd, "error");                break;        }        echo "fd: {$frame->fd} Message: {$frame->data} \n";        if(!empty($ret)){            $ws->push($frame->fd, json_encode($ret));        }    }    public function onClose($ws, $fd) {        $this->_delChatInfo($fd, true);    }    public function onTask($ws, $taskId, $workerId, $data) {        foreach($data['fds'] as $v){            $this->ws->push($v,json_encode($data['msg']));        }        return $taskId;    }    public function onFinish($ws, $taskId, $data) {        echo "task-{$taskId} is end\n";    }    //初始化频道计数器    private function _resetChatNum(){        for($i = 1; $i <= CHANNEL_MAX_SIZE; $i++){            $this->redis->zAdd(CNL, 0, CR . $i);        }    }    //获取人数最少频道    private function _getSuggestRoomId(){        $chat_list = $this->redis->zRange(CNL,0,0);        $no = substr($chat_list[0],14);        if(empty($no) || (int)$no < 1 || (int)$no > CHANNEL_MAX_SIZE){            $no = mt_rand(1,CHANNEL_MAX_SIZE);        }        return (int)$no;    }    //连接时设置推荐频道    private function _setConnectInfo($fd, $data){        $channel = $this->_getSuggestRoomId();        $this->_setChannelInfo($fd,$channel);        $this->redis->hSet(CF.$fd, 'uid',$data['uid']);        $this->redis->hSet(CF.$fd, 'gid',$data['gid']);        if(!empty($data['gid'])){            $this->redis->sAdd(GR.$data['gid'], $fd);        }        return array(            'msg_type'      => 1,            'data'          => $channel,            'room_id'               => $channel,            'code'          => 0,            'fd'            => $fd,        );    }    //设置频道相关数据    private function _setChannelInfo($fd, $channel){        //存入fd        $this->redis->sAdd(CR . $channel,$fd);        //变更计数器        $this->redis->zIncrBy(CNL, 1, CR . $channel);        //存入用户频道信息        $this->redis->hSet(CF.$fd, 'channel',(int)$channel);    }    //校验字段    private function _checkParam($param, $data){        foreach($param as $v){            if(!isset($data[$v]))return false;        }        return true;    }    //切换频道    private function _changeChannel($fd, $data){        $this->_delChatInfo($fd);        $this->_setChannelInfo($fd,$data['new_room']);        $this->_pushOldMsg($fd,$data['new_room']);        return array(            'msg_type'      => 1,            'data'          => $data['new_room'],            'code'          => 1,        );    }    //关闭连接时清除相关内容    private function _delChatInfo($fd, $del = false){        $channel = $this->redis->hget(CF.$fd,'channel');        if(empty($channel)) return true;        $this->redis->sRem(CR .$channel, $fd);        if($del){            $this->redis->del(CF.$fd);        }        $this->redis->zIncrBy(CNL, -1, CR . $channel);    }    //推送聊天信息    private function _pushChatInfo($fd, $data, $guild = false){        if(!$guild){            $user = $this->redis->hGetAll(CF.$fd);            $fds = $this->redis->sMembers(CR . $user['channel']);            $this->redis->lPush(CL . $user['channel'],json_encode($data));            $type = 2;        }else{            $fds = $this->redis->sMembers(GR . $data['gid']);            $this->redis->lPush(GCL . $data['gid'],json_encode($data));            $type = 4;        }        $msg = array(            'msg_type'      => $type,            'uid'           => $data['uid'],            'name'          => $data['name'],            'data'          => $data['data'],            'level' => $data['level'],            'head'          => $data['head'],            'code'          => $type,        );        $this->ws->task(['fds'=>$fds,'msg'=>$msg]);        return [];    }    //初始化redis资源    private function _getRedis()    {        $redis = new Redis();        $redis->connect('127.0.0.1', 6379);        return $redis;    }}//启动new Ws();

请求示例

{"new_room":8,"old_room":1,"msg_type":1} //切换频道{"level":10,"data":"嗷嗷嗷啊","room_id":1,"msg_type":2,"uid":xxxx,"name":1028,"head":1}//聊天{"gid":xxxx,"uid":xxxx,"msg_type":0} //初始化,其实这块本来想写道open里面但是这样的话需要前端改动,就先这样了。

到此,关于"怎么用swoole + js + redis实现简易聊天室"的学习就结束了,希望能够解决大家的疑惑。理论与实践的搭配能更好的帮助大家学习,快去试试吧!若想继续学习更多相关知识,请继续关注网站,小编会继续努力为大家带来更多实用的文章!

0