socket.php 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135
  1. <?php
  2. /**
  3. * WebSocket 服务端启动文件 (WSS 加密版)
  4. *
  5. * 使用方法:
  6. * php socket.php start # 调试模式启动(前台运行)
  7. * php socket.php start -d # 守护进程模式启动(后台运行)
  8. * php socket.php stop # 停止服务
  9. * php socket.php restart # 重启服务
  10. * php socket.php status # 查看运行状态
  11. *
  12. * 客户端连接地址:
  13. * wss://api.myjie.cn:2345?type=user&token=xxx&form_type=3&to_uid=对方UID
  14. */
  15. require __DIR__ . '/vendor/autoload.php';
  16. use Workerman\Worker;
  17. use Workerman\Connection\TcpConnection;
  18. // 日志目录
  19. define('LOG_PATH', __DIR__ . '/runtime/logs/workerman/');
  20. if (!is_dir(LOG_PATH)) {
  21. mkdir(LOG_PATH, 0755, true);
  22. }
  23. // ========== SSL 证书配置(WSS 必须) ==========
  24. // 宝塔证书默认文件名:fullchain.pem(证书)、privkey.pem(私钥)
  25. //$ssl_cert_path = '/www/wwwroot/api.myjie.cn/config/ssl/fullchain.pem'; // 证书文件
  26. //$ssl_key_path = '/www/wwwroot/api.myjie.cn/config/ssl/privkey.pem'; // 私钥文件
  27. $ssl_cert_path = '/www/server/panel/vhost/ssl/api.myjie.cn/fullchain.pem'; // 证书文件
  28. $ssl_key_path = '/www/server/panel/vhost/ssl/api.myjie.cn/privkey.pem'; // 私钥文件
  29. $context = [];
  30. if (file_exists($ssl_cert_path) && file_exists($ssl_key_path)) {
  31. $context = [
  32. 'ssl' => [
  33. 'local_cert' => $ssl_cert_path,
  34. 'local_pk' => $ssl_key_path,
  35. 'verify_peer' => false,
  36. ]
  37. ];
  38. }
  39. // ==============================================
  40. // 创建 WebSocket 服务(带 SSL 上下文)
  41. $ws_worker = new Worker('websocket://0.0.0.0:2345', $context);
  42. // 启用 SSL 传输层(使 websocket:// 升级为 wss://)
  43. if (!empty($context)) {
  44. $ws_worker->transport = 'ssl';
  45. }
  46. // 设置进程名称
  47. $ws_worker->name = 'ChatWebSocket';
  48. // 设置进程数
  49. $ws_worker->count = 4;
  50. // 设置心跳(60秒无响应则断开)
  51. $ws_worker->pingInterval = 60;
  52. $ws_worker->pingNotResponseLimit = 1;
  53. // 当进程启动时
  54. $ws_worker->onWorkerStart = function($worker) {
  55. echo "[Worker #{$worker->id}] started\n";
  56. // 初始化 ChatService
  57. $chatService = new \app\services\workerman\chat\ChatService($worker);
  58. $worker->chatService = $chatService;
  59. };
  60. // 当客户端连接时
  61. $ws_worker->onConnect = function(TcpConnection $connection) {
  62. $addr = $connection->getRemoteAddress();
  63. echo "[Connect] {$addr}\n";
  64. };
  65. // WebSocket 握手回调 - 解析 URL 查询参数(包括 to_uid)并自动登录
  66. $ws_worker->onWebSocketConnect = function(TcpConnection $connection) use ($ws_worker) {
  67. // 从 URL 查询字符串解析参数
  68. // 例如: wss://api.myjie.cn:2345?type=user&token=xxx&from=3&to_uid=456
  69. $query = $_GET ?? [];
  70. $connection->type = $query['type'] ?? 'user';
  71. $connection->token = $query['token'] ?? '';
  72. $connection->form_type = $query['form_type'] ?? 3;
  73. $connection->to_uid = isset($query['to_uid']) ? intval($query['to_uid']) : 0; // 聊天对象UID
  74. // 自动登录:根据 URL 参数设置用户信息
  75. if ($connection->token) {
  76. // TODO: 实际项目中应验证 token 有效性
  77. // 从 URL 参数获取用户 UID(from 或 uid 参数)
  78. $uid = intval($query['from'] ?? $query['uid'] ?? $connection->form_type ?? 0);
  79. $connection->user = (object)[
  80. 'uid' => $uid,
  81. 'nickname' => $query['nickname'] ?? '用户',
  82. 'avatar' => $query['avatar'] ?? '',
  83. ];
  84. $connection->chatToUid = $connection->to_uid;
  85. $ws_worker->chatService->setUser($connection);
  86. }
  87. echo "[WebSocket Connect] type={$connection->type}, uid={$connection->user->uid ?? 0}, to_uid={$connection->to_uid}\n";
  88. };
  89. // 当收到客户端消息时
  90. $ws_worker->onMessage = function(TcpConnection $connection, $data) use (&$chatService) {
  91. $chatService->onMessage($connection, $data);
  92. };
  93. // 当客户端断开连接时
  94. $ws_worker->onClose = function(TcpConnection $connection) use (&$chatService) {
  95. $chatService->onClose($connection);
  96. };
  97. // 当连接出错时
  98. $ws_worker->onError = function(TcpConnection $connection, $code, $msg) {
  99. echo "[Error] {$code}: {$msg}\n";
  100. };
  101. // 输出启动信息
  102. $ssl_ok = !empty($context);
  103. echo "\n";
  104. echo "========================================\n";
  105. echo " Chat WebSocket Server (WSS)\n";
  106. echo " Address: " . ($ssl_ok ? "wss" : "ws") . "://0.0.0.0:2345\n";
  107. if (!$ssl_ok) {
  108. echo " [警告] SSL 证书未配置,运行在 ws 模式\n";
  109. echo " 请将证书放到 config/ssl/cert.pem 和 config/ssl/key.pem\n";
  110. }
  111. echo " PID: " . getmypid() . "\n";
  112. echo "========================================\n\n";
  113. // 运行
  114. Worker::runAll();