使用 DotMSN 開發 MSN 機器人時,機器人在閒置一段時間後會**無聲斷線**:無任何事件通知,Connected 屬性仍為 true,Owner.Status 正常,極難排查。本文揭露根因並提供 **自訂 PingMessage 保活** 完整實作。
PNG\r\n (Ping) 指令,伺服器回覆 QNG\r\n (Pong),可作為保活探針。
void SendPing()
{
// NSMessage 會自動附加 TransactionID、參數、空格、換行
// 實際發送: "PNG 123 \r\n" —— 不符合協定純粹 "PNG\r\n"
NSMessage message = new NSMessage("PNG\r\n", new string[] { });
messenger.NameserverProcessor.SendMessage(message);
}
結果:雖觸發 PingAnswer 事件 (伺服器有回應),但同時觸發 SignedOff 被踢下線。**原因**:NSMessage.GetBytes() 序列化格式包含額外欄位,破壞 Ping 指令純粹性。
using XihSolutions.DotMSN.Core;
public class PingMessage : NSMessage
{
public override byte[] GetBytes()
{
// 純粹發送 "PNG\r\n",無 TransactionID、無參數、無額外空格
return System.Text.Encoding.UTF8.GetBytes("PNG\r\n");
}
}
void SendPing()
{
messenger.NameserverProcessor.SendMessage(new PingMessage());
}
測試:登入後呼叫 SendPing() → PingAnswer 觸發,**不再觸發 SignedOff**,連線維持正常。
using System.Timers;
public class MsnBot
{
private readonly Timer _keepAliveTimer;
private readonly Messenger _messenger;
public MsnBot(Messenger messenger)
{
_messenger = messenger;
_keepAliveTimer = new Timer(120_000) // 每 2 分鐘
{
AutoReset = true,
Enabled = false
};
_keepAliveTimer.Elapsed += (s, e) => SendPing();
}
public void Start()
{
_messenger.Login();
_keepAliveTimer.Start();
}
public void Stop()
{
_keepAliveTimer.Stop();
_messenger.Logout();
}
private void SendPing()
{
try
{
_messenger.NameserverProcessor.SendMessage(new PingMessage());
}
catch (Exception ex)
{
// 記錄錯誤,避免 Timer 執行緒崩潰
Console.WriteLine($"Ping 發送失敗: {ex.Message}");
}
}
}
NameserverProcessor,需參考原始碼調整存取路徑。| 指令 | 格式 | 方向 | 用途 |
|---|---|---|---|
| PNG | PNG\r\n | Client → Server | 心跳探針 |
| QNG | QNG\r\n | Server → Client | 心跳回應 |
| CHL | CHL | Server → Client | 挑戰認證 |
| QRY | QRY | Client → Server | 查詢用戶/群組 |
DotMSN 機器人斷線核心在於**缺乏應用層心跳**。關鍵突破點:NSMessage 序列化格式不符 Ping 指令規範 → **繼承並覆寫 GetBytes() 產出純粹 PNG\r\n** → 搭配 Timer 定時發送。此方案輕量、無侵入性、相容現有 DotMSN 架構,是解決 MSN 機器人閒置斷線的標準做法。