269 lines
8.8 KiB
PHP
269 lines
8.8 KiB
PHP
<?php
|
||
|
||
// WebSocket服务器启动脚本
|
||
// 使用方法:php ws_server.php
|
||
|
||
// 启用错误报告
|
||
ini_set('display_errors', 1);
|
||
ini_set('display_startup_errors', 1);
|
||
error_reporting(E_ALL);
|
||
|
||
require __DIR__ . '/vendor/autoload.php';
|
||
|
||
// 初始化ThinkPHP应用环境,加载配置和环境变量,主要用于数据库连接和其他配置
|
||
use think\App;
|
||
|
||
// 创建应用实例
|
||
// @intelephense-ignore-next-line
|
||
$app = new App();
|
||
|
||
// 加载环境变量
|
||
if (is_file(__DIR__ . '/.env')) {
|
||
$app->env->load(__DIR__ . '/.env');
|
||
}
|
||
|
||
// 根据APP_ENV加载环境特定的.env文件
|
||
$appEnv = getenv('APP_ENV') ?: '';
|
||
if ($appEnv) {
|
||
$envFile = __DIR__ . '/.env.' . $appEnv;
|
||
if (is_file($envFile)) {
|
||
$app->env->load($envFile);
|
||
}
|
||
}
|
||
|
||
use Ratchet\App as RatchetApp;
|
||
use Ratchet\ConnectionInterface;
|
||
use Ratchet\MessageComponentInterface;
|
||
|
||
// 配置WebSocket服务器
|
||
$httpHost = 'localhost'; // 客户端连接时使用的主机名
|
||
$port = getenv('WS_PORT') ?: 8080; // WebSocket服务器端口
|
||
$address = '0.0.0.0'; // 监听所有网络接口
|
||
|
||
// 创建WebSocket服务器应用 - 参数顺序:httpHost, port, address
|
||
$ratchetApp = new RatchetApp($httpHost, $port, $address);
|
||
|
||
// 获取所有addon(与InitAddon.php逻辑保持一致)
|
||
// 1. 从数据库获取插件列表
|
||
|
||
use app\model\system\Addon;
|
||
|
||
$addonDir = __DIR__ . '/addon';
|
||
$addonNames = [];
|
||
|
||
// 从数据库获取addon列表
|
||
try {
|
||
$addon_model = new Addon();
|
||
$addon_data = $addon_model->getAddonList([], 'name,status');
|
||
$current_addons = $addon_data['data'];
|
||
$db_addon_names = array_column($current_addons, 'name');
|
||
|
||
// 2. 从addon目录读取插件列表
|
||
$dir_addon_names = [];
|
||
if (is_dir($addonDir)) {
|
||
$dir_handle = opendir($addonDir);
|
||
if ($dir_handle) {
|
||
while (($file = readdir($dir_handle)) !== false) {
|
||
if ($file != '.' && $file != '..' && is_dir($addonDir . '/' . $file)) {
|
||
$dir_addon_names[] = $file;
|
||
}
|
||
}
|
||
closedir($dir_handle);
|
||
}
|
||
}
|
||
|
||
// 3. 使用数据库中的插件列表进行后续处理
|
||
$current_addon_names = $db_addon_names;
|
||
|
||
// 4. 记录已启用的插件
|
||
$enabled_addons = [];
|
||
foreach ($current_addons as $addon) {
|
||
if (isset($addon['status']) && $addon['status'] == 1) {
|
||
$enabled_addons[] = $addon['name'];
|
||
}
|
||
}
|
||
|
||
echo "[WebSocket服务器] 从数据库获取的插件列表: " . implode(', ', $current_addon_names) . "\n";
|
||
echo "[WebSocket服务器] 已启用的插件: " . implode(', ', $enabled_addons) . "\n";
|
||
echo "[WebSocket服务器] 目录中存在的插件: " . implode(', ', $dir_addon_names) . "\n";
|
||
|
||
// 5. 比较数据库和目录插件列表的差异
|
||
$db_only_addons = array_diff($db_addon_names, $dir_addon_names);
|
||
$dir_only_addons = array_diff($dir_addon_names, $db_addon_names);
|
||
|
||
if (!empty($db_only_addons)) {
|
||
echo "[WebSocket服务器] 数据库中存在但目录中不存在的插件: " . implode(', ', $db_only_addons) . "\n";
|
||
}
|
||
|
||
if (!empty($dir_only_addons)) {
|
||
echo "[WebSocket服务器] 目录中存在但数据库中不存在的插件: " . implode(', ', $dir_only_addons) . "\n";
|
||
}
|
||
|
||
} catch (\Exception $e) {
|
||
echo "[WebSocket服务器] 获取插件列表失败: " . $e->getMessage() . "\n";
|
||
echo "[WebSocket服务器] 回退到直接扫描目录获取插件列表\n";
|
||
|
||
// 回退到直接扫描目录
|
||
$addonNames = [];
|
||
if (is_dir($addonDir)) {
|
||
$handle = opendir($addonDir);
|
||
while (($file = readdir($handle)) !== false) {
|
||
if ($file != '.' && $file != '..' && is_dir($addonDir . '/' . $file)) {
|
||
$addonNames[] = $file;
|
||
}
|
||
}
|
||
closedir($handle);
|
||
sort($addonNames);
|
||
}
|
||
|
||
$current_addon_names = $addonNames;
|
||
$enabled_addons = $addonNames; // 回退模式下默认所有插件都启用
|
||
}
|
||
|
||
// 为每个addon注册WebSocket控制器,并记录注册情况
|
||
$registeredAddons = [];
|
||
$unregisteredAddons = [];
|
||
$disabledAddons = [];
|
||
$missingDirAddons = [];
|
||
|
||
foreach ($current_addon_names as $addonName) {
|
||
// 检查插件是否已启用
|
||
if (!in_array($addonName, $enabled_addons)) {
|
||
echo "[{$addonName}] 插件未启用,跳过WebSocket控制器注册\n";
|
||
$disabledAddons[] = $addonName;
|
||
continue;
|
||
}
|
||
|
||
// 检查插件目录是否存在
|
||
if (!is_dir($addonDir . '/' . $addonName)) {
|
||
echo "[{$addonName}] 插件目录不存在,跳过WebSocket控制器注册\n";
|
||
$missingDirAddons[] = $addonName;
|
||
continue;
|
||
}
|
||
|
||
$webSocketClass = "addon\\{$addonName}\\api\\controller\\WebSocket";
|
||
|
||
// 检查WebSocket控制器是否存在
|
||
try {
|
||
if (class_exists($webSocketClass)) {
|
||
// 注册到/ws/{addonName}路径
|
||
$path = '/ws/' . $addonName;
|
||
$ratchetApp->route($path, new $webSocketClass(), array('*'));
|
||
echo "已注册WebSocket控制器:{$webSocketClass} 到路径 {$path}\n";
|
||
$registeredAddons[] = $addonName;
|
||
} else {
|
||
echo "[{$addonName}] WebSocket控制器不存在 ({$webSocketClass})\n";
|
||
$unregisteredAddons[] = $addonName;
|
||
}
|
||
} catch (\Exception $e) {
|
||
echo "[{$addonName}] 检查WebSocket控制器时出错:{$e->getMessage()}\n";
|
||
$unregisteredAddons[] = $addonName;
|
||
}
|
||
}
|
||
|
||
// 实现默认的/ws路径的简单测试控制器
|
||
class DefaultWebSocketController implements MessageComponentInterface
|
||
{
|
||
protected $clients;
|
||
|
||
public function __construct()
|
||
{
|
||
$this->clients = new \SplObjectStorage;
|
||
}
|
||
|
||
public function onOpen(ConnectionInterface $conn)
|
||
{
|
||
$this->clients->attach($conn);
|
||
echo "[默认路径] New connection! ({$conn->resourceId})\n";
|
||
$conn->send(json_encode([
|
||
'type' => 'welcome',
|
||
'message' => '欢迎连接到默认WebSocket测试路径',
|
||
'info' => '此路径仅用于测试,不提供实际功能。请使用/ws/{addonName}连接到具体的addon服务。'
|
||
]));
|
||
}
|
||
|
||
public function onMessage(ConnectionInterface $conn, $msg)
|
||
{
|
||
echo "[默认路径] Received message from {$conn->resourceId}: $msg\n";
|
||
try {
|
||
$data = json_decode($msg, true);
|
||
if (isset($data['action']) && $data['action'] === 'ping') {
|
||
$conn->send(json_encode(['type' => 'pong']));
|
||
} else {
|
||
$conn->send(json_encode([
|
||
'type' => 'info',
|
||
'message' => '收到消息,但默认路径不提供实际功能',
|
||
'received' => $data
|
||
]));
|
||
}
|
||
} catch (\Exception $e) {
|
||
$conn->send(json_encode(['type' => 'error', 'message' => '解析消息失败: ' . $e->getMessage()]));
|
||
}
|
||
}
|
||
|
||
public function onClose(ConnectionInterface $conn)
|
||
{
|
||
$this->clients->detach($conn);
|
||
echo "[默认路径] Connection {$conn->resourceId} has disconnected\n";
|
||
}
|
||
|
||
public function onError(ConnectionInterface $conn, \Exception $e)
|
||
{
|
||
echo "[默认路径] An error has occurred: {$e->getMessage()}\n";
|
||
$conn->close();
|
||
}
|
||
}
|
||
|
||
// 注册默认的/ws路径测试控制器
|
||
$ratchetApp->route('/ws', new DefaultWebSocketController(), array('*'));
|
||
echo "已注册默认WebSocket测试控制器到路径 /ws\n";
|
||
|
||
echo "WebSocket服务器已启动,监听地址: ws://{$httpHost}:{$port}\n";
|
||
|
||
// 显示已注册WebSocket控制器的addon路径
|
||
echo "\n已注册WebSocket控制器的addon路径:\n";
|
||
if (!empty($registeredAddons)) {
|
||
foreach ($registeredAddons as $addonName) {
|
||
echo " - ws://{$httpHost}:{$port}/ws/{$addonName} (已注册)\n";
|
||
}
|
||
} else {
|
||
echo " - 暂无已注册的WebSocket控制器\n";
|
||
}
|
||
|
||
// 显示未注册WebSocket控制器的addon路径
|
||
echo "\n未注册WebSocket控制器的addon路径:\n";
|
||
if (!empty($unregisteredAddons)) {
|
||
foreach ($unregisteredAddons as $addonName) {
|
||
echo " - ws://{$httpHost}:{$port}/ws/{$addonName} (未注册)\n";
|
||
}
|
||
} else {
|
||
echo " - 所有已启用且目录存在的addon都已注册WebSocket控制器\n";
|
||
}
|
||
|
||
// 显示未启用的addon
|
||
echo "\n未启用的addon:\n";
|
||
if (!empty($disabledAddons)) {
|
||
foreach ($disabledAddons as $addonName) {
|
||
echo " - {$addonName} (未启用)\n";
|
||
}
|
||
} else {
|
||
echo " - 所有addon都已启用\n";
|
||
}
|
||
|
||
// 显示目录不存在的addon
|
||
echo "\n目录不存在的addon:\n";
|
||
if (!empty($missingDirAddons)) {
|
||
foreach ($missingDirAddons as $addonName) {
|
||
echo " - {$addonName} (目录不存在)\n";
|
||
}
|
||
} else {
|
||
echo " - 所有已启用的addon目录都存在\n";
|
||
}
|
||
|
||
echo "\n默认测试路径:\n";
|
||
echo " - ws://{$httpHost}:{$port}/ws (默认路径,用于连接测试)\n";
|
||
echo "按 Ctrl+C 停止服务器\n";
|
||
|
||
// 运行服务器
|
||
$ratchetApp->run();
|