Skip to main content
 首页 » 编程设计

c# AsyncSocket 服务器需要锁定

2024年02月27日35wuhuacong

我写了一个 TCP 套接字服务器。一般来说,我希望有以下行为:

  • A Listening Socket can accept N Connections (There a multiple Listeners on different Ports (1337,733) )
  • 一个连接可以将自己认证为“客户端”,多个连接可以归为一个“客户端”
  • 多个连接可以同时接收/接收数据(并发)

这是我的服务器类:

class Server 
{ 
    internal List ConnectionListeners; 
 
    internal bool IsListening = false; 
 
    internal Server() 
    { 
        this.ClientManager = new ClientManager(); 
        this.ConnectionManager = new ConnectionManager(); 
        this.ConnectionListeners = new List(); 
    } 
 
    internal void AddConnectionListener(int Port) 
    { 
        ConnectionListener c = new ConnectionListener(Port); 
        c.AcceptedConnection += new ConnectionListener.AcceptedConnectionEventHandler(ConnectionProcessor.AcceptConnection); 
        ConnectionListeners.Add(c); 
    } 
 
    internal void RemoveConnectionListener(ConnectionListener ConnectionListener) 
    { 
        ConnectionListeners.Remove(ConnectionListener); 
    } 
 
    public delegate void OnStartListeningEventHandler(); 
    public event OnStartListeningEventHandler OnStartListening; 
 
    internal void StartListening() 
    { 
        IsListening = true; 
 
        foreach (ConnectionListener cl in this.ConnectionListeners) 
        { 
            cl.StartListening(); 
        } 
 
        OnStartListening?.Invoke(); 
    } 
 
    public delegate void OnStopListeningEventHandler(); 
    public event OnStopListeningEventHandler OnStopListening; 
 
    internal void StopListening() 
    { 
        ConnectionManager.DisconnectConnections(); 
        foreach (ConnectionListener cl in this.ConnectionListeners) 
        { 
            cl.StopListening(); 
        } 
        IsListening = false; 
        OnStopListening?.Invoke(); 
    } 
 
} 

ConnectionProcessor 的方法,其中我处理一个新的接受连接 (ConnectionProcessor.AcceptConnection):

        internal void AcceptConnection(Socket Socket) 
        { 
            Connection Connection = new Connection(Socket); 
            Connection.Sent += new Connection.SentEventHandler(onSend); 
            Connection.Received += new Connection.ReceivedEventHandler(onRecive); 
            Connection.Disconnected += new Connection.DisconnectedEventHandler(OnDisconnect); 
            Connection.Recive(); 
 
            Logger.Instance.AddLog(new LogMessage(LogMessage.LogLevel.Normal, "Connection ("+Connection.ConnectionId+") Accepted")); 
            ConnectionManager.AddConnection(Connection); 
        } 

连接管理器:

    class ConnectionManager 
    { 
        internal ConnectionManager() 
        { 
            this.Connections = new List(); 
        } 
 
        internal void AddConnection(Connection Connection) 
        { 
            Connections.Add(Connection); 
            OnAddConnection(Connection); 
        } 
 
        internal void RemoveConnection(Connection Connection) 
        { 
            Connections.Remove(Connection); 
            OnRemoveConnection(Connection); 
        } 
 
        internal void DisconnectConnections() 
        { 
            foreach (Connection c in Connections) 
            { 
                c.Disconnect(); 
            } 
        } 
    } 

似乎一切正常,但我不确定并发性。

如您在 ConnectionManger 中所见,我将每个连接存储在一个列表 (Connections.Add(Connection)) 中。这样做够了吗?我必须关心一个普通的“列表”不是线程安全的吗?

我在 gerneal 中的“设计”是解决我的需求的正确方法吗?

请您参考如下方法:

由于您所做的只是在列表中添加\删除\枚举连接 - 您可以使用线程安全的集合,而无需任何锁。遗憾的是,没有 ConcurrentList 或 ConcurrentHashSet,但您可以将 ConcurrentDictionary 与虚拟键一起使用,可以直接使用,也可以通过包装在单独的类中:

class BasicConcurrentSet<T> : IEnumerable<T> { 
    private readonly ConcurrentDictionary<T, byte> _items = new ConcurrentDictionary<T, byte>(); 
 
    public void Add(T item) { 
        _items.TryAdd(item, 0); 
    } 
 
    public void Remove(T item) { 
        byte tmp; 
        _items.TryRemove(item, out tmp); 
    } 
 
    IEnumerator IEnumerable.GetEnumerator() { 
        return GetEnumerator(); 
    } 
 
    public IEnumerator<T> GetEnumerator() { 
        foreach (var kv in _items) { 
            yield return kv.Key; 
        } 
    } 
} 

从此类并发集合中添加、删除和枚举项目是线程安全的。

class ConnectionManager { 
    private readonly BasicConcurrentSet<Connection> _connections = new BasicConcurrentSet<Connection>(); 
 
    internal ConnectionManager() { 
 
    } 
 
    internal void AddConnection(Connection connection) { 
        _connections.Add(connection); 
        OnAddConnection(Connection); 
    } 
 
    internal void RemoveConnection(Connection connection) { 
        _connections.Remove(connection); 
        OnRemoveConnection(connection); 
    } 
 
    internal void DisconnectConnections() { 
        foreach (var connection in _connections) { 
            connection.Disconnect(); 
        } 
    } 
}