Workerman 聊天室用戶時間仍會被ISP 網路供應商的防呆刪除TCP連線, 如何設置 heartbeat 及 用戶自定義暱名 及 私下一對一聊天 及 群聊功能

回覆文章
Brave Ye
系統管理員
文章: 5547
註冊時間: 2025-11-08, 13:32
聯繫:

Workerman 聊天室用戶時間仍會被ISP 網路供應商的防呆刪除TCP連線, 如何設置 heartbeat 及 用戶自定義暱名 及 私下一對一聊天 及 群聊功能

文章 Brave Ye »

要在 Workerman(或 GatewayWorker,專門用於聊天室與即時通訊的 Workerman 框架)中解決 TCP 連線被 ISP 中斷的問題,並實現暱稱、私聊與群聊功能,最標準且高效的做法是使用 GatewayWorker 架構。
以下是完整的實現方案與步驟說明:
1. 心跳機制(Heartbeat)設置
ISP 網路供應商或防火牆通常會清理長時間無數據傳輸的「靜默」TCP 連線(預設常為 60~300 秒)。設置心跳可以確保連線持續保持活躍。
在 GatewayWorker 的 start_gateway.php 配置文件中設置:
use GatewayWorker\Gateway;

$gateway = new Gateway("websocket://0.0.0.0:8282");
$gateway->name = 'ChatGateway';
$gateway->count = 4;
$gateway->lanIp = '127.0.0.1';
$gateway->startPort = 2000;

// 心跳檢測間隔(秒)
$gateway->pingInterval = 30;

// 客戶端連續未傳送數據的允許次數,超過 30*2 = 60 秒未收到數據則強制斷開
$gateway->pingNotResponseLimit = 2;

// 發送給客戶端的心跳數據包(客戶端收到後可選擇回應或僅保持連線)
$gateway->pingData = '{"type":"ping"}';

前端(Client)處理邏輯:
客戶端收到 {"type":"ping"} 時,需回傳 {"type":"pong"} 給服務端,證明連線仍存活。
2. 後端核心業務邏輯(Events.php)
包含 自定義暱稱、一對一私聊 與 群聊 功能的完整邏輯:
<?php
use \GatewayWorker\Lib\Gateway;

class Events {

// 當客戶端發送訊息時觸發
public static function onMessage($client_id, $message) {
$data = json_decode($message, true);
if (!$data || !isset($data['type'])) {
return;
}

switch ($data['type']) {
// 1. 客戶端回應心跳包
case 'pong':
return;

// 2. 用戶登入/設定自定義暱稱
case 'login':
$nickname = htmlspecialchars($data['nickname'] ?? '匿名用戶_' . substr($client_id, -4));

// 將 nickname 綁定到當前連線 session 中
$_SESSION['nickname'] = $nickname;

// 預設將所有人加入公共大廳群組 'room_public'
Gateway::joinGroup($client_id, 'room_public');

// 回覆登入成功訊息
Gateway::sendToClient($client_id, json_encode([
'type' => 'login_success',
'client_id' => $client_id,
'nickname' => $nickname
]));

// 廣播給所有人有新用戶加入
Gateway::sendToGroup('room_public', json_encode([
'type' => 'system',
'content' => "{$nickname} 加入了聊天室"
]));
break;

// 3. 群聊訊息(發送到指定群組)
case 'group_chat':
$group_id = $data['group_id'] ?? 'room_public';
$content = htmlspecialchars($data['content']);
$sender = $_SESSION['nickname'] ?? '匿名';

Gateway::sendToGroup($group_id, json_encode([
'type' => 'group_chat',
'from_client_id' => $client_id,
'from_nickname' => $sender,
'group_id' => $group_id,
'content' => $content
]));
break;

// 4. 一對一私聊訊息
case 'private_chat':
$target_client_id = $data['target_client_id'];
$content = htmlspecialchars($data['content']);
$sender = $_SESSION['nickname'] ?? '匿名';

// 確認目標用戶是否在線
if (Gateway::isOnline($target_client_id)) {
Gateway::sendToClient($target_client_id, json_encode([
'type' => 'private_chat',
'from_client_id' => $client_id,
'from_nickname' => $sender,
'content' => $content
]));
} else {
// 目標離線提示
Gateway::sendToClient($client_id, json_encode([
'type' => 'system',
'content' => '對方目前不在此連線中'
]));
}
break;
}
}

// 當用戶斷開連線時觸發
public static function onClose($client_id) {
$nickname = $_SESSION['nickname'] ?? '匿名用戶';
Gateway::sendToGroup('room_public', json_encode([
'type' => 'system',
'content' => "{$nickname} 離開了聊天室"
]));
}
}

3. 前端(JavaScript)調用範例
前端建立 WebSocket 連線並處理收發數據邏輯:
const ws = new WebSocket('ws://your-server-ip:8282');

ws.onopen = function() {
console.log("WebSocket 連線成功");

// 發送登入並設定暱稱
ws.send(JSON.stringify({
type: 'login',
nickname: '極客大師'
}));
};

ws.onmessage = function(e) {
const data = JSON.parse(e.data);

// 處理服務端發送的心跳檢測
if (data.type === 'ping') {
ws.send(JSON.stringify({ type: 'pong' }));
return;
}

// 訊息處理
switch (data.type) {
case 'login_success':
console.log(`登入成功,你的 client_id 是: ${data.client_id}`);
break;
case 'group_chat':
console.log(`[群聊] ${data.from_nickname}: ${data.content}`);
break;
case 'private_chat':
console.log(`[私聊] ${data.from_nickname}: ${data.content}`);
break;
case 'system':
console.log(`[系統通知] ${data.content}`);
break;
}
};

// --- 發送訊息函式 ---

// 發送群聊
function sendGroupMsg(content, groupId = 'room_public') {
ws.send(JSON.stringify({
type: 'group_chat',
group_id: groupId,
content: content
}));
}

// 發送私聊
function sendPrivateMsg(targetClientId, content) {
ws.send(JSON.stringify({
type: 'private_chat',
target_client_id: targetClientId,
content: content
}));
}

關鍵功能驗證方法
* 心跳驗證: 打開瀏覽器開發者工具(F12)-> Network -> WS -> Network bounds,可看到每隔 30 秒會收到一個 {"type":"ping"} 影格,且前端會立即回應 {"type":"pong"},確認連線不會被 ISP 清理。
* 私聊與群聊驗證: 開啟兩個不同的無痕視窗連線,取得各自的 client_id 後,即可透過 sendPrivateMsg(targetClientId, '內容') 進行私訊,或是透過 sendGroupMsg('內容') 進行全體廣播。
php8.5-cli的php.ini設定
https://share.gemini.google/q6UrTLqjjXS5
針對高並發場景,Workerman 還需要調整哪些 Linux 系統與 PHP 參數來達到最大吞吐量?
https://share.gemini.google/EtUeCsdUPhb5
回覆文章

誰在線上

正在瀏覽這個版面的使用者:沒有註冊會員 和 0 位訪客