參考博客,大家可以去看看原文,這裏只是根據業務需求做了更改
後端參考
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樣式,就可以實現一些特殊的樣式需求了