swoole学习笔记
swoole流程图
学习当中的问题和疑问收集
最重要的一个问题:
假设该用户正在线上, 而swoole\websocket\server进程被强制关闭, 存储在redis中的用户iD连接池中的用户ID来不及清除(原因是:程序的机制是socket断开连接后, on close的回掉中才会删除该用户在redis中的ID), 而被保留下来. 等到程序再次运行的时候, 使用On message进行发送消息时, 遍历redis中的用户ID, 但其实有很多用户ID是之前运行的程序遗留下来的用户ID. 这时候就会报错Notice;
我想到的解决办法是,
1. swoole 一直检测redis中某一个Key存在的话, 就删除userid同时close掉进程.如果想要停掉该进程则,直接在redis中写入该key.
2. 利用进程间的通信,用另一个进程调用swoole的close方法, 来关闭进程.
使用如下方式进行的swoole下的socket聊天室,功能;
1. 据称,单个进程有最大连接限制, 一旦超过连接限制, 会造成进程的自动回收, 即close, 关闭该进程, 这个该怎么解决.
2. 研究一下,swoole的多进程, 进程间的关系.
3. 接收图片和接受消息时header中的关系, 以及如何判断用户发送的是图片信息还是文字信息,如何处理用户发送的视频音频和图片
swooleWebsocket不能通过访问页面的形式开启, 只能通过cli的形式激活.
简易代码,利用redis作为连接池,群发消息
<?PHP
$ip = '127.0.0.1';
$port = '9999';
# 连接本地的服务
$redis = new Redis();
$redis->connect('127.0.0.1', 6379);
/**
* 我的代码
*/
$serv = new Swoole\Websocket\Server($ip, $port);
# 连接时触发的事件
$serv->on('Open', function($server, $req) use($redis) {
echo "成功连接connection open: ".$req->fd. "\n";
# 用户组的连接池
$redis->hset('Users', $req->fd, $req->fd);
});
# 收到消息触发的事件
$serv->on('Message', function($server, $frame) use($redis){
echo "用户message: ".$frame->data. "\n";
# 获取用户列表
$users = $redis->hgetall('Users');
# 循环发送数据
foreach ($users as $k1=>$v1){
# 判断该用户是否还在线
if (!$server->exist($v1)) {
echo $v1.": 用户退出This user out\n";
$redis->hDel('Users', $v1);
continue;
}
$server->push($v1, json_encode(['name' => $frame->fd, 'content' => strip_tags($frame->data)]));
}
});
# 关闭连接时触发的事件
$serv->on('Close', function($server, $fd) use($redis){
# 断开连接时清除该用户key, 不在为该用户发送消息
$redis->hdel('Users', $fd);
echo "连接关闭connection close: ".$fd. "\n";
});
# 开始运行
$serv->start();
/**
* 该程序问题收集: 当client和SERVER正在通信时,突然停掉进程, 会导致,正在通信的clientId没有被close掉,clientId依然存储在redis中未被删除.
* 下次启动进程,在on(Message)发送数据时, 该用户的ID依然被遍历发送, 导致Notice错误, 因为此时该用户实际上并未连接, 该ID已经失效.
* 应该想一个办法, 当进程被关闭时, 清空redis中hash表上User的所有数据, 销毁所有的ID, 不知道封装成一个类, 使用 __destruct魔术方法好不好使.
*/
- On Open函数中$req内容
class Swoole\Http\Request#8 (3) {
public $fd =>
int(1)
public $header =>
array(12) {
'host' =>
string(14) "127.0.0.1:9999"
'connection' =>
string(7) "Upgrade"
'pragma' =>
string(8) "no-cache"
'cache-control' =>
string(8) "no-cache"
'upgrade' =>
string(9) "websocket"
'origin' =>
string(20) "http://test.demo.com"
'sec-websocket-version' =>
string(2) "13"
'user-agent' =>
string(120) "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/57.0.2987.98 Safari/537.36"
'accept-encoding' =>
string(23) "gzip, deflate, sdch, br"
'accept-language' =>
string(35) "zh-CN,zh;q=0.8,en-US;q=0.6,en;q=0.4"
'sec-websocket-key' =>
string(24) "8uuhzMSyitKPFYcvb33XVA=="
'sec-websocket-extensions' =>
string(42) "permessage-deflate; client_max_window_bits"
}
public $server =>
array(10) {
'request_method' =>
string(3) "GET"
'request_uri' =>
string(1) "/"
'path_info' =>
string(1) "/"
'request_time' =>
int(1492708365)
'request_time_float' =>
double(1492708365.3182)
'server_port' =>
int(9999)
'remote_port' =>
int(55495)
'remote_addr' =>
string(9) "127.0.0.1"
'server_protocol' =>
string(8) "HTTP/1.1"
'server_software' =>
string(18) "swoole-http-server"
}
}
- On Message中的内容
class Swoole\WebSocket\Frame#10 (4) {
public $fd =>
int(1)
public $finish =>
bool(true)
public $opcode =>
int(1)
public $data =>
string(4) "asdf"
}
- 判断用户是否登录
因为TCP/IP模式下, 无法向客户端发送cookie, 所以无法使用session判断用户是否登录.
目前我想到的是, 首先将session存储在redis中, Session存储在redis中是以string形式存储的格式Key以PHPREDIS_SESSION:用户sessionID.
当用户发送连接请求时将用户cookie中的PHPSESSID同时放在消息中发送到SERVER,
Swoole\Websocket\Server通过对用户发送到服务器的消息内容进行处理, 获取到用户PHPSESSID,
使用该PHPSESSID从redis数据库中查询是否存在, 通过该手段进行判断, 用户是否是登录状态.
- 发送图片时,怎样通过header判断这是一张图片
暂时未从header头中看到相关信息
仅仅实在On Message中的内容中的$data看到对变量的描述为string