private bool CreateReceiverSocket() {
try {
_receiverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
_receiverSocket.Bind(new IPEndPoint(IPAddress.Any, _port));
_receiverSocket.Listen(_maxBacklog);
return true;
} catch {
OnReceiverException();
return false;
}
}
private void ListenClientRequest(object state) {
Socket client = null;
while (!_stopReceiver) {
if (_stopAccepting) {
// 停止接受新連線:關閉監聽 Socket 強制解除 Accept 阻塞
_receiverSocket?.Close();
_receiverSocket = null;
Thread.Sleep(100);
continue;
}
if (_receiverSocket == null) {
if (!CreateReceiverSocket()) continue;
}
try {
// Poll 非阻塞檢查是否有連線待接受
if (_receiverSocket.Poll(_pollTimeoutMs, SelectMode.SelectRead)) {
client = _receiverSocket.Accept();
if (client == null || !client.Connected) continue;
// 1. 連線數限制
if (_clientCount >= _maxClients) {
RejectClient(client, "Max clients reached");
continue;
}
// 2. IP 重複檢測(同一 IP 僅允許一連線)
if (IsDuplicateIP(client)) {
RejectClient(client, "Duplicate IP");
continue;
}
// 3. 建立會話物件
var session = new TSession(client) {
LoginTime = DateTime.Now
};
lock (_sessionTable) {
// 確保 ID 唯一
while (_sessionTable.ContainsKey(session.ID))
session.ID = 100000 + session.ID;
_sessionTable.Add(session.ID, session);
Interlocked.Increment(ref _clientCount);
}
OnClientConnected(session);
// 4. 啟動非同步接收鏈
try {
session.ClientSocket.BeginReceive(
session.Buffer, 0, session.BufferSize, SocketFlags.None,
EndReceiveCallback, session);
} catch {
session.State = TSessionState.NoReply;
session.DisconnectType = TDisconnectType.Exception;
}
}
} catch (Exception ex) {
OnReceiverException(ex);
client?.Close();
}
Thread.Sleep(_loopSleepMs); // 降低 CPU 佔用
}
}
private void RejectClient(Socket client, string reason) {
try { client.Shutdown(SocketShutdown.Both); client.Close(); } catch { }
Log($"Rejected: {reason} from {client.RemoteEndPoint}");
}
private void EndReceiveCallback(IAsyncResult ar) {
var session = (TSession)ar.AsyncState;
if (session == null || session.State != TSessionState.Normal) return;
Socket client = session.ClientSocket;
int bytesRead = 0;
try {
bytesRead = client.EndReceive(ar);
} catch (SocketException) {
bytesRead = 0;
} catch (ObjectDisposedException) {
return; // Socket 已關閉
}
if (bytesRead == 0) {
// 客戶端正常關閉 (FIN)
session.State = TSessionState.NoReply;
session.DisconnectType = TDisconnectType.Normal;
return;
}
// 更新活躍時間
session.LastActivity = DateTime.Now;
// 處理接收資料(解析封包、業務邏輯)
bool processed = ProcessReceivedData(session, bytesRead);
if (!processed) {
session.State = TSessionState.NoReply;
session.DisconnectType = TDisconnectType.Exception;
return;
}
// 關鍵:啟動下一輪非同步接收,形成接收鏈
try {
client.BeginReceive(session.Buffer, 0, session.BufferSize, SocketFlags.None,
EndReceiveCallback, session);
} catch {
session.State = TSessionState.NoReply;
session.DisconnectType = TDisconnectType.Exception;
}
}
class TSession {
public int ID { get; set; } = Interlocked.Increment(ref _globalIdSeed);
public Socket ClientSocket { get; }
public byte[] Buffer { get; } = new byte[8192];
public int BufferSize => Buffer.Length;
public DateTime LoginTime { get; set; }
public DateTime LastActivity { get; set; } = DateTime.Now;
public TSessionState State { get; set; } = TSessionState.Normal;
public TDisconnectType DisconnectType { get; set; }
public TSession(Socket socket) => ClientSocket = socket;
public void ClearBuffer() => Array.Clear(Buffer, 0, Buffer.Length);
}
| 難點 | 風險 | 對策 |
|---|---|---|
| Accept/BeginReceive 異常 | Socket 關閉時拋 ObjectDisposedException | try/catch 忽略,檢查 session.State |
| 非同步鏈斷裂 | 忘記呼叫下一輪 BeginReceive | 在 EndReceive 末尾強制呼叫 |
| 緩衝區重用 | 多執行緒並發存取同一緩衝區 | 每 Session 獨立緩衝區 + Monitor 保護 |
| 頻繁連線/斷線 | Poll/Accept 競態、ID 衝突 | ID 衝突重試、快照隔離清理 |
| 大封包分片 | 單次 Receive 不保證完整封包 | 應用層協定定長/分隔符/長度前綴 |
_pollTimeoutMs = 100:Poll 逾時,平衡響應與 CPU_loopSleepMs = 10:迴圈休眠,避免空轉BufferSize = 8192:8KB 接收緩衝,適應多數 MTU_maxBacklog = 1000:Listen backlog,高併發調大_scanIntervalMs = 5000:清理掃描間隔請參考「C# 高效能非同步 Socket 接收器框架(五)」:三階段清理、狀態機驅動、快照隔離、系統效能總結。