swoole实现多对多群聊(一)

参考博客,大家可以去看看原文,这里只是根据业务需求做了更改
后端参考
swoole创建多人多房间聊天室一
swoole创建多人多房间聊天室二
swoole创建多人多房间聊天室三
前端参考
微信小程序开发聊天室

搭建环境和域名配置这里就先跳过了,大家可以自行百度
话不多说直接上代码

PHP代码

<?php

define('HOST', '0.0.0.0');
define('PORT', '19501');
define('WORKER_NUM', 10);
define('DISPATCH_MODE', 2);
define('DAEMONIZE', 0);

class WebsocktDemo
{
    public $ws = null;
    public $redis = null;
    public function __construct()
    {

        $this->ws = new Swoole\WebSocket\Server(HOST, PORT);
        $this->redis = $this->redis();
        $this->redis->auth("***********");

        //$this->ws->on("start", [$this, 'onStart']);
        $this->ws->on("open", [$this, 'onOpen']);
        $this->ws->on('message',[$this,'onMessage']);
        $this->ws->on('close',[$this,'onClose']);
        $this->ws->start();
    }
	/**
     * 获取redis实例
     * @return null|Redis
     */
    protected function redis()
    {
        static $redis = null;
        if (is_null($redis)) {
            $redis = new \Redis();
            $redis->connect("*********");
        }

        return $redis;
    }
    
    /**
     * 监听用户链接事件,链接时需要带用户id与房间id参数,再把用户存到房间域中
     */
    public function onOpen($server, $request){
        //加入房间域
        $this->redis->hset($request->get['room'], $request->get['uid'], $request->fd);
        //加入组集合
        $this->redis->sadd('group', $request->get['room']);
    }
    
    /**
     * 监听接收事件的回调
     */
    public function onMessage($server, $frame)
    {
        //在接收数据的时候进行推送,每次发送消息都要带上标记信息(uid,room)
        $data = json_decode($frame->data, true);
        //组装数据判断类型
        switch ($data['type']){
            case "change"://发送消息
                $arr['name'] = $data['name'];
                $arr['content'] = $data['content'];
                $arr['type'] = "change";
                $arr['uid'] = $data['uid'];
                $arr['room'] = $data['room'];
                break;
        }
        //推送消息到房间,
        self::push_room($data['room'], $arr);
    }
    
	/**
     * 监听关闭事件的回调,这一步照搬,没有做修改
     */
    public function onClose($ser, $fd)
    {
        //退出并删除多余的分组fd
        $group = $this->redis->sMembers('group');
        foreach ($group as $v) {
            $fangjian = $this->redis->hgetall($v);
            foreach ($fangjian as $k => $vv) {
                if ($fd == $vv) {
                    $this->redis->hdel($v, $k);
                }
            }
        }
    }
  }
  //最后实例化对象
  new WebsocktDemo();
    

后端的代码主要是用到Swoole的WebSocket 服务和redis hash域的概念,刚开始做的时候并没有想到使用redis,而是直接去链接数据实现的,后来参考了别人的文章,豁然开朗,换成了redis(手动滑稽),顺便了解了一下redis的hsah

小程序 websocket.js 代码

// 请求域名
var url = 'wss://你的wss域名';
// 房间信息
var room = ''
// 用户签名
var sign = 0
// 用户名
var name = null;
// 收到消息回调函数
var func = null;

function connect(room,sign,name,func) {
  //把链接信息全局化的目的为断开后实现重新链接,*如果前端通过超过一分钟没有与socket交互就会自动断开
  let that = this
  room = room
  sign = sign
  name = name
  func = func
  wx.connectSocket({
    url: url + "?room="+room+"&uid="+sign+"&name="+name,
    method: 'post',
    header:{'content-type': 'application/json'},
    success: function (res) {
      console.log('连接成功',res)
    },
    fail: function (res) {
      console.log('连接失败',res)
      wx.closeSocket()
    }
  })
  wx.onSocketOpen(function (res) {
     //接受服务器消息
     wx.onSocketMessage(func);//func回调可以拿到服务器返回的数据
  });
  wx.onSocketClose(function (res) {
    console.log("链接失败",res);
    if(res.reason == "abnormal closure" && res.code == 1006){//实现重连
      that.connect(room,sign,name,func);
    }
  });
}
//发送消息
function send(msg,callbackll) {
  wx.sendSocketMessage({
    data: msg,
    complete(res){
      callbackll(res);
    }
  });
}
module.exports = {
  connect: connect,
  send: send
}

小程序 js 代码

import websocket from "../utils/websocket.js";
//页面加载就链接soket,监听服务器返回消息
onLoad: function (options) {
	websocket.connect(options.id, wx.getStorageSync("sign"), nickname, function (res) {
		//监听接受到数据回调时间
		let server = JSON.parse(res.data)
		that.setData({
			bullet_screen: that.data.bullet_screen.concat(server)
		})
	})
}
// 发送到socket
send: function (e) {
  let that = this
  if (that.data.msgContent == '') {
    wx.showToast({
      title: '请输入您想说的话',
      icon: 'none',
      duration: 2000
    })
    return false
  }
  websocket.send('{"name":"' + wx.getStorageSync("nickname") + '","uid":"' + wx.getStorageSync("sign") + '","type":"change","room": "' + that.data.roomid + '","content": "' + that.data.msgContent + '"}', function (call) {
    if (call.errMsg == "sendSocketMessage:fail WebSocket is not connected") {
      wx.showToast({
        title: '发送失败',
        icon: "none",
        duration: 2000
      })
      return false;
    }
    that.setData({
      msgContent: ''
    })
  })
}

前端主要是使用微信小程序的websocket链接API,可以把整个websocket封装成一个工具类。
js的话只有两段核心代码,一个是前端链接socket后拿到返回消息的处理,其次就是发送消息到socket,整体前后端代码核心部分就只有这么多了,跟其他博客大同小异,主要是看大家的业务需求和实现方式的不同。
前端html这里就不一一展示了,主要就是循环服务器返回的数组,再根据不同的type值添加css样式,就可以实现一些特殊的样式需求了

上一篇:Swoole.001.手撸网络服务器模型


下一篇:安装swoole扩展之一——win下用cygwin