swoole+redis实现简单聊天室
文章分类:
后端
5年前
1385
0
实现一对一和一对多即时通讯,能查看聊天记录以及离线留言,新消息提醒。
Redis 实现每个连接websocket的服务都唯一绑定一个用户。通过 用户账号 = websocket fd 存到redis中。
将离线消息存在 Redis 队列里,由于是简单实现效果,没有使用数据库存数据,聊天记录暂时放在Local Storage里面,代码如下:
服务端:
<?php
use Redis;
class WebSocket extends Base
{
#redis key
protected static $redisKey = 'swoole_fd';
protected static $historyKey = 'swoole_history:';
# 过期时间
protected static $historyKeyExpire = 3600*24;
# redis
protected static $redis;
public function __construct()
{
# 初始化redis
self::$redis = new Redis();
self::$redis->connect('120.0.0.1',6379);
self::$redis->del(self::$redisKey);
}
public function handle()
{
try {
# 开启swoole
$ws_server = new \swoole_websocket_server('0.0.0.0', 8888);
$ws_server->set(array(
'worker_num' => 8,
'daemonize' => false,
'max_request' => 10000,
'dispatch_mode' => 2,
'debug_mode' => 1
));
# 监听WebSocket连接打开事件
$ws_server->on('Open', array($this, 'onOpen'));
# 监听WebSocket消息事件,onMessage回调必须被设置,未设置服务器将无法启动
$ws_server->on('Message', array($this, 'onMessage'));
# 监听WebSocket连接关闭事件
$ws_server->on('Close', array($this, 'onClose'));
$ws_server->start();
} catch (\Exception $e) {
}
}
public function onOpen($server, $req)
{
echo '已连接'.$req->fd . PHP_EOL;
}
public function onMessage($server, $frame)
{
$data = json_decode($frame->data,true);
if(isset($data['type']) && $data['type'] == 'init')
{
# 绑定fd
self::$redis->hset(self::$redisKey,$data['id'], $frame->fd);
# 获取离线的消息
$historyList = self::$redis->lrange(self::$historyKey.$data['id'],0,-1);
if($historyList){
self::$redis->ltrim(self::$historyKey.$data['id'],count($historyList),-1);//删除取出的数据
$out['type'] = 'list';
$out['data'] = $historyList;
$server->push($frame->fd, json_encode($out,256));
}
}elseif($data['to']['type'] == 'friend')
{
# 私聊
$fd = self::$redis->hget(self::$redisKey,$data['to']['id']);
if($fd){
$server->push($fd,$frame->data);
}else{
# 添加消息队列
self::$redis->rpush(self::$historyKey.$data['to']['id'],$frame->data);
self::$redis->expire(self::$historyKey.$data['to']['id'],self::$historyKeyExpire);
}
}elseif($data['to']['type'] == 'group')
{
# 群聊
$fds = self::$redis->hgetall(self::$redisKey);
foreach ($fds as $k => $fd){
if($data['mine']['id'] != $k){
$server->push($fd,$frame->data);
}
}
}
}
public function onClose($server, $fd)
{
echo "关闭: " . $fd . PHP_EOL;
$fds = self::$redis->hgetall(self::$redisKey);
$fd = array_search($fd, $fds);
self::$redis->hDel(self::$redisKey,$fd);
self::out($fd . PHP_EOL);
}
}
客户端:
<script>
var webSocket = new WebSocket("ws://0.0.0.0:8888");
webSocket.onopen = function (event) {
console.log('onopen');
//getLocal 生成随机数作为身份标识,存入Local Storage
var uidRst = getLocal('身份标识');
if(uidRst){
var e = {
'type':'init',
'id':uidRst,
};
webSocket.send(JSON.stringify(e));
return;
}
alert('uid 获取失败');
setTimeout(function() {
location.reload();
}, 1000);
},
webSocket.onmessage = function (event) {
var rst = JSON.parse(event.data);
if(rst && rst.code === -1){
// 失败 或者 登陆超时
alert(rst.msg);return;
}else if(rst && rst.type == 'list'){
// 离线消息
var list = rst.data;
$(list).each(function(i,j){
var e = JSON.parse(j);
e = formaMsg(e);
t.getMessage(e);
})
}else if(rst && rst.to && rst.to.type){
var e = formaMsg(rst);
t.getMessage(e);
}
},
formaMsg = function(rst){
var e = {};
if(rst.to.type == 'friend'){
// 私聊
e = {
username: rst.mine.username,
avatar: rst.mine.avatar,
id: rst.mine.id,
type: rst.to.type,
content: rst.mine.content
};
}else if(rst.to.type == 'group'){
// 群聊
e = {
username: rst.mine.username,
avatar: rst.mine.avatar,
id: rst.to.id,
type: rst.to.type,
content: rst.mine.content
};
}
return e;
}
<script>
演示地址:
注意: 同一台电脑上聊天体验,需要打开不同的浏览器进行通讯,因为同一浏览器只生成一个身份标识。
http://www.itselfstudy.cn/me/test
mac 安装
1:下载swoole源码,https://github.com/swoole/swoole-src/releases
2:tar -zxvf swoole.tgz
3:进入解压目录,输入:/usr/local/php/bin/phpize
4:./confiure —with-php-config=/usr/local/php/bin/php-config
5:make && make install
6:修改php.ini,extension=swoole.so