using System; using System.Collections.Generic; using System.Linq; using System.Net; using System.Net.Sockets; using System.Threading; using ZeroLevel.Network.SDL; namespace ZeroLevel.Network { internal sealed class SocketServer : BaseSocket, IRouter { private Socket _serverSocket; private ReaderWriterLockSlim _connection_set_lock = new ReaderWriterLockSlim(); private Dictionary _connections = new Dictionary(); private readonly IRouter _router; public IPEndPoint LocalEndpoint { get; } public event Action OnDisconnect = _ => { }; public event Action OnConnect = _ => { }; public IEnumerable ConnectionList { get { try { _connection_set_lock.EnterReadLock(); return _connections.Select(c => c.Value.EndPoint).ToList(); } finally { _connection_set_lock.ExitReadLock(); } } } private void DisconnectEventRise(ISocketClient client) { try { OnDisconnect?.Invoke(client); } catch { } } private void ConnectEventRise(ExClient client) { try { OnConnect?.Invoke(client); } catch { } } public SocketServer(IPEndPoint endpoint, IRouter router) { _router = router; LocalEndpoint = endpoint; _serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); _serverSocket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.KeepAlive, true); _serverSocket.Bind(endpoint); _serverSocket.Listen(100); Working(); _serverSocket.BeginAccept(BeginAcceptCallback, null); } private void BeginAcceptCallback(IAsyncResult ar) { if (Status == SocketClientStatus.Working) { try { var client_socket = _serverSocket.EndAccept(ar); _connection_set_lock.EnterWriteLock(); var connection = new SocketClient(client_socket, _router); connection.OnDisconnect += Connection_OnDisconnect; _connections[connection.Endpoint] = new ExClient(connection); connection.UseKeepAlive(TimeSpan.FromMilliseconds(BaseSocket.MINIMUM_HEARTBEAT_UPDATE_PERIOD_MS)); ConnectEventRise(_connections[connection.Endpoint]); } catch (Exception ex) { Broken(); Log.SystemError(ex, "[ZSocketServer.BeginAcceptCallback] Error with connect accepting"); } finally { _connection_set_lock.ExitWriteLock(); } try { _serverSocket.BeginAccept(BeginAcceptCallback, null); } catch (Exception ex) { Log.SystemError(ex, "[ZSocketServer.BeginAcceptCallback] BeginAccept error"); } } else { Log.Warning($"Server socket change state to: {Status}"); } } private void Connection_OnDisconnect(ISocketClient client) { client.OnDisconnect -= Connection_OnDisconnect; try { _connection_set_lock.EnterWriteLock(); _connections[client.Endpoint].Dispose(); _connections.Remove(client.Endpoint); } finally { _connection_set_lock.ExitWriteLock(); } DisconnectEventRise(client); } public override void Dispose() { try { foreach (var c in _connections) { c.Value.Dispose(); } _connections.Clear(); } catch (Exception ex) { Log.SystemError(ex, "[SocketServer.Dispose]"); } } #region IRouter public void HandleMessage(Frame frame, ISocketClient client) => _router.HandleMessage(frame, client); public void HandleRequest(Frame frame, ISocketClient client, int identity, Action handler) => _router.HandleRequest(frame, client, identity, handler); public IServer RegisterInbox(string inbox, MessageHandler handler) => _router.RegisterInbox(inbox, handler); public IServer RegisterInbox(MessageHandler handler) => _router.RegisterInbox(handler); public IServer RegisterInbox(string inbox, MessageHandler handler) => _router.RegisterInbox(inbox, handler); public IServer RegisterInbox(MessageHandler handler) => _router.RegisterInbox(handler); public IServer RegisterInbox(string inbox, RequestHandler handler) => _router.RegisterInbox(inbox, handler); public IServer RegisterInbox(string inbox, RequestHandler handler) => _router.RegisterInbox(inbox, handler); public IServer RegisterInbox(RequestHandler handler) => _router.RegisterInbox(handler); public IServer RegisterInbox(RequestHandler handler) => _router.RegisterInbox(handler); public bool ContainsInbox(string inbox) => _router.ContainsInbox(inbox); public bool ContainsHandlerInbox(string inbox) => _router.ContainsHandlerInbox(inbox); public bool ContainsRequestorInbox(string inbox) => _router.ContainsRequestorInbox(inbox); public IEnumerable CollectInboxInfo() => _router.CollectInboxInfo(); #endregion } }