using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Security;
using System.Security.Authentication;
using System.Security.Cryptography.X509Certificates;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using SuperSocket.Common;
using SuperSocket.SocketBase.Command;
using SuperSocket.SocketBase.Config;
using SuperSocket.SocketBase.Logging;
using SuperSocket.SocketBase.Metadata;
using SuperSocket.SocketBase.Protocol;
using SuperSocket.SocketBase.Provider;
using SuperSocket.SocketBase.Security;
namespace SuperSocket.SocketBase
{
///
/// AppServer base class
///
/// The type of the app session.
/// The type of the request info.
[AppServerMetadataType(typeof(DefaultAppServerMetadata))]
public abstract partial class AppServerBase : IAppServer, IRawDataProcessor, IRequestHandler, ISocketServerAccessor, IStatusInfoSource, IRemoteCertificateValidator, IActiveConnector, ISystemEndPoint, IDisposable
where TRequestInfo : class, IRequestInfo
where TAppSession : AppSession, IAppSession, new()
{
///
/// Null appSession instance
///
protected readonly TAppSession NullAppSession = default(TAppSession);
///
/// Gets the server's config.
///
public IServerConfig Config { get; private set; }
//Server instance name
private string m_Name;
///
/// the current state's code
///
private int m_StateCode = ServerStateConst.NotInitialized;
///
/// Gets the current state of the work item.
///
///
/// The state.
///
public ServerState State
{
get
{
return (ServerState)m_StateCode;
}
}
///
/// Gets the certificate of current server.
///
public X509Certificate Certificate { get; private set; }
///
/// Gets or sets the receive filter factory.
///
///
/// The receive filter factory.
///
public virtual IReceiveFilterFactory ReceiveFilterFactory { get; protected set; }
///
/// Gets the Receive filter factory.
///
object IAppServer.ReceiveFilterFactory
{
get { return this.ReceiveFilterFactory; }
}
private List>> m_CommandLoaders = new List>>();
private Dictionary>> m_CommandContainer;
private CommandFilterAttribute[] m_GlobalCommandFilters;
private ISocketServerFactory m_SocketServerFactory;
///
/// Gets the basic transfer layer security protocol.
///
public SslProtocols BasicSecurity { get; private set; }
///
/// Gets the root config.
///
protected IRootConfig RootConfig { get; private set; }
///
/// Gets the logger assosiated with this object.
///
public ILog Logger { get; private set; }
///
/// Gets the bootstrap of this appServer instance.
///
protected IBootstrap Bootstrap { get; private set; }
private static bool m_ThreadPoolConfigured = false;
private List m_ConnectionFilters;
private long m_TotalHandledRequests = 0;
///
/// Gets the total handled requests number.
///
protected long TotalHandledRequests
{
get { return m_TotalHandledRequests; }
}
private ListenerInfo[] m_Listeners;
///
/// Gets or sets the listeners inforamtion.
///
///
/// The listeners.
///
public ListenerInfo[] Listeners
{
get { return m_Listeners; }
}
///
/// Gets the started time of this server instance.
///
///
/// The started time.
///
public DateTime StartedTime { get; private set; }
///
/// Gets or sets the log factory.
///
///
/// The log factory.
///
public ILogFactory LogFactory { get; private set; }
///
/// Gets the default text encoding.
///
///
/// The text encoding.
///
public Encoding TextEncoding { get; private set; }
///
/// Initializes a new instance of the class.
///
public AppServerBase()
{
}
///
/// Initializes a new instance of the class.
///
/// The Receive filter factory.
public AppServerBase(IReceiveFilterFactory receiveFilterFactory)
{
this.ReceiveFilterFactory = receiveFilterFactory;
}
///
/// Gets the filter attributes.
///
/// The type.
///
internal static CommandFilterAttribute[] GetCommandFilterAttributes(Type type)
{
var attrs = type.GetCustomAttributes(true);
return attrs.OfType().ToArray();
}
///
/// Setups the command into command dictionary
///
/// The discovered commands.
///
protected virtual bool SetupCommands(Dictionary> discoveredCommands)
{
foreach (var loader in m_CommandLoaders)
{
loader.Error += new EventHandler(CommandLoaderOnError);
loader.Updated += new EventHandler>>(CommandLoaderOnCommandsUpdated);
if (!loader.Initialize(RootConfig, this))
{
if (Logger.IsErrorEnabled)
Logger.ErrorFormat("Failed initialize the command loader {0}.", loader.ToString());
return false;
}
IEnumerable> commands;
if (!loader.TryLoadCommands(out commands))
{
if (Logger.IsErrorEnabled)
Logger.ErrorFormat("Failed load commands from the command loader {0}.", loader.ToString());
return false;
}
if (commands != null && commands.Any())
{
foreach (var c in commands)
{
if (discoveredCommands.ContainsKey(c.Name))
{
if (Logger.IsErrorEnabled)
Logger.Error("Duplicated name command has been found! Command name: " + c.Name);
return false;
}
var castedCommand = c as ICommand;
if (castedCommand == null)
{
if (Logger.IsErrorEnabled)
Logger.Error("Invalid command has been found! Command name: " + c.Name);
return false;
}
if (Logger.IsDebugEnabled)
Logger.DebugFormat("The command {0}({1}) has been discovered", castedCommand.Name, castedCommand.ToString());
discoveredCommands.Add(c.Name, castedCommand);
}
}
}
return true;
}
void CommandLoaderOnCommandsUpdated(object sender, CommandUpdateEventArgs> e)
{
var workingDict = m_CommandContainer.Values.ToDictionary(c => c.Command.Name, c => c.Command, StringComparer.OrdinalIgnoreCase);
var updatedCommands = 0;
foreach (var c in e.Commands)
{
if (c == null)
continue;
if (c.UpdateAction == CommandUpdateAction.Remove)
{
workingDict.Remove(c.Command.Name);
if (Logger.IsInfoEnabled)
Logger.InfoFormat("The command '{0}' has been removed from this server!", c.Command.Name);
}
else if (c.UpdateAction == CommandUpdateAction.Add)
{
workingDict.Add(c.Command.Name, c.Command);
if (Logger.IsInfoEnabled)
Logger.InfoFormat("The command '{0}' has been added into this server!", c.Command.Name);
}
else
{
workingDict[c.Command.Name] = c.Command;
if (Logger.IsInfoEnabled)
Logger.InfoFormat("The command '{0}' has been updated!", c.Command.Name);
}
updatedCommands++;
}
if (updatedCommands > 0)
{
OnCommandSetup(workingDict);
}
}
void CommandLoaderOnError(object sender, ErrorEventArgs e)
{
if (!Logger.IsErrorEnabled)
return;
Logger.Error(e.Exception);
}
///
/// Setups the specified root config.
///
/// The root config.
/// The config.
///
protected virtual bool Setup(IRootConfig rootConfig, IServerConfig config)
{
return true;
}
partial void SetDefaultCulture(IRootConfig rootConfig, IServerConfig config);
private void SetupBasic(IRootConfig rootConfig, IServerConfig config, ISocketServerFactory socketServerFactory)
{
if (rootConfig == null)
throw new ArgumentNullException("rootConfig");
RootConfig = rootConfig;
if (config == null)
throw new ArgumentNullException("config");
if (!string.IsNullOrEmpty(config.Name))
m_Name = config.Name;
else
m_Name = string.Format("{0}-{1}", this.GetType().Name, Math.Abs(this.GetHashCode()));
Config = config;
SetDefaultCulture(rootConfig, config);
if (!m_ThreadPoolConfigured)
{
if (!TheadPoolEx.ResetThreadPool(rootConfig.MaxWorkingThreads >= 0 ? rootConfig.MaxWorkingThreads : new Nullable(),
rootConfig.MaxCompletionPortThreads >= 0 ? rootConfig.MaxCompletionPortThreads : new Nullable(),
rootConfig.MinWorkingThreads >= 0 ? rootConfig.MinWorkingThreads : new Nullable(),
rootConfig.MinCompletionPortThreads >= 0 ? rootConfig.MinCompletionPortThreads : new Nullable()))
{
throw new Exception("Failed to configure thread pool!");
}
m_ThreadPoolConfigured = true;
}
if (socketServerFactory == null)
{
var socketServerFactoryType =
Type.GetType("SuperSocket.SocketEngine.SocketServerFactory, SuperSocket.SocketEngine", true);
socketServerFactory = (ISocketServerFactory)Activator.CreateInstance(socketServerFactoryType);
}
m_SocketServerFactory = socketServerFactory;
//Read text encoding from the configuration
if (!string.IsNullOrEmpty(config.TextEncoding))
TextEncoding = Encoding.GetEncoding(config.TextEncoding);
else
TextEncoding = new ASCIIEncoding();
}
private bool SetupMedium(IReceiveFilterFactory receiveFilterFactory, IEnumerable connectionFilters, IEnumerable>> commandLoaders)
{
if (receiveFilterFactory != null)
ReceiveFilterFactory = receiveFilterFactory;
if (connectionFilters != null && connectionFilters.Any())
{
if (m_ConnectionFilters == null)
m_ConnectionFilters = new List();
m_ConnectionFilters.AddRange(connectionFilters);
}
if (commandLoaders != null && commandLoaders.Any())
m_CommandLoaders.AddRange(commandLoaders);
return SetupCommandLoaders(m_CommandLoaders);
}
private bool SetupAdvanced(IServerConfig config)
{
if (!SetupSecurity(config))
return false;
if (!SetupListeners(config))
return false;
m_GlobalCommandFilters = GetCommandFilterAttributes(this.GetType());
var discoveredCommands = new Dictionary>(StringComparer.OrdinalIgnoreCase);
if (!SetupCommands(discoveredCommands))
return false;
OnCommandSetup(discoveredCommands);
return true;
}
private void OnCommandSetup(IDictionary> discoveredCommands)
{
var commandContainer = new Dictionary>>(StringComparer.OrdinalIgnoreCase);
foreach (var command in discoveredCommands.Values)
{
commandContainer.Add(command.Name,
new CommandInfo>(command, m_GlobalCommandFilters));
}
Interlocked.Exchange(ref m_CommandContainer, commandContainer);
}
internal abstract IReceiveFilterFactory CreateDefaultReceiveFilterFactory();
private bool SetupFinal()
{
//Check receiveFilterFactory
if (ReceiveFilterFactory == null)
{
ReceiveFilterFactory = CreateDefaultReceiveFilterFactory();
if (ReceiveFilterFactory == null)
{
if (Logger.IsErrorEnabled)
Logger.Error("receiveFilterFactory is required!");
return false;
}
}
var plainConfig = Config as ServerConfig;
if (plainConfig == null)
{
//Using plain config model instead of .NET configuration element to improve performance
plainConfig = new ServerConfig(Config);
if (string.IsNullOrEmpty(plainConfig.Name))
plainConfig.Name = Name;
Config = plainConfig;
}
try
{
m_ServerStatus = new StatusInfoCollection();
m_ServerStatus.Name = Name;
m_ServerStatus.Tag = Name;
m_ServerStatus[StatusInfoKeys.MaxConnectionNumber] = Config.MaxConnectionNumber;
m_ServerStatus[StatusInfoKeys.Listeners] = m_Listeners;
}
catch (Exception e)
{
if (Logger.IsErrorEnabled)
Logger.Error("Failed to create ServerSummary instance!", e);
return false;
}
return SetupSocketServer();
}
///
/// Setups with the specified port.
///
/// The port.
/// return setup result
public bool Setup(int port)
{
return Setup("Any", port);
}
private void TrySetInitializedState()
{
if (Interlocked.CompareExchange(ref m_StateCode, ServerStateConst.Initializing, ServerStateConst.NotInitialized)
!= ServerStateConst.NotInitialized)
{
throw new Exception("The server has been initialized already, you cannot initialize it again!");
}
}
#if NET_35
///
/// Setups with the specified ip and port.
///
/// The ip.
/// The port.
/// The providers.
///
public bool Setup(string ip, int port, params object[] providers)
{
return Setup(new ServerConfig
{
Name = string.Format("{0}-{1}", this.GetType().Name, Math.Abs(this.GetHashCode())),
Ip = ip,
Port = port
}, providers);
}
///
/// Setups with the specified config, used for programming setup
///
/// The server config.
/// The providers.
///
public bool Setup(IServerConfig config, params object[] providers)
{
return Setup(new RootConfig(), config, providers);
}
///
/// Setups with the specified root config, used for programming setup
///
/// The root config.
/// The server config.
/// The providers.
///
public bool Setup(IRootConfig rootConfig, IServerConfig config, params object[] providers)
{
TrySetInitializedState();
SetupBasic(rootConfig, config, GetProviderInstance(providers));
if (!SetupLogFactory(GetProviderInstance(providers)))
return false;
Logger = CreateLogger(this.Name);
if (!SetupMedium(GetProviderInstance>(providers), GetProviderInstance>(providers), GetProviderInstance>>>(providers)))
return false;
if (!SetupAdvanced(config))
return false;
if (!Setup(rootConfig, config))
return false;
if(!SetupFinal())
return false;
m_StateCode = ServerStateConst.NotStarted;
return true;
}
private T GetProviderInstance(object[] providers)
{
if (providers == null || !providers.Any())
return default(T);
var providerType = typeof(T);
return (T)providers.FirstOrDefault(p => p != null && providerType.IsAssignableFrom(p.GetType()));
}
#else
///
/// Setups with the specified config.
///
/// The server config.
/// The socket server factory.
/// The receive filter factory.
/// The log factory.
/// The connection filters.
/// The command loaders.
///
public bool Setup(IServerConfig config, ISocketServerFactory socketServerFactory = null, IReceiveFilterFactory receiveFilterFactory = null, ILogFactory logFactory = null, IEnumerable connectionFilters = null, IEnumerable>> commandLoaders = null)
{
return Setup(new RootConfig(), config, socketServerFactory, receiveFilterFactory, logFactory, connectionFilters, commandLoaders);
}
///
/// Setups the specified root config, this method used for programming setup
///
/// The root config.
/// The server config.
/// The socket server factory.
/// The Receive filter factory.
/// The log factory.
/// The connection filters.
/// The command loaders.
///
public bool Setup(IRootConfig rootConfig, IServerConfig config, ISocketServerFactory socketServerFactory = null, IReceiveFilterFactory receiveFilterFactory = null, ILogFactory logFactory = null, IEnumerable connectionFilters = null, IEnumerable>> commandLoaders = null)
{
TrySetInitializedState();
SetupBasic(rootConfig, config, socketServerFactory);
if (!SetupLogFactory(logFactory))
return false;
Logger = CreateLogger(this.Name);
if (!SetupMedium(receiveFilterFactory, connectionFilters, commandLoaders))
return false;
if (!SetupAdvanced(config))
return false;
if (!Setup(rootConfig, config))
return false;
if (!SetupFinal())
return false;
m_StateCode = ServerStateConst.NotStarted;
return true;
}
///
/// Setups with the specified ip and port.
///
/// The ip.
/// The port.
/// The socket server factory.
/// The Receive filter factory.
/// The log factory.
/// The connection filters.
/// The command loaders.
/// return setup result
public bool Setup(string ip, int port, ISocketServerFactory socketServerFactory = null, IReceiveFilterFactory receiveFilterFactory = null, ILogFactory logFactory = null, IEnumerable connectionFilters = null, IEnumerable>> commandLoaders = null)
{
return Setup(new ServerConfig
{
Ip = ip,
Port = port
},
socketServerFactory,
receiveFilterFactory,
logFactory,
connectionFilters,
commandLoaders);
}
#endif
///
/// Setups the specified root config.
///
/// The bootstrap.
/// The socket server instance config.
/// The factories.
///
bool IWorkItem.Setup(IBootstrap bootstrap, IServerConfig config, ProviderFactoryInfo[] factories)
{
if (bootstrap == null)
throw new ArgumentNullException("bootstrap");
Bootstrap = bootstrap;
if (factories == null)
throw new ArgumentNullException("factories");
TrySetInitializedState();
var rootConfig = bootstrap.Config;
SetupBasic(rootConfig, config, GetSingleProviderInstance(factories, ProviderKey.SocketServerFactory));
if (!SetupLogFactory(GetSingleProviderInstance(factories, ProviderKey.LogFactory)))
return false;
Logger = CreateLogger(this.Name);
IEnumerable connectionFilters = null;
if (!TryGetProviderInstances(factories, ProviderKey.ConnectionFilter, null,
(p, f) =>
{
var ret = p.Initialize(f.Name, this);
if(!ret)
{
Logger.ErrorFormat("Failed to initialize the connection filter: {0}.", f.Name);
}
return ret;
}, out connectionFilters))
{
return false;
}
if (!SetupMedium(
GetSingleProviderInstance>(factories, ProviderKey.ReceiveFilterFactory),
connectionFilters,
GetProviderInstances>>(
factories,
ProviderKey.CommandLoader,
(t) => Activator.CreateInstance(t.MakeGenericType(typeof(ICommand))))))
{
return false;
}
if (!SetupAdvanced(config))
return false;
if (!Setup(rootConfig, config))
return false;
if (!SetupFinal())
return false;
m_StateCode = ServerStateConst.NotStarted;
return true;
}
private TProvider GetSingleProviderInstance(ProviderFactoryInfo[] factories, ProviderKey key)
{
var factory = factories.FirstOrDefault(p => p.Key.Name == key.Name);
if (factory == null)
return default(TProvider);
return factory.ExportFactory.CreateExport();
}
private bool TryGetProviderInstances(ProviderFactoryInfo[] factories, ProviderKey key, Func creator, Func initializer, out IEnumerable providers)
where TProvider : class
{
IEnumerable selectedFactories = factories.Where(p => p.Key.Name == key.Name);
if (!selectedFactories.Any())
{
providers = null;
return true;
}
providers = new List();
var list = (List)providers;
foreach (var f in selectedFactories)
{
var provider = creator == null ? f.ExportFactory.CreateExport() : f.ExportFactory.CreateExport(creator);
if (!initializer(provider, f))
return false;
list.Add(provider);
}
return true;
}
private IEnumerable GetProviderInstances(ProviderFactoryInfo[] factories, ProviderKey key)
where TProvider : class
{
return GetProviderInstances(factories, key, null);
}
private IEnumerable GetProviderInstances(ProviderFactoryInfo[] factories, ProviderKey key, Func creator)
where TProvider : class
{
IEnumerable providers;
TryGetProviderInstances(factories, key, creator, (p, f) => true, out providers);
return providers;
}
private bool SetupLogFactory(ILogFactory logFactory)
{
if (logFactory != null)
{
LogFactory = logFactory;
return true;
}
//Log4NetLogFactory is default log factory
if (LogFactory == null)
LogFactory = new Log4NetLogFactory();
return true;
}
///
/// Setups the command loaders.
///
/// The command loaders.
///
protected virtual bool SetupCommandLoaders(List>> commandLoaders)
{
commandLoaders.Add(new ReflectCommandLoader>());
return true;
}
///
/// Creates the logger for the AppServer.
///
/// Name of the logger.
///
protected virtual ILog CreateLogger(string loggerName)
{
return LogFactory.GetLog(loggerName);
}
///
/// Setups the security option of socket communications.
///
/// The config of the server instance.
///
private bool SetupSecurity(IServerConfig config)
{
if (!string.IsNullOrEmpty(config.Security))
{
SslProtocols configProtocol;
if (!config.Security.TryParseEnum(true, out configProtocol))
{
if (Logger.IsErrorEnabled)
Logger.ErrorFormat("Failed to parse '{0}' to SslProtocol!", config.Security);
return false;
}
BasicSecurity = configProtocol;
}
else
{
BasicSecurity = SslProtocols.None;
}
try
{
var certificate = GetCertificate(config.Certificate);
if (certificate != null)
{
Certificate = certificate;
}
else if(BasicSecurity != SslProtocols.None)
{
if (Logger.IsErrorEnabled)
Logger.Error("Certificate is required in this security mode!");
return false;
}
}
catch (Exception e)
{
if (Logger.IsErrorEnabled)
Logger.Error("Failed to initialize certificate!", e);
return false;
}
return true;
}
///
/// Gets the certificate from server configuguration.
///
/// The certificate config.
///
protected virtual X509Certificate GetCertificate(ICertificateConfig certificate)
{
if (certificate == null)
{
if (BasicSecurity != SslProtocols.None && Logger.IsErrorEnabled)
Logger.Error("There is no certificate configured!");
return null;
}
if (string.IsNullOrEmpty(certificate.FilePath) && string.IsNullOrEmpty(certificate.Thumbprint))
{
if (BasicSecurity != SslProtocols.None && Logger.IsErrorEnabled)
Logger.Error("You should define certificate node and either attribute 'filePath' or 'thumbprint' is required!");
return null;
}
return CertificateManager.Initialize(certificate, GetFilePath);
}
bool IRemoteCertificateValidator.Validate(IAppSession session, object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors)
{
return ValidateClientCertificate((TAppSession)session, sender, certificate, chain, sslPolicyErrors);
}
///
/// Validates the client certificate. This method is only used if the certificate configuration attribute "clientCertificateRequired" is true.
///
/// The session.
/// The sender.
/// The certificate.
/// The chain.
/// The SSL policy errors.
/// return the validation result
protected virtual bool ValidateClientCertificate(TAppSession session, object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors)
{
return sslPolicyErrors == SslPolicyErrors.None;
}
///
/// Setups the socket server.instance
///
///
private bool SetupSocketServer()
{
try
{
m_SocketServer = m_SocketServerFactory.CreateSocketServer(this, m_Listeners, Config);
return m_SocketServer != null;
}
catch (Exception e)
{
if (Logger.IsErrorEnabled)
Logger.Error(e);
return false;
}
}
private IPAddress ParseIPAddress(string ip)
{
if (string.IsNullOrEmpty(ip) || "Any".Equals(ip, StringComparison.OrdinalIgnoreCase))
return IPAddress.Any;
else if ("IPv6Any".Equals(ip, StringComparison.OrdinalIgnoreCase))
return IPAddress.IPv6Any;
else
return IPAddress.Parse(ip);
}
///
/// Setups the listeners base on server configuration
///
/// The config.
///
private bool SetupListeners(IServerConfig config)
{
var listeners = new List();
try
{
if (config.Port > 0)
{
listeners.Add(new ListenerInfo
{
EndPoint = new IPEndPoint(ParseIPAddress(config.Ip), config.Port),
BackLog = config.ListenBacklog,
Security = BasicSecurity
});
}
else
{
//Port is not configured, but ip is configured
if (!string.IsNullOrEmpty(config.Ip))
{
if (Logger.IsErrorEnabled)
Logger.Error("Port is required in config!");
return false;
}
}
//There are listener defined
if (config.Listeners != null && config.Listeners.Any())
{
//But ip and port were configured in server node
//We don't allow this case
if (listeners.Any())
{
if (Logger.IsErrorEnabled)
Logger.Error("If you configured Ip and Port in server node, you cannot defined listener in listeners node any more!");
return false;
}
foreach (var l in config.Listeners)
{
SslProtocols configProtocol;
if (string.IsNullOrEmpty(l.Security))
{
configProtocol = BasicSecurity;
}
else if (!l.Security.TryParseEnum(true, out configProtocol))
{
if (Logger.IsErrorEnabled)
Logger.ErrorFormat("Failed to parse '{0}' to SslProtocol!", config.Security);
return false;
}
if (configProtocol != SslProtocols.None && (Certificate == null))
{
if (Logger.IsErrorEnabled)
Logger.Error("There is no certificate loaded, but there is a secure listener defined!");
return false;
}
listeners.Add(new ListenerInfo
{
EndPoint = new IPEndPoint(ParseIPAddress(l.Ip), l.Port),
BackLog = l.Backlog,
Security = configProtocol
});
}
}
if (!listeners.Any())
{
if (Logger.IsErrorEnabled)
Logger.Error("No listener defined!");
return false;
}
m_Listeners = listeners.ToArray();
return true;
}
catch (Exception e)
{
if (Logger.IsErrorEnabled)
Logger.Error(e);
return false;
}
}
///
/// Gets the name of the server instance.
///
public string Name
{
get { return m_Name; }
}
private ISocketServer m_SocketServer;
///
/// Gets the socket server.
///
///
/// The socket server.
///
ISocketServer ISocketServerAccessor.SocketServer
{
get { return m_SocketServer; }
}
///
/// Starts this server instance.
///
///
/// return true if start successfull, else false
///
public virtual bool Start()
{
var origStateCode = Interlocked.CompareExchange(ref m_StateCode, ServerStateConst.Starting, ServerStateConst.NotStarted);
if (origStateCode != ServerStateConst.NotStarted)
{
if (origStateCode < ServerStateConst.NotStarted)
throw new Exception("You cannot start a server instance which has not been setup yet.");
if (Logger.IsErrorEnabled)
Logger.ErrorFormat("This server instance is in the state {0}, you cannot start it now.", (ServerState)origStateCode);
return false;
}
if (!m_SocketServer.Start())
{
m_StateCode = ServerStateConst.NotStarted;
return false;
}
StartedTime = DateTime.Now;
m_StateCode = ServerStateConst.Running;
m_ServerStatus[StatusInfoKeys.IsRunning] = true;
m_ServerStatus[StatusInfoKeys.StartedTime] = StartedTime;
try
{
//Will be removed in the next version
#pragma warning disable 0612, 618
OnStartup();
#pragma warning restore 0612, 618
OnStarted();
}
catch (Exception e)
{
if (Logger.IsErrorEnabled)
{
Logger.Error("One exception wa thrown in the method 'OnStartup()'.", e);
}
}
finally
{
if (Logger.IsInfoEnabled)
Logger.Info(string.Format("The server instance {0} has been started!", Name));
}
return true;
}
///
/// Called when [startup].
///
[Obsolete("Use OnStarted() instead")]
protected virtual void OnStartup()
{
}
///
/// Called when [started].
///
protected virtual void OnStarted()
{
}
///
/// Called when [stopped].
///
protected virtual void OnStopped()
{
}
///
/// Stops this server instance.
///
public virtual void Stop()
{
if (Interlocked.CompareExchange(ref m_StateCode, ServerStateConst.Stopping, ServerStateConst.Running)
!= ServerStateConst.Running)
{
return;
}
m_SocketServer.Stop();
m_StateCode = ServerStateConst.NotStarted;
OnStopped();
m_ServerStatus[StatusInfoKeys.IsRunning] = false;
m_ServerStatus[StatusInfoKeys.StartedTime] = null;
if (Logger.IsInfoEnabled)
Logger.Info(string.Format("The server instance {0} has been stopped!", Name));
}
///
/// Gets command by command name.
///
/// Name of the command.
///
private CommandInfo> GetCommandByName(string commandName)
{
CommandInfo> commandProxy;
if (m_CommandContainer.TryGetValue(commandName, out commandProxy))
return commandProxy;
else
return null;
}
private Func m_RawDataReceivedHandler;
///
/// Gets or sets the raw binary data received event handler.
/// TAppSession: session
/// byte[]: receive buffer
/// int: receive buffer offset
/// int: receive lenght
/// bool: whether process the received data further
///
event Func IRawDataProcessor.RawDataReceived
{
add { m_RawDataReceivedHandler += value; }
remove { m_RawDataReceivedHandler -= value; }
}
///
/// Called when [raw data received].
///
/// The session.
/// The buffer.
/// The offset.
/// The length.
internal bool OnRawDataReceived(IAppSession session, byte[] buffer, int offset, int length)
{
var handler = m_RawDataReceivedHandler;
if (handler == null)
return true;
return handler((TAppSession)session, buffer, offset, length);
}
private RequestHandler m_RequestHandler;
///
/// Occurs when a full request item received.
///
public virtual event RequestHandler NewRequestReceived
{
add { m_RequestHandler += value; }
remove { m_RequestHandler -= value; }
}
///
/// Executes the command.
///
/// The session.
/// The request info.
protected virtual void ExecuteCommand(TAppSession session, TRequestInfo requestInfo)
{
if (m_RequestHandler == null)
{
var commandProxy = GetCommandByName(requestInfo.Key);
if (commandProxy != null)
{
var command = commandProxy.Command;
var commandFilters = commandProxy.Filters;
session.CurrentCommand = requestInfo.Key;
var cancelled = false;
if (commandFilters == null)
{
command.ExecuteCommand(session, requestInfo);
}
else
{
var commandContext = new CommandExecutingContext();
commandContext.Initialize(session, requestInfo, command);
for (var i = 0; i < commandFilters.Length; i++)
{
var filter = commandFilters[i];
filter.OnCommandExecuting(commandContext);
if (commandContext.Cancel)
{
cancelled = true;
if(Logger.IsInfoEnabled)
Logger.Info(session, string.Format("The executing of the command {0} was cancelled by the command filter {1}.", command.Name, filter.GetType().ToString()));
break;
}
}
if (!cancelled)
{
try
{
command.ExecuteCommand(session, requestInfo);
}
catch (Exception exc)
{
commandContext.Exception = exc;
}
for (var i = 0; i < commandFilters.Length; i++)
{
var filter = commandFilters[i];
filter.OnCommandExecuted(commandContext);
}
if (commandContext.Exception != null && !commandContext.ExceptionHandled)
{
try
{
session.InternalHandleExcetion(commandContext.Exception);
}
catch
{
}
}
}
}
if(!cancelled)
{
session.PrevCommand = requestInfo.Key;
if (Config.LogCommand && Logger.IsInfoEnabled)
Logger.Info(session, string.Format("Command - {0}", requestInfo.Key));
}
}
else
{
session.InternalHandleUnknownRequest(requestInfo);
}
session.LastActiveTime = DateTime.Now;
}
else
{
session.CurrentCommand = requestInfo.Key;
try
{
m_RequestHandler(session, requestInfo);
}
catch (Exception e)
{
session.InternalHandleExcetion(e);
}
session.PrevCommand = requestInfo.Key;
session.LastActiveTime = DateTime.Now;
if (Config.LogCommand && Logger.IsInfoEnabled)
Logger.Info(session, string.Format("Command - {0}", requestInfo.Key));
}
Interlocked.Increment(ref m_TotalHandledRequests);
}
///
/// Executes the command for the session.
///
/// The session.
/// The request info.
internal void ExecuteCommand(IAppSession session, TRequestInfo requestInfo)
{
this.ExecuteCommand((TAppSession)session, requestInfo);
}
///
/// Executes the command.
///
/// The session.
/// The request info.
void IRequestHandler.ExecuteCommand(IAppSession session, TRequestInfo requestInfo)
{
this.ExecuteCommand((TAppSession)session, requestInfo);
}
///
/// Gets or sets the server's connection filter
///
///
/// The server's connection filters
///
public IEnumerable ConnectionFilters
{
get { return m_ConnectionFilters; }
}
///
/// Executes the connection filters.
///
/// The remote address.
///
private bool ExecuteConnectionFilters(IPEndPoint remoteAddress)
{
if (m_ConnectionFilters == null)
return true;
for (var i = 0; i < m_ConnectionFilters.Count; i++)
{
var currentFilter = m_ConnectionFilters[i];
if (!currentFilter.AllowConnect(remoteAddress))
{
if (Logger.IsInfoEnabled)
Logger.InfoFormat("A connection from {0} has been refused by filter {1}!", remoteAddress, currentFilter.Name);
return false;
}
}
return true;
}
///
/// Creates the app session.
///
/// The socket session.
///
IAppSession IAppServer.CreateAppSession(ISocketSession socketSession)
{
if (!ExecuteConnectionFilters(socketSession.RemoteEndPoint))
return NullAppSession;
var appSession = CreateAppSession(socketSession);
appSession.Initialize(this, socketSession);
return appSession;
}
///
/// create a new TAppSession instance, you can override it to create the session instance in your own way
///
/// the socket session.
/// the new created session instance
protected virtual TAppSession CreateAppSession(ISocketSession socketSession)
{
return new TAppSession();
}
///
/// Registers the new created app session into the appserver's session container.
///
/// The session.
///
bool IAppServer.RegisterSession(IAppSession session)
{
var appSession = session as TAppSession;
if (!RegisterSession(appSession.SessionID, appSession))
return false;
appSession.SocketSession.Closed += OnSocketSessionClosed;
if (Config.LogBasicSessionActivity && Logger.IsInfoEnabled)
Logger.Info(session, "A new session connected! ");
OnNewSessionConnected(appSession);
return true;
}
///
/// Registers the session into session container.
///
/// The session ID.
/// The app session.
///
protected virtual bool RegisterSession(string sessionID, TAppSession appSession)
{
return true;
}
private SessionHandler m_NewSessionConnected;
///
/// The action which will be executed after a new session connect
///
public event SessionHandler NewSessionConnected
{
add { m_NewSessionConnected += value; }
remove { m_NewSessionConnected -= value; }
}
///
/// Called when [new session connected].
///
/// The session.
protected virtual void OnNewSessionConnected(TAppSession session)
{
var handler = m_NewSessionConnected;
if (handler == null)
return;
handler.BeginInvoke(session, OnNewSessionConnectedCallback, handler);
}
private void OnNewSessionConnectedCallback(IAsyncResult result)
{
try
{
var handler = (SessionHandler)result.AsyncState;
handler.EndInvoke(result);
}
catch (Exception e)
{
Logger.Error(e);
}
}
///
/// Resets the session's security protocol.
///
/// The session.
/// The security protocol.
public void ResetSessionSecurity(IAppSession session, SslProtocols security)
{
m_SocketServer.ResetSessionSecurity(session, security);
}
///
/// Called when [socket session closed].
///
/// The socket session.
/// The reason.
private void OnSocketSessionClosed(ISocketSession session, CloseReason reason)
{
//Even if LogBasicSessionActivity is false, we also log the unexpected closing because the close reason probably be useful
if (Logger.IsInfoEnabled && (Config.LogBasicSessionActivity || (reason != CloseReason.ServerClosing && reason != CloseReason.ClientClosing && reason != CloseReason.ServerShutdown && reason != CloseReason.SocketError)))
Logger.Info(session, string.Format("This session was closed for {0}!", reason));
var appSession = session.AppSession as TAppSession;
appSession.Connected = false;
OnSessionClosed(appSession, reason);
}
private SessionHandler m_SessionClosed;
///
/// Gets/sets the session closed event handler.
///
public event SessionHandler SessionClosed
{
add { m_SessionClosed += value; }
remove { m_SessionClosed -= value; }
}
///
/// Called when [session closed].
///
/// The appSession.
/// The reason.
protected virtual void OnSessionClosed(TAppSession session, CloseReason reason)
{
var handler = m_SessionClosed;
if (handler != null)
{
handler.BeginInvoke(session, reason, OnSessionClosedCallback, handler);
}
session.OnSessionClosed(reason);
}
private void OnSessionClosedCallback(IAsyncResult result)
{
try
{
var handler = (SessionHandler)result.AsyncState;
handler.EndInvoke(result);
}
catch (Exception e)
{
Logger.Error(e);
}
}
///
/// Gets the app session by ID.
///
/// The session ID.
///
public abstract TAppSession GetSessionByID(string sessionID);
///
/// Gets the app session by ID.
///
///
///
IAppSession IAppServer.GetSessionByID(string sessionID)
{
return this.GetSessionByID(sessionID);
}
///
/// Gets the matched sessions from sessions snapshot.
///
/// The prediction critera.
public virtual IEnumerable GetSessions(Func critera)
{
throw new NotSupportedException();
}
///
/// Gets all sessions in sessions snapshot.
///
public virtual IEnumerable GetAllSessions()
{
throw new NotSupportedException();
}
///
/// Gets the total session count.
///
public abstract int SessionCount { get; }
///
/// Gets the physical file path by the relative file path,
/// search both in the appserver's root and in the supersocket root dir if the isolation level has been set other than 'None'.
///
/// The relative file path.
///
public string GetFilePath(string relativeFilePath)
{
var filePath = System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, relativeFilePath);
if (!System.IO.File.Exists(filePath) && RootConfig != null && RootConfig.Isolation != IsolationMode.None)
{
var rootDir = System.IO.Directory.GetParent(AppDomain.CurrentDomain.BaseDirectory).Parent.FullName;
var rootFilePath = System.IO.Path.Combine(rootDir, relativeFilePath);
if (System.IO.File.Exists(rootFilePath))
return rootFilePath;
}
return filePath;
}
#region IActiveConnector
///
/// Connect the remote endpoint actively.
///
/// The target end point.
/// The local end point.
///
/// This server cannot support active connect.
Task IActiveConnector.ActiveConnect(EndPoint targetEndPoint, EndPoint localEndPoint)
{
var activeConnector = m_SocketServer as IActiveConnector;
if (activeConnector == null)
throw new Exception("This server cannot support active connect.");
return activeConnector.ActiveConnect(targetEndPoint, localEndPoint);
}
///
/// Connect the remote endpoint actively.
///
/// The target end point.
///
/// This server cannot support active connect.
Task IActiveConnector.ActiveConnect(EndPoint targetEndPoint)
{
return ((IActiveConnector)this).ActiveConnect(targetEndPoint, null);
}
#endregion IActiveConnector
#region ISystemEndPoint
///
/// Transfers the system message
///
/// Type of the message.
/// The message data.
void ISystemEndPoint.TransferSystemMessage(string messageType, object messageData)
{
OnSystemMessageReceived(messageType, messageData);
}
///
/// Called when [system message received].
///
/// Type of the message.
/// The message data.
protected virtual void OnSystemMessageReceived(string messageType, object messageData)
{
}
#endregion ISystemEndPoint
#region IStatusInfoSource
private StatusInfoCollection m_ServerStatus;
StatusInfoAttribute[] IStatusInfoSource.GetServerStatusMetadata()
{
return this.GetType().GetStatusInfoMetadata();
}
StatusInfoCollection IStatusInfoSource.CollectServerStatus(StatusInfoCollection bootstrapStatus)
{
UpdateServerStatus(m_ServerStatus);
this.AsyncRun(() => OnServerStatusCollected(bootstrapStatus, m_ServerStatus), e => Logger.Error(e));
return m_ServerStatus;
}
///
/// Updates the summary of the server.
///
/// The server status.
protected virtual void UpdateServerStatus(StatusInfoCollection serverStatus)
{
DateTime now = DateTime.Now;
serverStatus[StatusInfoKeys.IsRunning] = m_StateCode == ServerStateConst.Running;
serverStatus[StatusInfoKeys.TotalConnections] = this.SessionCount;
var totalHandledRequests0 = serverStatus.GetValue(StatusInfoKeys.TotalHandledRequests, 0);
var totalHandledRequests = this.TotalHandledRequests;
serverStatus[StatusInfoKeys.RequestHandlingSpeed] = ((totalHandledRequests - totalHandledRequests0) / now.Subtract(serverStatus.CollectedTime).TotalSeconds);
serverStatus[StatusInfoKeys.TotalHandledRequests] = totalHandledRequests;
if (State == ServerState.Running)
{
var sendingQueuePool = m_SocketServer.SendingQueuePool;
serverStatus[StatusInfoKeys.AvialableSendingQueueItems] = sendingQueuePool.AvialableItemsCount;
serverStatus[StatusInfoKeys.TotalSendingQueueItems] = sendingQueuePool.TotalItemsCount;
}
else
{
serverStatus[StatusInfoKeys.AvialableSendingQueueItems] = 0;
serverStatus[StatusInfoKeys.TotalSendingQueueItems] = 0;
}
serverStatus.CollectedTime = now;
}
///
/// Called when [server status collected].
///
/// The bootstrapStatus status.
/// The server status.
protected virtual void OnServerStatusCollected(StatusInfoCollection bootstrapStatus, StatusInfoCollection serverStatus)
{
}
#endregion IStatusInfoSource
#region IDisposable Members
///
/// Releases unmanaged and - optionally - managed resources
///
public void Dispose()
{
if (m_StateCode == ServerStateConst.Running)
Stop();
}
#endregion
}
}