音乐播放器
Great Wei
 
文章 标签
16

Powered by Gridea | Theme: Fog

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