mirror of
https://github.com/lkeme/BiliHelper-personal.git
synced 2025-12-19 09:30:10 +08:00
265 lines
6.5 KiB
PHP
265 lines
6.5 KiB
PHP
<?php
|
|
|
|
/**
|
|
* Website: https://mudew.com/
|
|
* Author: Lkeme
|
|
* License: The MIT License
|
|
* Email: Useri@live.cn
|
|
* Updated: 2020 ~ 2021
|
|
*/
|
|
|
|
namespace BiliHelper\Plugin;
|
|
|
|
use BiliHelper\Core\Log;
|
|
use BiliHelper\Core\Curl;
|
|
use BiliHelper\Util\TimeLock;
|
|
|
|
use Exception;
|
|
use Socket\Raw\Factory;
|
|
|
|
class AloneTcpClient
|
|
{
|
|
use TimeLock;
|
|
private static $heart_lock = 0;
|
|
private static $client = null;
|
|
private static $server_addr = null;
|
|
private static $server_key = null;
|
|
private static $socket_timeout = 0;
|
|
|
|
/**
|
|
* @use 入口
|
|
*/
|
|
public static function run()
|
|
{
|
|
if (self::getLock() > time() || getenv('USE_ALONE_SERVER') == 'false') {
|
|
return;
|
|
}
|
|
self::init();
|
|
self::heartBeat();
|
|
self::receive();
|
|
}
|
|
|
|
|
|
/**
|
|
* @use 初始化
|
|
*/
|
|
private static function init()
|
|
{
|
|
if (empty(getenv('ALONE_SERVER_ADDR')) || empty(getenv('ALONE_SERVER_KEY'))) {
|
|
exit('推送服务器信息不完整, 请检查配置文件!');
|
|
}
|
|
if (!self::$server_addr || !self::$server_key) {
|
|
self::$server_addr = getenv('ALONE_SERVER_ADDR');
|
|
self::$server_key = getenv('ALONE_SERVER_KEY');
|
|
}
|
|
if (!self::$client) {
|
|
self::openConnect();
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* @use 数据封装
|
|
* @param $value
|
|
* @param $fmt
|
|
* @return string
|
|
*/
|
|
private static function packMsg($value, $fmt = "N")
|
|
{
|
|
$head = pack($fmt, strlen($value));
|
|
$data = $head . $value;
|
|
return $data;
|
|
}
|
|
|
|
/**
|
|
* @use 数据解包
|
|
* @param $value
|
|
* @param string $fmt
|
|
* @return array|false
|
|
*/
|
|
private static function unPackMsg($value, $fmt = "N")
|
|
{
|
|
$data = unpack($fmt, $value);
|
|
return $data[1];
|
|
}
|
|
|
|
/**
|
|
* @use 连接认证
|
|
*/
|
|
private static function handShake()
|
|
{
|
|
self::writer(
|
|
json_encode([
|
|
'code' => 0,
|
|
'type' => 'ask',
|
|
'data' => [
|
|
'key' => self::$server_key,
|
|
]
|
|
])
|
|
);
|
|
}
|
|
|
|
/**
|
|
* @use 心跳
|
|
*/
|
|
private static function heartBeat()
|
|
{
|
|
if (self::$heart_lock <= time()) {
|
|
if (self::writer("")) {
|
|
// 心跳默认35s 调整数据错开错误
|
|
self::$heart_lock = time() + 25;
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @use 读数据
|
|
* @param $length
|
|
* @return array|bool|false
|
|
*/
|
|
private static function reader($length)
|
|
{
|
|
$data = false;
|
|
try {
|
|
while (self::$client->selectRead(self::$socket_timeout)) {
|
|
$data = self::$client->read($length);
|
|
if (!$data) {
|
|
throw new Exception("Connection failure");
|
|
}
|
|
if ($length == 4) $data = self::unPackMsg($data);
|
|
break;
|
|
}
|
|
} catch (Exception $exception) {
|
|
self::reConnect();
|
|
}
|
|
return $data;
|
|
}
|
|
|
|
/**
|
|
* @use 写数据
|
|
* @param $data
|
|
* @return bool
|
|
*/
|
|
private static function writer($data)
|
|
{
|
|
$status = false;
|
|
try {
|
|
while (self::$client->selectWrite(self::$socket_timeout)) {
|
|
$data = self::packMsg($data);
|
|
$status = self::$client->write($data);
|
|
break;
|
|
}
|
|
} catch (Exception $exception) {
|
|
self::reConnect();
|
|
}
|
|
return $status;
|
|
}
|
|
|
|
|
|
/**
|
|
* @use 打开连接
|
|
*/
|
|
private static function openConnect()
|
|
{
|
|
if (!self::$client) {
|
|
try {
|
|
$socket = (new Factory())->createClient(self::$server_addr, 40);
|
|
$socket->setBlocking(false);
|
|
self::$client = $socket;
|
|
self::handShake();
|
|
Log::info("连接到 {$socket->getPeerName()} 推送服务器");
|
|
} catch (Exception $e) {
|
|
Log::error("连接到推送服务器失败, {$e->getMessage()}");
|
|
self::setLock(60);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @use 重新连接
|
|
*/
|
|
private static function reConnect()
|
|
{
|
|
Log::info('重新连接到推送服务器');
|
|
self::closeConnect();
|
|
self::openConnect();
|
|
}
|
|
|
|
/**
|
|
* @use 断开连接
|
|
*/
|
|
private static function closeConnect()
|
|
{
|
|
Log::info('断开推送服务器');
|
|
try {
|
|
self::$client->shutdown();
|
|
self::$client->close();
|
|
} catch (Exception $exception) {
|
|
// var_dump($exception);
|
|
}
|
|
self::$client = null;
|
|
}
|
|
|
|
|
|
/**
|
|
* @use 读取数据
|
|
*/
|
|
private static function receive()
|
|
{
|
|
$len_body = self::reader(4);
|
|
if (!$len_body) {
|
|
// 长度为0 ,空信息
|
|
return;
|
|
}
|
|
Log::debug("(len=$len_body)");
|
|
$body = self::reader($len_body);
|
|
$raw_data = json_decode($body, true);
|
|
// 人气值(或者在线人数或者类似)以及心跳
|
|
$data_type = $raw_data['type'];
|
|
switch ($data_type) {
|
|
case 'raffle':
|
|
// 抽奖推送
|
|
Log::debug("(receive={$body})");
|
|
DataTreating::distribute($raw_data['data']);
|
|
break;
|
|
case 'entered':
|
|
// 握手确认
|
|
Log::info("确认到推送服务器 {$raw_data['type']}");
|
|
break;
|
|
case 'error':
|
|
// 致命错误
|
|
Log::error("推送服务器发生致命错误 {$raw_data['data']['msg']}");
|
|
exit();
|
|
break;
|
|
case 'heartbeat':
|
|
// 服务端心跳推送
|
|
// Log::info("推送服务器心跳推送 {$body}");
|
|
Log::debug("(heartbeat={$raw_data['data']['now']})");
|
|
break;
|
|
default:
|
|
// 未知信息
|
|
var_dump($raw_data);
|
|
Log::info("出现未知信息 {$body}");
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* @use 写入log
|
|
* @param $message
|
|
*/
|
|
private static function writeLog($message)
|
|
{
|
|
$path = './danmu/';
|
|
if (!file_exists($path)) {
|
|
mkdir($path);
|
|
chmod($path, 0777);
|
|
}
|
|
$filename = $path . getenv('APP_USER') . ".log";
|
|
$date = date('[Y-m-d H:i:s] ');
|
|
$data = "[{$date}]{$message}" . PHP_EOL;
|
|
file_put_contents($filename, $data, FILE_APPEND);
|
|
return;
|
|
}
|
|
} |