Swoole实现IM
0X01
swoole_table 共享内存表
在swoole_server->start()之前创建swoole_table对象。并存入全局变量或者类静态变量/对象属性中。
在worker/task进程中获取table对象,并使用
注意点:只有在swoole_server->start()之前创建的table对象才能在子进程中使用
swoole_table构造方法中指定了最大容量,一旦超过此数据容量将无法分配内存导致set操作失败。所以使用swoole_table之前一定要规划好数据容量,实现进程之间的数据共享。
$table = new swoole_table(1024);
$table->column('fd', swoole_table::TYPE_INT);
$table->column('from_id', swoole_table::TYPE_INT);
$table->column('data', swoole_table::TYPE_STRING, 64);
$table->create();
$serv = new swoole_server('127.0.0.1', 9501);
//将table保存在serv对象上
$serv->table = $table;
$serv->on('receive', function ($serv, $fd, $from_id, $data) {
$ret = $serv->table->set($fd, array('from_id' => $data, 'fd' => $fd, 'data' => $data));
});
$serv->start();
0X02
swoole_websocket_server
WebSocket服务器是建立在Http服务器之上的长连接服务器,客户端首先会发送一个Http的请求与服务器进行握手。握手成功后会触发onOpen事件,表示连接已就绪,onOpen函数中可以得到$request对象,包含了Http握手的相关信息,如GET参数、Cookie、Http头信息等。
建立连接后客户端与服务器端就可以双向通信了。
//创建websocket服务器对象,监听0.0.0.0:9502端口
$ws = new swoole_websocket_server("0.0.0.0", 9502);
//监听WebSocket连接打开事件
$ws->on('open', function ($ws, $request) {
var_dump($request->fd, $request->get, $request->server);
$ws->push($request->fd, "hello, welcome\n");
});
//监听WebSocket消息事件
$ws->on('message', function ($ws, $frame) {
echo "Message: {$frame->data}\n";
$ws->push($frame->fd, "server: {$frame->data}");
});
//监听WebSocket连接关闭事件
$ws->on('close', function ($ws, $fd) {
echo "client-{$fd} is closed\n";
});
$ws->start();
0X02
swoole_process
swoole增加了多进程管理模块来替代PHP的pcntl,它相比pcntl的不同点是:
swoole_process提供了pcntl没有的进程间通信
swoole_process支持重定向标准输入和输出,在子进程内echo或者读键盘输入可以被重定向为从管道中取数据
子进程可以异步化
$worker_num = 8;
for($i = 0; $i < $worker_num; $i++)
{
$process = new swoole_process('callback_function', $redirect_stdout);
$pid = $process->start();
$workers[$pid] = $process;
}
function callback_function(swoole_process $worker)
{
//echo "Worker: start. PID=".$worker->pid."\n";
//recv data from master
$recv = $worker->read();
echo "From Master: $recv\n";
//send data to master
$worker->write("hello master\n");
sleep(2);
$worker->exit(0);
}
// read/write 2个方法就是向管道内读写数据。主进程内可以通过write/read 向子进程写入,读取数据。
// swoole_process支持了标准输入输出的重定向,子进程内echo时,会自动写入管道,而不是打印到屏幕。
// 子进程异步
function callback_function_async(swoole_process $worker)
{
//echo "Worker: start. PID=".$worker->pid."\n";
//recv data from master
$GLOBALS['worker'] = $worker;
swoole_event_add($worker->pipe, function($pipe) {
$worker = $GLOBALS['worker'];
$recv = $worker->read();
echo "From Master: $recv\n";
//send data to master
$worker->write("hello master\n");
sleep(2);
$worker->exit(0);
});
}
//可以将管道加入到swoole_event中即可实现异步的进程间通信,另外子进程内可以使用swoole_timer/swoole_client/swoole_async这些异步的API。或者使用swoole_event_add直接操作swoole的EventLoop。
0X03 实现IM
创建一个swoole_table共享内存表,用于记录连接的客户端信息
记录:
用户id, 设备id, 设备型号, 登录状态,数据接收池,客户端ip,客户端端口,上一次在线时间。
创建一个websocket_service
启动之前编写一个协议类IM,负责处理业务逻辑。
swoole_process 创建一个进程专门用于处理定时器,websocket_service.addprocess()
加载配置文件
回调函数内需要编写的内容
connect:
客户端连接时触发,将用户信息记录到swoole_table中,并且调用IM协议类中的方法,创建一个定时器,等待用户进行登录操作,swoole_timer_after 指定时间后自动关闭当前用户连接(主动关闭用户连接会触发回调函数close),如果用户在指定时间内登录,清除定时器。
handShake:
服务端握手时触发,有需要可以自己进行处理(可选项)。
message:
服务端接收到数据时触发,判断当前接收到是的数据是否完整(数据传输过程发生了拆包,swoole_frame->finish判断是否完整),如果数据不完整将数据存储到内存表中的当前用户的数据接收池,请求结束。否则将当前用户数据接收池数据获取出来与当前数据进行组和,情况数据接收池,获得到完整数据进行业务逻辑处理。调用IM协议类的方法进行相应处理。
close
主动断开客户端连接时触发,清除swoole_table用户相关数据。
start
websocket服务启动触发,有需要可以自行修改
managerStart
manager进程启动时触发,有需要可以自行修改
workerStart
worker进程启动时触发
shutdown
服务关闭时触发,清除远端用户在线列表信息(redis)。
workerError
work异常触发,释放work资源,调用使用类的析构函数。
workerStop
work停止触发,释放work资源,调用使用类的析构函数。
task
task启动时触发
IM协议类
接收消息
解析接收的消息,判断当前消息是否正常,检查内存表中用户是否存在信息。
每次接收到消息重置心跳定时器。
检查用户是否有在线信息,如果有更新在线时间。
客户端需要定时发送ping包
用户登录
用户登录后清除登录定时器,更新本地在线信息,远端在线信息。
请到客户端“主题--自定义配置--配置”中填入leancloud_appID和key