'0.0.0.0', // 监听地址
'port' => 9501, // 监听端口
'secret_key' => '123456', //允许推送消息的秘钥
'mode' => "SWOOLE_PROCESS", // 运行模式 默认为SWOOLE_PROCESS
'sock_type' => ["SWOOLE_SOCK_TCP", "SWOOLE_SSL"], //数组 sock type 默认为SWOOLE_SOCK_TCP | SWOOLE_SSL
'isInsertMysql' => true, //发送消息后是否写入到mysql数据库
// 分布式服务器通道 //允许哪些ip +秘钥连接到与本机接收推送消息
"distributed" => [
'local' => [
// 本机配置
"host" => '27.1.0.1', //公网ip
"ip" => '192.168.0.150', //本机ip局域组网,分布式接口ip
"secret_key" => "123456",
"port" => 9501,
],
// 搭档服务器配置
"websocket_group" => [
[
"ip" => "192.168.0.150", //ip组网,分布式连接ip
"secret_key" => "123456", //组网,分布式连接秘钥
"port" => 9502, //服务端开启服务的端口号
], [
"ip" => "192.168.0.150",
"secret_key" => "123456",
"port" => 9502, //
],
],
],
'set' => [
'dispatch_mode' => 2, //绑定uid=5
'daemonize' => false, //守护进程化。
'heartbeat_check_interval' => 10, //间隔多少秒进行心跳检测
'heartbeat_idle_time' => 60, //间隔多少秒无状态 踢下线
'worker_num' => 1, //开启进程数量
'insert_db_turns_time' => 5, //轮流读取redis缓存写入数据库。间隔秒,一般对数据库实时要求并不高,时间可以设置在5-10秒,读取redis缓存消息堆,批量写入数据库,减少数据库操作频率。提升写入性能
'insert_db_number' => 1000, //间隔秒,读取redis缓存消息堆数量,批量写入数据库,如果单台redis主机建议每秒吞吐量2000以内。
],
];
// 自动注册属性 防止连接丢失
public function __get($name) {
switch ($name) {
// case 'Arr':
// // 连接Arr工具类
// return $this->$name = new Arr;
// break;
default:
# code...
break;
}
}
public function __set($name, $vue) {
$this->$name = $vue;
}
public function __construct($config = []) {
if (!empty($config) && is_array($config)) {
$websocketConfig = array_merge($this->websocketConfig, $config);
} else {
$websocketConfig = array_merge($this->websocketConfig, App::config('websocket'));
}
$isSsl = false;
if (isset($websocketConfig['set']['ssl_cert_file']) && isset($websocketConfig['set']['ssl_key_file']) && !empty($websocketConfig['set']['ssl_cert_file'])) {
$websocketConfig['set']['ssl_cert_file'] = App::getRootPath() . '/ssl/' . $websocketConfig['set']['ssl_cert_file'];
$websocketConfig['set']['ssl_key_file'] = App::getRootPath() . '/ssl/' . $websocketConfig['set']['ssl_key_file'];
$isSsl = true;
}
echo "serve\websocket\chat.php isSsl:$isSsl:\n";
// ws服务
// print_r($websocketConfig);
if (isset($websocketConfig['sock_type'][1]) && $isSsl) {
$server = new Server($websocketConfig['ip'], $websocketConfig['port'], constant($websocketConfig['mode']), constant($websocketConfig['sock_type'][0]) | constant($websocketConfig['sock_type'][1]));
echo "开启wss服务\n";
} else {
$server = new Server($websocketConfig['ip'], $websocketConfig['port'], constant($websocketConfig['mode']), constant($websocketConfig['sock_type'][0]));
}
// $this->distributed = $this->websocketConfig['distributed'];
$local = $websocketConfig['distributed']['local']; //本机ip配置
$websocket_group = $websocketConfig['distributed']['websocket_group'] ?? null; //联机ip配置
// $websocketConfig['set']['dispatch_func'] = function ($serv, $fd, $type, $data) use ($websocket_group) {
// // echo $worker_num = $serv->setting['worker_num'] . "\n";
// // $type数据的类型,0表示来自客户端的数据发送,4表示客户端连接关闭,5表示客户端连接建立
// if ($worker_num > 1 && !empty($websocket_group)) {
// $clientInfo = $serv->getClientInfo($fd);
// // $clientInfo['remote_ip'];
// foreach ($websocket_group as $key => $value) {
// if ($value['ip'] === $clientInfo['remote_ip']) {
// return 0;
// }
// }
// $worker = ($fd % ($worker_num - 1)) + 1;
// return $worker;
// } else {
// return 0;
// }
// return intval($data[0]);
// };
// $this->websocketConfig = $websocketConfig;
// $websocketConfig['set']['config'] = $this->websocketConfig;
$server->websocketConfig = $websocketConfig;
// print_r($websocket_group);
// return;
//配置参数
$server->set($websocketConfig['set']);
// $onManagerStart = new \serve\websocket\managerstart\Message($server); //onMeanagerStert 绑定
// 当管理进程启动时调用它
$server->on('ManagerStart', [$this, 'onManagerStart']);
// $server->on('ManagerStart', [$onManagerStart, 'onManagerStart']);
// $server->set(array("open_websocket_close_frame" => true));
//启动每个worker进程会触发
$server->on('WorkerStart', [$this, 'onWorkerStart']);
//建立连接触发
// $server->on('open', [$this, 'onOpen']);
$onOpen = new \serve\websocket\open\Message($server); //onOpen 绑定类
$onMessage = new \serve\websocket\message\Message($server); //onMessage绑定
// $message->config = $websocketConfig;
$onTask = new \serve\websocket\task\Message($server); //onTask绑定
//接收到消息
// $server->on('message', [$message, 'onMessage']);
// $server->on('task', [$this, 'onTask']); //接收到消息
// $server->on('finish', [$this, 'onFinish']); //接收到消息
// $server->on('receive', [$this, 'onReceive']); //接收到消息
//关闭连接触发
$server->on('close', [$this, 'onClose']);
//创建Server对象,监听 127.0.0.1:9502端口,类型为SWOOLE_SOCK_UDP
$udp_server = $server->listen('192.168.0.150', 9502, SWOOLE_SOCK_UDP);
// udp服务器组
$udpsocket_group = $websocketConfig['distributed']['udpsocket_group'] ?? null;
//监听数据接收事件
$udp_server->on('packet', function ($serv, $data, $clientInfo) use ($server, $udpsocket_group) {
// print_r();
$isAuth = false;
if ($clientInfo['address'] === '127.0.0.1') {
$isAuth = true;
} else {
$data = json_decode($data, true);
foreach ($udpsocket_group as $key => $value) {
if ($value['ip'] === $clientInfo['address'] && $value['secret_key'] === $data['sk']) {
$isAuth = true;
break;
}
}
}
if ($isAuth === true) {
$data = [
'message' => 'ok',
'code' => 200,
'data' => $data,
];
} else {
$data = [
'message' => '失败',
'code' => 400,
'data' => $data,
];
}
$data = json_encode($data, JSON_UNESCAPED_UNICODE);
$serv->sendto($clientInfo['address'], $clientInfo['port'], "Server " . $data);
// var_dump($clientInfo);
});
//......设置各个回调......
//多监听一个tcp端口,对外开启tcp服务,并设置tcp服务器的回调
$tcp_server = $server->listen('0.0.0.0', 9503, SWOOLE_SOCK_TCP);
//需要调用 set 方法覆盖主服务器的设置
$tcp_server->set([
// "ssl_cert_file" => App::getRootPath() . '/ssl/' . $websocketConfig['set']['ssl_cert_file'],
// "ssl_key_file" => App::getRootPath() . '/ssl/' . $websocketConfig['set']['ssl_key_file'],
// 'open_mqtt_protocol' => true,
'open_http_protocol' => false, // 设置这个端口关闭http协议功能
]);
// $udp_server = $server->listen('0.0.0.0', 9502, SWOOLE_SOCK_UDP);
// //默认新监听的端口 9999 会继承主服务器的设置,也是 Http 协议
// //需要调用 set 方法覆盖主服务器的设置
// $udp_server->set([
// 'open_http_protocol' => false, // 设置这个端口关闭http协议功能
// ]);
// $udp_server->on("receive", function ($serv, $fd, $threadId, $data) use ($server) {
// print_r($data);
// // $serv->push($fd, $data);
// });
$tcp_server->on("message", function ($serv, $data) use ($server) {
echo "tcp_server";
echo $data;
// $serv->push($fd, $data);
});
$tcp_server->on('connect', function ($serv, $fd) {
echo "Client:Connect.\n";
$serv->send($fd, '{"fd":"' . $fd . '"}' . "\n");
});
$tcp_server->on('receive', function ($serv, $fd, $from_id, $data) {
$clientInfo = $serv->getClientInfo($fd);
// print_r($clientInfo);
$distributed = $this->websocketConfig['distributed'];
$source = $this->websocketConfig['source'];
$socket_group = $distributed['socket_group'];
// 验证ip+fd是否合法
if (isset($GLOBALS['client'][$fd]) && $GLOBALS['client'][$fd]['isServer'] == true) {
echo "验证通过$fd\n";
print_r($data);
$data = json_decode($data, true);
$serv->send($fd, json_encode($data, JSON_UNESCAPED_UNICODE) . "\n");
# code...
} else {
// 验证服务器是否合法
// if ($clientInfo['remote_ip'] == $addrIp) {
$data = json_decode($data, true);
// if (isset($data['sk']) && $data['sk'] == $sk) {
// $serv->$addrIp = $fd;
// 分布服务器认证授权
echo "\n组网服务器正则进行连接验证fd:{$fd} ip:{$clientInfo['remote_ip']} sk:{$data['sk']}\n";
// $distributed = $this->distributed;
// $websocket_group = $server->websocketConfig['websocket_group'];
if (isset($data['sk']) && !empty($socket_group) && is_array($socket_group)) {
$ip = $clientInfo['remote_ip']; //获取客户端ip
foreach ($socket_group as $key => $value) {
if ($value['ip'] === $ip && $value['secret_key'] === $data['sk']) {
echo "\n组网服务器已连接fd:{$fd} ip:{$ip} sk:{$data['sk']}\n";
// ip +密码验证通过 创建授权服务器的联网许可证明,保存到redis缓存服务器
// $fdinfo = $server->getClientInfo($request->fd);
// $ip = $fdinfo['remote_ip'];
$uid = intval(ip2long($distributed['local']['ip']) . $distributed['local']['port'] . ip2long($ip));
// print_r($this->distributed['local']);
$ClientInfo = [
'fd' => $fd,
// 'uid' => $uid,
"port" => $distributed['local']['port'],
'ip' => $distributed['local']['ip'],
"time" => time(),
];
$data['source'] = 'pc';
// $server->bind($request->fd, 12355464110647986);
// $fdinfo = $server->getClientInfo($request->fd);
$fdInfo = [
'uid' => $uid,
'isServer' => true,
"source" => $data['source'],
'time' => time(),
];
$GLOBALS['client'][$fd] = $fdInfo;
//uid缓存到本机缓存池, key=本机ip
$oldClient = $this->redis()->hGet("ws_clientInfo", $uid);
is_array($oldClient) or $oldClient = [];
$oldClient[$data['source']] = $ClientInfo;
$this->redis()->hSet("ws_clientInfo", $uid, $oldClient);
//———————————————————————————————验证通过 存入redis——fdInfo=hGet(ws_fdInfo.local->ip,fd)————clinetInfo= hGet(ws_clientInfo,uid)————————————————————————————————————
$returnMsg = [
'message' => '授权成功',
'code' => 200,
'data' => [],
];
// $server->push($fd, json_encode($returnMsg));
echo "验证通过$fd\n";
$serv->send($fd, json_encode($returnMsg, JSON_UNESCAPED_UNICODE) . "\n");
return;
}
}
}
$serv->close($fd);
}
});
$tcp_server->on('close', function ($serv, $fd) {
echo "Client: Close.\n";
});
$tcp_server->on('packet', function ($serv, $data, $addr) {
var_dump($data, $addr);
});
// $onRequest = new \serve\websocket\request\Message($server); //onRequest绑定
$server->on('request', function ($request, $response) use ($server) {
print_r('http\n');
$client = new \swoole_client(SWOOLE_SOCK_TCP);
if ($client->connect('127.0.0.1', 9503, 1)) {
$client->send("data");
echo $client->recv();
$client->close();
} else {
echo "connect failed.";
}
$response->end('fdsfdsf');
});
// echo "\n已经启动server->start\n";
$server->start();
// $this->insertDb();
}
public function onManagerStart($server) {
// 开启轮询读取redis缓存数据堆,执行批量写入数据库
// swoole_timer_tick(6000, function ($timer_id) {
// $this->insertDb();
// echo "tick-4000ms\n";
// });
echo "\n管理进程启动server->start,onManagerStart\n";
}
// 启动worker进程触发
public function onWorkerStart($server, $worker_id) {
echo "启动worker_id:$worker_id\n";
// if ($server->worker_id == 0) {
// }
}
public function onFinish($server, $task_id, $data) {
// print_r([$data, $task_id]);
echo "onFinish\n";
// print_r($response);
}
// 断开连接触发
public function onClose($server, $fd) {
echo "\nclient {$fd} closed.\n";
// if ($server->exist($fd)) {
// $server->push($fd, 0);
// }
$distributed = $server->websocketConfig['distributed'];
$fdInfo = $GLOBALS['client'][$fd];
if (is_array($fdInfo) && isset($fdInfo['uid'])) {
$clientInfo = $this->redis()->hGet("ws_clientInfo", $fdInfo['uid']);
echo "删除前\n";
print_r($fdInfo);
print_r($clientInfo);
if (!empty($clientInfo)) {
$source = $clientInfo['source'] ?? "app";
unset($clientInfo[$source]);
if (empty($clientInfo)) {
// 没有在线的用户 清空缓存
$this->redis()->hDel("ws_clientInfo", $fdInfo['uid']);
} else {
// 存在其他设备在线用户,只删除当前下线的用户缓存
$this->redis()->hSet("ws_clientInfo", $fdInfo['uid'], $clientInfo);
}
}
// $this->redis()->hDel("ws_fdInfo:" . $distributed['local']['ip'] . $distributed['local']['port'], $fd);
}
unset($GLOBALS['client'][$fd]);
echo "删除后\n";
print_r($this->redis()->hGet("ws_fdInfo:" . $distributed['local']['ip'] . $distributed['local']['port'], $fd));
print_r($this->redis()->hGet("ws_clientInfo", $fdInfo['uid']));
}
public function start() {
$this->server->start();
}
}