在上一个例子中,实现了多房间的多人聊天,这次这个聊天室有所改变,需要做到类似微信或QQ那样(app,pc同时使用),多个端口登入,保证数据的同时传递,考虑了很久,最好的办法就是在之前代码的基础上,再加一层redis的关系,结构脑图如下:
简单点说也就是group->房间room_id->uid->fd,因为全是键值对的关系,所以我这里为了好区分全部用的是redis集合;
代码如下:
<?php
namespace app\common;
require_once 'Predis.php';
require_once 'Task.php';
/**
* socket面向对象的编译
*/
class Http
{
CONST HOST='0.0.0.0';
CONST PORT='9501';
public $ws=null;
public $http_server=null;
private $key='paiv3@$))(';
private $prefix='paiv3_';
public function __construct()
{
$this->ws=new \swoole_websocket_server(self::HOST,self::PORT);
$this->ws->set([
//心跳检测
// 'heartbeat_check_interval' => 5,
// 'heartbeat_idle_time' => 10,
]);
//监听新端口
$this->http_server=$this->ws->listen("0.0.0.0", 9502, SWOOLE_SOCK_TCP);
//开启http模式
$this->http_server->set([
'open_http_protocol' => true,
]);
$this->ws->on("start", [$this, 'onStart']);
$this->http_server->on("request", [$this, 'onRequest']);
$this->ws->on('message',[$this,'onmessage']);
$this->ws->on('close',[$this,'onclose']);
$this->http_server->on('close',[$this,'oncloses']);
$this->ws->start();
}
//监听数据接收事件
public function onRequest($request, $response)
{
//获取全部的参数
$da=$request->post;
//转译utf8
foreach ($da as $k => $v) {
$data[$k]=urldecode($v);
}
//判断权限
if ($data['key'] != $this->key) {
$response->end(json_encode(['code'=>400,'message'=>"您暂无权限访问接口",'data'=>null]));
}else{
//判断类型
if ($data['type'] == 'start') {
//开始拍卖
//记录最新价格
Predis::getInstance()->hset('new_price',$data['room_id'],$data['new_price']);
//组装数据
$arr['number']=Predis::getInstance()->scard('room_id'.$data['room_id']);
$arr['type']="start";
self::push_room($data['room_id'],$arr);
$response->end(json_encode(['code'=>200,'message'=>"推送成功",'data'=>null]));
}elseif ($data['type'] == 'change') {
//出价成功
//组装数据
$arr['name']=$data['name'];
$arr['price']=$data['price'];
$arr['type']="change";
//记录最新出价
Predis::getInstance()->hset('new_price',$data['room_id'],$data['price']);
self::push_room($data['room_id'],$arr);
$response->end(json_encode(['code'=>200,'message'=>"推送成功",'data'=>null]));
}elseif ($data['type'] == 'stop') {
//暂停拍卖
$arr['msg']=$data['msg'];
$arr['type']="stop";
self::push_room($data['room_id'],$arr);
$response->end(json_encode(['code'=>200,'message'=>"推送成功",'data'=>null]));
}elseif ($data['type'] == 'end') {
//拍卖结束
$arr['name']=$data['name'];
$arr['price']=$data['price'];
$arr['type']="end";
self::push_room($data['room_id'],$arr);
//删除对应的房间缓存
//获取该房间下的用户
// 1) "paiv3_room_id1"
// 2) "paiv3_fd1"
// 3) "paiv3_room_id1_12"
// 3) "paiv3_room_id1_13"
// 3) "paiv3_room_id1_14"
// 4) "paiv3_group"
$push_arr=Predis::getInstance()->smembers('room_id'.$data['room_id']);
//获取该房间下用户的fd
$fds=[];
foreach ($push_arr as $v) {
$fds=Predis::getInstance()->smembers(substr($v,6)); //smembers自动写入,所以要截取掉
foreach ($fds as $vv) {
Predis::getInstance()->del(substr($vv,6)); //删除在线的fd字符
}
Predis::getInstance()->del(substr($v,6)); //删除在线的用户
}
Predis::getInstance()->del('room_id'.$data['room_id']); //对应的价格
Predis::getInstance()->hdel('new_price',$data['room_id']); //对应的价格
$response->end(json_encode(['code'=>200,'message'=>"推送成功",'data'=>null]));
}
}
}
/**
* $room_id 当前房间id
* $arr 组装数据
*/
public function push_room($room_id,$arr)
{
//获取该房间下的用户
$push_arr=Predis::getInstance()->smembers('room_id'.$room_id);
//获取该房间下用户的fd
$fds=[];
foreach ($push_arr as $k => $v) {
$fds=Predis::getInstance()->smembers(substr($v,6)); //smembers自动写入,所以要截取掉
foreach ($fds as $vv) {
//推送
$this->ws->push(substr($vv,8), json_encode($arr));
}
}
}
/**
* 设置进程名,为后续平滑重启进程
* @param $server
*/
public function onStart($server) {
swoole_set_process_name("live_master");
}
/**
监听接收事件的回调
*/
public function onmessage($server, $frame)
{
//在接收数据的时候进行推送
$data=json_decode($frame->data,true);
//加入组集合(集合)
Predis::getInstance()->sadd('group',$data['room_id']);
//加入分组(集合)
Predis::getInstance()->sadd('room_id'.$data['room_id'],'room_id'.$data['room_id'].'_'.$data['uid']);
//加入会员fd的集合(集合)
Predis::getInstance()->sadd('room_id'.$data['room_id'].'_'.$data['uid'],'fd'.$frame->fd);
//创建fd的json数据(字符)
$json['room_id']=$data['room_id'];
$json['uid']=$data['uid'];
Predis::getInstance()->set('fd'.$frame->fd,json_encode($json));
//组装发送的数据
$arr['name']=$data['name'];
$arr['new_price']=Predis::getInstance()->hget('new_price',$data['room_id']);
$arr['type']="join";
$arr['number']=Predis::getInstance()->scard('room_id'.$data['room_id']);
//推送
self::push_room($data['room_id'],$arr);
}
/**
监听关闭事件的回调
*/
public function onclose($ser, $fd)
{
print_r("你好,我的{$fd}\n");
//先获取字符fd,并得到room_id和uid
$data=json_decode(Predis::getInstance()->get('fd'.$fd),true);
//删除用户该次的fd
Predis::getInstance()->srem('room_id'.$data['room_id'].'_'.$data['uid'],'fd'.$fd);
//删除字段
Predis::getInstance()->del('fd'.$fd);
}
public function oncloses($ser, $fd)
{
print_r("这个是http_server{$fd}\n");
}
}
new Http();