基于 ThinkPhp5 WorkerMan Vue3 及websocket 实现的简易聊天室

成果如下在这里插入图片描述![在这里插入图片描述](https://img-blog.csdnimg.cn/de07bb3cb67d4ad4bc8a343af087c7a4.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAd2VpeGluXzQ1ODA2MTAy,size_8,color_FFFFFF,t_70,g_se,x_16在这里插入图片描述

环境搭建

1. tp5.0安装workerman
composer require topthink/think-worke

注:windows 环境下需要额外安装 workerman-for-win

composer require workerman/workerman-for-win
2.在项目根目录下新建 server.php

在这里插入图片描述
红色圈出的 是项目下真正用来处理请求的Worker 类
在这里插入图片描述
以下代码均为核心代码
Worker类的基本定义

use think\worker\Server;
class Worker extends Server
{
	// 建立的连接地址
    protected $socket = 'websocket://localhost:2345';
	// 存储当前连接的用户集合,下面会详细介绍
    public $clients=[];

    //用户模型
    public $m=null;
    //消息记录的模型
    public $message=null;
}

Workerman 提供的 Server类【think\worker\Server】 我们可以点进去查看,里面有一个init 方法,可以对其进行重写,并做一些初始化的操作,我仅在此对模型进行了初始化

 public function init()
    {
        parent::init(); // TODO: Change the autogenerated stub
        $this->m=new User();
        $this->message=new Message();
    }

Server类中的代码对 Socket 的各个事件都有其对应的回调方法

['onWorkerStart', 'onConnect', 'onMessage', 'onClose', 'onError', 'onBufferFull', 'onBufferDrain', 'onWorkerStop', 'onWorkerReload']

我仅使用了

  1. onMessage 即收到信息
  2. onClose 断开连接的回调

说说思路:

因为服务端都是通过onMessage来服务客户端,因此可以约定一个type参数用来表示当前操作类型,例如,type=”join” 意为首次建立连接并加入会话。需要记录当前的用户信息和连接对象( c o n n e c t i o n ) , 即 上 述 的 【 connection),即上述的【 connection)ciients】建议用数组存储,键可以使用当前客户端的ip【此种方式会使得当前ip只能有一个用户】或者 ip+用户id以及其他唯一标识。 因此$clients看起来可能是如下形式

$clients=[
	"127.0.0.1&12"=>{userInfo=>"当前链接的用户信息",key=>"127.0.0.1&12",...clients对象的其他属性}
]
 /**
     * 收到信息
     * @param $connection
     * @param $data Object 带过来的参数
     */
    public function onMessage($connection, $data)
    {
        $data=json_decode($data);

        if(empty($data->uid)) {
            $connection->send("未携带uid");
        }else{
            //拼接请求的key
            $data->key=$connection->getRemoteIp()."&".$data->uid;

            //如果不存在于记录的连接数组 就将 添加进去
            if(!array_key_exists($data->key,$this->clients)){

                //查询出当前用户的昵称和头像
                $temp=$this->m->field(["nickname","avatar","id"])->find($data->uid);
                $temp["joinTime"]=date("Y-m-d H:i:s",time());

                //将用户信息和key存在链接对象里
                $connection->userInfo=$temp;
                $connection->key=$data->key;

                //保存当前用户链接的key
                $this->clients[$data->key]=$connection;

                //广播所有链接用户
                $this->broadCast(['type'=>"join",'data'=>$temp,"total"=>sizeof($this->clients)]);

            }else{
                // 如果当前消息意为发送信息
                if(!empty($data->type) && $data->type=="send"){
                    //将消息存储至数据库
                    $this->message->insert([
                        "createtime"=>time(),
                        "text"=>$data->data->text,
                        "images"=>$data->data->images,
                        "uid"=>$connection->userInfo->id
                    ]);
                    //将消息广播至所有用户
                    $this->broadCast([
                        'type'=>"onMessage",
                        'data'=>$data->data,
                        "publisher"=>$connection->userInfo,
                        'publishTime'=>datetime(time())
                    ]);
                }
            }
        }
    }

消息广播即遍历clients数组分别对其发送

 /**
     * 给当前所有的链接用户发送消息
     * @param $data
     * @param $exceptKey array  期望忽略的用户的key列表
     */
    public function broadCast($data, array $exceptKey=[])
    {
        //遍历客户端数组
        foreach ($this->clients as $k=>$v){
            // 如果不是被忽略的对象 就给该用户发送数据
            if(!in_array($k,$exceptKey)){
                $v->send(json_encode($data));
            }
        }
    }

当客户端断开连接,需要关闭当前链接,并告知所有用户

 /**
     * 当连接断开时触发的回调函数
     * @param $connection
     */
    public function onClose($connection)
    {
        $connection->close();
        if(!empty($connection->key)){
           unset($this->clients[$connection->key]);
           $this->broadCast(['type'=>"leave",'data'=>$connection->userInfo,"total"=>sizeof($this->clients)]);
        }
    }

前端部分,将websocket对象放至pinia中了。
就建立连接发送消息,接收消息并渲染

function send(p = {}) {
  //发送信息时携带uid
  p["uid"] = store.uinfo.id
  // p["uid"]=10;
  store.websocket.send(JSON.stringify(p));
}
 
function connection() {
  
  //如果当前链接对象是空或者还没链接
  if (store.websocket == null || store.websocket.readyState != 1) {

    store.websocket = new WebSocket("ws://localhost:2345");

    console.log("进行重连");

    store.websocket.onopen = function (evt) {
      //立即将用户uid发过去 用来告知其他用户 有人加入
      send();
      online.value = true;
    };
    store.websocket.onclose = function (evt) {
      console.log("关闭链接", evt); 
      online.value = false;

    };
    store.websocket.onmessage = function (evt) {
      let data = JSON.parse(evt.data);
 
      messageList.value.push(data);

    };
    store.websocket.onerror = function (evt) {
      console.log("错误", evt);
    };
  } else {
    online.value = true;
  }

}

至此,基本功能就实现了。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值