Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Net;
- using System.Net.Sockets;
- using System.Threading;
- using CrystalNetwork.Shared;
- namespace CrystalNetwork.Server
- {
- public class BaseServer
- {
- public static Thread MainThread;
- /// <summary>
- /// Throw exceptions?
- /// </summary>
- public static bool DebugMode = false;
- public string Hostname
- {
- get { return _hostname; }
- set
- {
- var oldname = _hostname;
- _hostname = value;
- OnHostnameChanged(oldname, _hostname);
- }
- }
- public IPAddress Address { get; protected set; }
- public uint MaxClients
- {
- get { return _maxclients; }
- set
- {
- var oldval = _maxclients;
- _maxclients = value;
- OnMaxClientsChanged(oldval, _maxclients);
- }
- }
- public uint ClientsOnline
- {
- get { return _online; }
- set
- {
- var oldval = _online;
- _online = value;
- OnOnlineClientsChanged(oldval, _online);
- }
- }
- public ushort Port { get; protected set; }
- public BaseProtocol Protocol { get; set; }
- public MessageManager Log { get; protected set; }
- protected Socket SocketListen { get; set; }
- protected Thread ThreadListen { get; set; }
- protected Thread ThreadTick { get; set; }
- public bool ShouldRun { get; set; }
- public bool IsRuning
- {
- get { return _isrunning; }
- protected set
- {
- if (_isrunning != value)
- {
- _isrunning = !_isrunning;
- OnRunningStateChanged();
- }
- }
- }
- /// <summary>
- /// Number of connecting clients queue
- /// </summary>
- public uint Backlog { get; protected set; }
- public List<BaseClientConnection> Clients { get; protected set; }
- protected Dictionary<int, ClientAnswerHandler> AnswerHandlers { get; set; }
- public event Action EV_LAUNCH;
- public event Action EV_STOP;
- public event Action<string, string> EV_HOSTNAME_CHANGED;
- public event Action<uint, uint> EV_MAXCLIENTS_CHANGED;
- public event Action EV_RUNNINGSTATE_CHANGED;
- public event Action EV_ONLINE_CLIENTS_CHANGED;
- protected virtual void OnServerLaunch()
- {
- var handler = EV_LAUNCH;
- if (handler != null) handler();
- }
- protected virtual void OnServerStop()
- {
- var handler = EV_STOP;
- if (handler != null) handler();
- }
- protected virtual void OnHostnameChanged(string prev, string @new)
- {
- var handler = EV_HOSTNAME_CHANGED;
- if (handler != null) handler(prev, @new);
- }
- protected virtual void OnMaxClientsChanged(uint prev, uint @new)
- {
- var handler = EV_MAXCLIENTS_CHANGED;
- if (handler != null) handler(prev, @new);
- }
- protected virtual void OnRunningStateChanged()
- {
- var handler = EV_RUNNINGSTATE_CHANGED;
- if (handler != null) handler();
- }
- protected virtual void OnOnlineClientsChanged(uint oldval, uint online)
- {
- var handler = EV_ONLINE_CLIENTS_CHANGED;
- if (handler != null) handler();
- }
- private bool _isrunning;
- private bool _initialized;
- private string _hostname = "";
- private uint _maxclients = 32;
- private uint _online = 0;
- public BaseServer()
- {
- MainThread = Thread.CurrentThread;
- Log = new MessageManager();
- Hostname = "Basic CrystalNetwork Server";
- ShouldRun = true;
- IsRuning = false;
- Backlog = 64;
- Clients = new List<BaseClientConnection>();
- AnswerHandlers = new Dictionary<int, ClientAnswerHandler>();
- }
- /// <summary>
- /// Initializes a server, also starts it, if ShouldRun == true
- /// </summary>
- /// <param name="ip">IP to listen</param>
- /// <param name="port">Port to bind to</param>
- /// <returns>Was an operation successful?</returns>
- public bool Start(IPAddress ip = null, ushort port = 27015)
- {
- Address = ip ?? IPAddress.Any;
- Port = port;
- try
- {
- SocketListen = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
- SocketListen.Bind(new IPEndPoint(Address, port));
- }
- catch (Exception ex)
- {
- Log.PostFrom(
- LogMessageType.CriticalError,
- "Could not bind to {0}:{1} - {2}",
- "BasicServer.Start",
- Address.ToString(), Port, ex.Message
- );
- if (DebugMode)
- throw;
- return false;
- }
- Log.Post(
- LogMessageType.Info,
- "Successfully binded to {0}:{1}",
- Address.ToString(), Port
- );
- _initialized = true;
- if (ShouldRun) Run();
- return true;
- }
- /// <summary>
- /// If the server is initialized, starts listening loop
- /// </summary>
- public void Run()
- {
- if (!_initialized)
- {
- Log.PostFrom(
- LogMessageType.Error,
- "Failed to run - server is not initialized by BaseServer.Start()",
- "BaseServer.Run");
- return;
- }
- if (IsRuning)
- {
- Log.PostFrom(
- LogMessageType.Error,
- "Failed to run - server is already running",
- "BaseServer.Run");
- return;
- }
- ShouldRun = true;
- SocketListen.Listen((int)Backlog);
- ThreadListen = new Thread(AcceptLoop);
- ThreadListen.Start();
- OnServerLaunch();
- ThreadTick = new Thread(ServerTick);
- ThreadTick.Start();
- }
- public void ServerTick()
- {
- while (IsRuning || ShouldRun)
- {
- for (var i = 0; i < Clients.Count; i++)
- {
- if (Clients[i] != null && (!Clients[i].IsActive || !Clients[i].ShouldBeActive))
- {
- Kick((uint)i);
- }
- }
- Thread.Sleep(1000);
- }
- }
- /// <summary>
- /// Main listening loop, accepts clients from SocketListen
- /// </summary>
- public void AcceptLoop()
- {
- if (!_initialized)
- {
- Log.PostFrom(
- LogMessageType.Error,
- "Failed to start accept loop - server is not initialized by BaseServer.Start()",
- "BaseServer.AcceptLoop");
- return;
- }
- IsRuning = true;
- Log.Post(LogMessageType.Info, "Listening started!");
- while (ShouldRun)
- {
- try
- {
- var sockNew = SocketListen.Accept();
- var remEP = sockNew.RemoteEndPoint as IPEndPoint;
- Log.Post(LogMessageType.Info,
- "New incoming connection from '{0}:{1}'",
- remEP.Address, remEP.Port);
- if (Protocol != null && Protocol.FuncServerNewConnection != null)
- Protocol.FuncServerNewConnection(this, sockNew);
- }
- catch (Exception ex)
- {
- if (ex is ThreadAbortException)
- return;
- if (ex is SocketException)
- {
- var ec = (ex as SocketException).SocketErrorCode;
- if (ec == SocketError.Interrupted)
- break;
- Log.PostFrom(LogMessageType.Debug,
- "Socket exception {2} - ({1}) {0}",
- "BaseServer.AcceptLoop", ex.Message, ex, ec);
- return;
- }
- Log.PostFrom(LogMessageType.CriticalError,
- "Failed to accept new client connection - ({1}) {0}",
- "BaseServer.AcceptLoop", ex.Message, ex);
- if (DebugMode)
- throw;
- }
- }
- IsRuning = false;
- }
- public void ShutDown()
- {
- if (!IsRuning || ThreadListen == null)
- {
- Log.PostFrom(
- LogMessageType.Error,
- "Failed to shut the server down - server is not running",
- "BaseServer.ShutDown");
- return;
- }
- if (!_initialized)
- {
- Log.PostFrom(
- LogMessageType.Error,
- "Failed to shut the server down - server is not initialized by BaseServer.Start()",
- "BaseServer.ShutDown");
- return;
- }
- IsRuning = false;
- ShouldRun = false;
- SocketListen.Close();
- ThreadTick.Abort();
- ThreadTick.Join(500);
- ThreadTick = null;
- ThreadListen.Abort();
- ThreadListen.Join(500);
- ThreadListen = null;
- for (uint i = 0; i < Clients.Count; i++)
- {
- if (Clients[(int)i] != null)
- Kick(i);
- }
- Clients.Clear();
- Log.Post(LogMessageType.Info, "Server has been stopped!");
- OnServerStop();
- }
- public bool ReStart()
- {
- ShutDown();
- ShouldRun = true;
- return Start(Address, Port);
- }
- public void OnClientDisconnected(uint clid)
- {
- Log.PostFrom(LogMessageType.Debug,
- "client [{0}] - stoping client...",
- "BaseServer.OnClientDisconnected()", clid);
- Clients[(int)clid].Stop();
- Clients[(int)clid] = null;
- ClientsOnline--;
- Log.PostInfo("Client [{0}] disconnected.", clid);
- }
- public void Kick(uint clid)
- {
- Log.PostFrom(LogMessageType.Debug,
- "client [{0}] - kicking client...",
- "BaseServer.Kick()", clid);
- Clients[(int)clid].Stop();
- Clients[(int)clid] = null;
- ClientsOnline--;
- Log.PostInfo("Kicked client [{0}].", clid);
- }
- public bool UnregisterAnswerHandler(int key)
- {
- if (key == 0)
- return true;
- if (AnswerHandlers.ContainsKey(key))
- {
- AnswerHandlers.Remove(key);
- return true;
- }
- return false;
- }
- public bool RegisterAnswerHandler(int key, DProcessRequestResultsServer h)
- {
- if (key == 0 || h == null)
- return true;
- if (AnswerHandlers.Count > 256)
- {
- var lst = (from cah in AnswerHandlers where cah.Value.IsExpired select cah.Key).ToList();
- foreach (var i in lst)
- {
- AnswerHandlers.Remove(i);
- }
- }
- ClientAnswerHandler proc;
- if (AnswerHandlers.TryGetValue(key, out proc))
- {
- if (proc.IsExpired)
- {
- Log.Post(LogMessageType.Error,
- "Registration of packet({1}) failed - previous request has not been yet answered",
- key);
- return false;
- }
- proc.Expires = DateTime.Now.AddSeconds(5);
- proc.Func = h;
- }
- else
- {
- var nh = new ClientAnswerHandler
- {
- Expires = DateTime.Now.AddSeconds(5),
- Func = h
- };
- AnswerHandlers.Add(key, nh);
- }
- return true;
- }
- public SocketError SendPacket(uint clid, DataPacket p, DProcessRequestResultsServer handler = null)
- {
- if (clid < Clients.Count || Clients[(int)clid] == null )
- {
- Log.Post(LogMessageType.Error, "Cannot send packet to client[{0}] - client id is invalid!", clid);
- return SocketError.SocketError;
- }
- if (Protocol == null)
- {
- Log.Post(LogMessageType.CriticalError, "Cannot send packet to client[{0}] - no protocol specified!", clid);
- return SocketError.SocketError;
- }
- if (p.DataLength > Protocol.MaxPacketLength)
- {
- Log.Post(LogMessageType.CriticalError,
- "Cannot send packet to client[{0}] - packet is too big ({1} vs {2} bytes allowed by protocol)",
- clid, p.DataLength, Protocol.MaxPacketLength);
- return SocketError.SocketError;
- }
- var client = Clients[(int) clid];
- if (!client.ShouldBeActive || !client.IsActive || client.Socket == null)
- {
- Log.Post(LogMessageType.Error,
- "Failed to send packet to client[{0}] - client is now disconnecting",
- clid);
- client.ShouldBeActive = false;
- return SocketError.SocketError;
- }
- if (!RegisterAnswerHandler(p.Key, handler))
- return SocketError.SocketError;
- SocketError err;
- client.Socket.Send(p.GetBytes(), 0, DataPacket.HeaderLength, SocketFlags.None, out err);
- if (err != SocketError.Success)
- {
- Log.Post(LogMessageType.Error,
- "Failed to send packet to client[{0}] - SocketError.{1}",
- clid, err);
- }
- return err;
- }
- }
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement