using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using xServer.Core.Packets;
namespace xServer.Core
{
public class Server
{
///
/// 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 amount of currently connected and authenticated clients.
///
public int ConnectedAndAuthenticatedClients { get; set; }
///
/// 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);
}
}
///
/// Handle of the Server Socket.
///
private Socket _handle;
///
/// The event to accept new connections asynchronously.
///
private SocketAsyncEventArgs _item;
///
/// Gets or sets if the server is currently processing data that should prevent disconnection.
///
public bool Processing { get; private set; }
///
/// The listening state of the server. True if listening, else False.
///
public bool Listening { get; private set; }
///
/// List of the clients connected to the server.
///
private List _clients;
///
/// Lock object for the list of clients.
///
private readonly object _clientsLock = new object();
///
/// 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];
}
}
}
///
/// A collection containing all clients that have ever connected to the server.
///
public Dictionary AllTimeConnectedClients { get; set; }
///
/// 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();
AllTimeConnectedClients = new Dictionary();
}
///
/// 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 += Process;
if (_handle != null)
{
try
{
_handle.Close();
}
catch
{
}
}
_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))
Process(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 Process(object s, SocketAsyncEventArgs e)
{
try
{
if (e.SocketError == SocketError.Success)
{
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);
}
e.AcceptSocket = null;
if (!_handle.AcceptAsync(e))
Process(null, e);
}
else
Disconnect();
}
catch (Exception ex)
{
Disconnect();
}
}
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);
}
}
}