参考博客,大家可以去看看原文,这里只是根据业务需求做了更改
后端参考
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样式,就可以实现一些特殊的样式需求了