using System; using System.Collections.Generic; using System.Net; using System.Net.Sockets; using xServer.Core.Packets; namespace xServer.Core.Networking { public class Server { /// /// Occurs when the state of the server changes. /// public event ServerStateEventHandler ServerState; /// /// Represents a method that will handle a change in the server's state. /// /// The server which changed its state. /// The new listening state of the server. public delegate void ServerStateEventHandler(Server s, bool listening); /// /// Fires an event that informs subscribers that the server has changed it's state. /// /// The new listening state of the server. private void OnServerState(bool listening) { if (ServerState != null) { ServerState(this, listening); } } /// /// Occurs when the state of a client changes. /// public event ClientStateEventHandler ClientState; /// /// Represents a method that will handle a change in a client's state. /// /// The server, the client is connected to. /// The client which changed its state. /// The new connection state of the client. public delegate void ClientStateEventHandler(Server s, Client c, bool connected); /// /// Fires an event that informs subscribers that a client has changed its state. /// /// The client which changed its state. /// The new connection state of the client. private void OnClientState(Client c, bool connected) { if (ClientState != null) { ClientState(this, c, connected); } } /// /// Occurs when a packet is received by a client. /// public event ClientReadEventHandler ClientRead; /// /// Represents a method that will handle a packet received from a client. /// /// The server, the client is connected to. /// The client that has received the packet. /// The packet that received by the client. public delegate void ClientReadEventHandler(Server s, Client c, IPacket packet); /// /// Fires an event that informs subscribers that a packet has been /// received from the client. /// /// The client that has received the packet. /// The packet that received by the client. private void OnClientRead(Client c, IPacket packet) { if (ClientRead != null) { ClientRead(this, c, packet); } } /// /// Occurs when a packet is sent by a client. /// public event ClientWriteEventHandler ClientWrite; /// /// Represents the method that will handle the sent packet by a client. /// /// The server, the client is connected to. /// The client that has sent the packet. /// The packet that has been sent by the client. /// The length of the packet. /// The packet in raw bytes. public delegate void ClientWriteEventHandler(Server s, Client c, IPacket packet, long length, byte[] rawData); /// /// Fires an event that informs subscribers that the client has sent a packet. /// /// The client that has sent the packet. /// The packet that has been sent by the client. /// The length of the packet. /// The packet in raw bytes. private void OnClientWrite(Client c, IPacket packet, long length, byte[] rawData) { if (ClientWrite != null) { ClientWrite(this, c, packet, length, rawData); } } /// /// The port on which the server is listening. /// public ushort Port { get; private set; } /// /// The total amount of received bytes. /// public long BytesReceived { get; set; } /// /// The total amount of sent bytes. /// public long BytesSent { get; set; } /// /// The buffer size for receiving data in bytes. /// public int BUFFER_SIZE { get { return (1024 * 1024) * 1; } } // 1MB /// /// The keep-alive time in ms. /// public uint KEEP_ALIVE_TIME { get { return 25000; } } // 25s /// /// The keep-alive interval in ms. /// public uint KEEP_ALIVE_INTERVAL { get { return 25000; } } // 25s /// /// The header size in bytes. /// public int HEADER_SIZE { get { return 3; } } // 3B /// /// Gets or sets if the server is currently processing data that should prevent disconnection. /// public bool Processing { get; private set; } /// /// The buffer manager to handle the receive buffers for the clients. /// public static PooledBufferManager BufferManager { get; private set; } /// /// The listening state of the server. True if listening, else False. /// public bool Listening { get; private set; } /// /// Gets the clients currently connected to the server, or an empty array of /// clients if the server is currently not listening. /// public Client[] Clients { get { lock (_clientsLock) { return Listening ? _clients.ToArray() : new Client[0]; } } } /// /// Handle of the Server Socket. /// private Socket _handle; /// /// The event to accept new connections asynchronously. /// private SocketAsyncEventArgs _item; /// /// List of the clients connected to the server. /// private List _clients; /// /// Lock object for the list of clients. /// private readonly object _clientsLock = new object(); /// /// List of all supported Packet Types by the server. /// private List PacketTypes { get; set; } /// /// Constructor of the server, initializes variables. /// public Server() { PacketTypes = new List(); } /// /// Begins listening for clients. /// /// Port to listen for clients on. public void Listen(ushort port) { this.Port = port; try { if (!Listening) { lock (_clientsLock) { _clients = new List(); } _item = new SocketAsyncEventArgs(); _item.Completed += AcceptClient; if (_handle != null) { try { _handle.Close(); } catch { } } if (BufferManager == null) BufferManager = new PooledBufferManager(BUFFER_SIZE, 1) { ClearOnReturn = true }; _handle = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); _handle.Bind(new IPEndPoint(IPAddress.Any, port)); _handle.Listen(1000); Processing = false; Listening = true; OnServerState(true); if (!_handle.AcceptAsync(_item)) AcceptClient(null, _item); } } catch (Exception) { Disconnect(); } } /// /// Adds a Type to the serializer so a message can be properly serialized. /// /// The parent type. /// Type to be added. public void AddTypeToSerializer(Type parent, Type type) { if (type == null || parent == null) throw new ArgumentNullException(); PacketTypes.Add(type); } /// /// Adds Types to the serializer. /// /// The parent type, i.e.: IPacket /// Types to add. public void AddTypesToSerializer(Type parent, params Type[] types) { foreach (Type type in types) AddTypeToSerializer(parent, type); } /// /// Processes an incoming client; adding the client to the list of clients, /// hooking up the client's events, and finally accepts the client. /// /// Unused, use null. /// Asynchronously Socket Event private void AcceptClient(object s, SocketAsyncEventArgs e) { try { do { switch (e.SocketError) { case SocketError.Success: if (BufferManager.BuffersAvailable == 0) BufferManager.IncreaseBufferCount(1); Client client = new Client(this, e.AcceptSocket, PacketTypes.ToArray()); lock (_clientsLock) { _clients.Add(client); client.ClientState += OnClientState; client.ClientRead += OnClientRead; client.ClientWrite += OnClientWrite; OnClientState(client, true); } break; case SocketError.ConnectionReset: break; default: throw new Exception("SocketError"); } e.AcceptSocket = null; // enable reuse } while (!_handle.AcceptAsync(e)); } catch (ObjectDisposedException) { } catch (Exception ex) { Disconnect(); } } /// /// Removes a disconnected client from the list of clients. /// /// The client to remove. public void RemoveClient(Client client) { lock (_clientsLock) { int index = -1; for (int i = 0; i < _clients.Count; i++) if (_clients[i].Equals(client)) { index = i; break; } if (index < 0) return; try { _clients[index].Disconnect(); _clients.RemoveAt(index); } catch { } } } /// /// Disconnect the server from all of the clients and discontinue /// listening (placing the server in an "off" state). /// public void Disconnect() { if (Processing) return; Processing = true; if (_handle != null) { _handle.Close(); } lock (_clientsLock) { while (_clients.Count != 0) { _clients[0].Disconnect(); try { _clients.RemoveAt(0); } catch { } } } Listening = false; OnServerState(false); } } }