我写了一个 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();
}
}
}
