首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> 關於C語言

C# 高效能非同步 Socket 接收器框架(三):監聽迴圈、客戶端連線處理與非同步接收鏈

     編輯:關於C語言

建立監聽 Socket


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}");
}

非同步接收回呼:EndReceive → 處理 → BeginReceive


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;
    }
}

TSession 會話物件設計


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 關閉時拋 ObjectDisposedExceptiontry/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 接收器框架(五)」:三階段清理、狀態機驅動、快照隔離、系統效能總結。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved