diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 000000000..1ff0c4230 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,63 @@ +############################################################################### +# Set default behavior to automatically normalize line endings. +############################################################################### +* text=auto + +############################################################################### +# Set default behavior for command prompt diff. +# +# This is need for earlier builds of msysgit that does not have it on by +# default for csharp files. +# Note: This is only used by command line +############################################################################### +#*.cs diff=csharp + +############################################################################### +# Set the merge driver for project and solution files +# +# Merging from the command prompt will add diff markers to the files if there +# are conflicts (Merging from VS is not affected by the settings below, in VS +# the diff markers are never inserted). Diff markers may cause the following +# file extensions to fail to load in VS. An alternative would be to treat +# these files as binary and thus will always conflict and require user +# intervention with every merge. To do so, just uncomment the entries below +############################################################################### +#*.sln merge=binary +#*.csproj merge=binary +#*.vbproj merge=binary +#*.vcxproj merge=binary +#*.vcproj merge=binary +#*.dbproj merge=binary +#*.fsproj merge=binary +#*.lsproj merge=binary +#*.wixproj merge=binary +#*.modelproj merge=binary +#*.sqlproj merge=binary +#*.wwaproj merge=binary + +############################################################################### +# behavior for image files +# +# image files are treated as binary by default. +############################################################################### +#*.jpg binary +#*.png binary +#*.gif binary + +############################################################################### +# diff behavior for common document formats +# +# Convert binary document formats to text before diffing them. This feature +# is only available from the command line. Turn it on by uncommenting the +# entries below. +############################################################################### +#*.doc diff=astextplain +#*.DOC diff=astextplain +#*.docx diff=astextplain +#*.DOCX diff=astextplain +#*.dot diff=astextplain +#*.DOT diff=astextplain +#*.pdf diff=astextplain +#*.PDF diff=astextplain +#*.rtf diff=astextplain +#*.RTF diff=astextplain diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 000000000..6f537fcea --- /dev/null +++ b/.travis.yml @@ -0,0 +1,5 @@ +language: csharp +script: + - xbuild SuperSocket.2013.sln + - xbuild SuperSocket.2013.NET40.sln + - xbuild SuperSocket.2010.NET35.sln \ No newline at end of file diff --git a/Agent/Program.cs b/Agent/Program.cs index 5592c2510..483709f70 100644 --- a/Agent/Program.cs +++ b/Agent/Program.cs @@ -1,10 +1,11 @@ using System; using System.Collections.Generic; using System.Diagnostics; +using System.Reflection; using System.Runtime.Remoting; using System.Runtime.Remoting.Channels; using System.Runtime.Remoting.Channels.Ipc; -using System.Reflection; +using System.Runtime.Serialization.Formatters; using System.Text; using System.Threading; using SuperSocket.SocketBase; @@ -65,7 +66,7 @@ static void Main(string[] args) try { - var serverChannel = new IpcServerChannel("IpcAgent", channelPort); + var serverChannel = new IpcServerChannel("IpcAgent", channelPort, new BinaryServerFormatterSinkProvider { TypeFilterLevel = TypeFilterLevel.Full }); var clientChannel = new IpcClientChannel(); ChannelServices.RegisterChannel(serverChannel, false); ChannelServices.RegisterChannel(clientChannel, false); diff --git a/Agent/WorkItemAgent.cs b/Agent/WorkItemAgent.cs index 890b70b3e..444aa5480 100644 --- a/Agent/WorkItemAgent.cs +++ b/Agent/WorkItemAgent.cs @@ -3,6 +3,7 @@ using System.Linq; using System.Runtime.Remoting.Lifetime; using System.Text; +using SuperSocket.Common; using SuperSocket.SocketBase; using SuperSocket.SocketBase.Config; using SuperSocket.SocketBase.Logging; @@ -33,10 +34,15 @@ public WorkItemAgent() } - public bool Setup(string serverType, string bootstrapUri, string assemblyImportRoot, IServerConfig config, ProviderFactoryInfo[] factories) + public bool Setup(string serverType, string bootstrapUri, string assemblyImportRoot, IServerConfig config, ProviderFactoryInfo[] factories, string startupConfigFile) { m_AssemblyImporter = new AssemblyImport(assemblyImportRoot); + if (!string.IsNullOrEmpty(startupConfigFile)) + { + AppDomain.CurrentDomain.ResetConfiguration(startupConfigFile); + } + var serviceType = Type.GetType(serverType); m_AppServer = (IWorkItem)Activator.CreateInstance(serviceType); @@ -78,6 +84,15 @@ public void Stop() m_AppServer.Stop(); } + /// + /// Reports the potential configuration change. + /// + /// The server config which may be changed. + public void ReportPotentialConfigChange(IServerConfig config) + { + m_AppServer.ReportPotentialConfigChange(config); + } + /// /// Gets the session count. /// @@ -131,5 +146,17 @@ public void TransferSystemMessage(string messageType, object messageData) { m_AppServer.TransferSystemMessage(messageType, messageData); } + + + /// + /// Gets the server's config. + /// + /// + /// The server's config. + /// + public IServerConfig Config + { + get { return m_AppServer.Config; } + } } } diff --git a/Build.Net45.bat b/Build.Net45.bat deleted file mode 100644 index 206aaef02..000000000 --- a/Build.Net45.bat +++ /dev/null @@ -1,19 +0,0 @@ -@echo off - -set fdir=%WINDIR%\Microsoft.NET\Framework64 - -if not exist %fdir% ( - set fdir=%WINDIR%\Microsoft.NET\Framework -) - -set msbuild=%fdir%\v4.0.30319\msbuild.exe - -%msbuild% SuperSocket.2012.sln /p:Configuration=Debug /t:Clean;Rebuild /p:OutputPath=..\bin\Net45\Debug -FOR /F "tokens=*" %%G IN ('DIR /B /AD /S obj') DO RMDIR /S /Q "%%G" - -%msbuild% SuperSocket.2012.sln /p:Configuration=Release /t:Clean;Rebuild /p:OutputPath=..\bin\Net45\Release -FOR /F "tokens=*" %%G IN ('DIR /B /AD /S obj') DO RMDIR /S /Q "%%G" - - - -pause \ No newline at end of file diff --git a/Build.bat b/Build.bat index d238f7f17..fe28a6f79 100644 --- a/Build.bat +++ b/Build.bat @@ -20,4 +20,13 @@ FOR /F "tokens=*" %%G IN ('DIR /B /AD /S obj') DO RMDIR /S /Q "%%G" %msbuild% SuperSocket.2010.NET35.sln /p:Configuration=Release /t:Clean;Rebuild /p:OutputPath=..\bin\Net35\Release FOR /F "tokens=*" %%G IN ('DIR /B /AD /S obj') DO RMDIR /S /Q "%%G" +reg query "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\.NETFramework\v4.0.30319\SKUs\.NETFramework,Version=v4.5" 2>nul +if errorlevel 0 ( + %msbuild% SuperSocket.2013.sln /p:Configuration=Debug /t:Clean;Rebuild /p:OutputPath=..\bin\Net45\Debug + FOR /F "tokens=*" %%G IN ('DIR /B /AD /S obj') DO RMDIR /S /Q "%%G" + + %msbuild% SuperSocket.2013.sln /p:Configuration=Release /t:Clean;Rebuild /p:OutputPath=..\bin\Net45\Release + FOR /F "tokens=*" %%G IN ('DIR /B /AD /S obj') DO RMDIR /S /Q "%%G" +) + pause \ No newline at end of file diff --git a/BuildServerManager.bat b/BuildServerManager.bat new file mode 100644 index 000000000..831932c72 --- /dev/null +++ b/BuildServerManager.bat @@ -0,0 +1,32 @@ +@echo off + +set fdir=%WINDIR%\Microsoft.NET\Framework64 + +if not exist %fdir% ( + set fdir=%WINDIR%\Microsoft.NET\Framework +) + +set msbuild=%fdir%\v4.0.30319\msbuild.exe + +%msbuild% Management\Server\SuperSocket.ServerManager.Net40.csproj /p:Configuration=Debug /t:Clean;Rebuild /p:OutputPath=..\..\bin\Net40\Debug +FOR /F "tokens=*" %%G IN ('DIR /B /AD /S obj') DO RMDIR /S /Q "%%G" + +%msbuild% Management\Server\SuperSocket.ServerManager.Net40.csproj /p:Configuration=Release /t:Clean;Rebuild /p:OutputPath=..\..\bin\Net40\Release +FOR /F "tokens=*" %%G IN ('DIR /B /AD /S obj') DO RMDIR /S /Q "%%G" + +%msbuild% Management\Server\SuperSocket.ServerManager.Net35.csproj /p:Configuration=Debug /t:Clean;Rebuild /p:OutputPath=..\..\bin\Net35\Debug +FOR /F "tokens=*" %%G IN ('DIR /B /AD /S obj') DO RMDIR /S /Q "%%G" + +%msbuild% Management\Server\SuperSocket.ServerManager.Net35.csproj /p:Configuration=Release /t:Clean;Rebuild /p:OutputPath=..\..\bin\Net35\Release +FOR /F "tokens=*" %%G IN ('DIR /B /AD /S obj') DO RMDIR /S /Q "%%G" + +reg query "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\.NETFramework\v4.0.30319\SKUs\.NETFramework,Version=v4.5" 2>nul +if errorlevel 0 ( + %msbuild% Management\Server\SuperSocket.ServerManager.Net45.csproj /p:Configuration=Debug /t:Clean;Rebuild /p:OutputPath=..\..\bin\Net45\Debug + FOR /F "tokens=*" %%G IN ('DIR /B /AD /S obj') DO RMDIR /S /Q "%%G" + + %msbuild% Management\Server\SuperSocket.ServerManager.Net45.csproj /p:Configuration=Release /t:Clean;Rebuild /p:OutputPath=..\..\bin\Net45\Release + FOR /F "tokens=*" %%G IN ('DIR /B /AD /S obj') DO RMDIR /S /Q "%%G" +) + +pause \ No newline at end of file diff --git a/Common/AssemblyUtil.cs b/Common/AssemblyUtil.cs index 04746dab5..1fa32f69b 100644 --- a/Common/AssemblyUtil.cs +++ b/Common/AssemblyUtil.cs @@ -187,19 +187,38 @@ public static T BinaryClone(this T target) } #endif - private static object[] m_EmptyObjectArray = new object[] { }; /// /// Copies the properties of one object to another object. /// + /// /// The source. /// The target. + /// public static T CopyPropertiesTo(this T source, T target) { - PropertyInfo[] properties = source.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.GetProperty); + return source.CopyPropertiesTo(p => true, target); + } + + /// + /// Copies the properties of one object to another object. + /// + /// + /// The source. + /// The properties predict. + /// The target. + /// + public static T CopyPropertiesTo(this T source, Predicate predict, T target) + { + PropertyInfo[] properties = source.GetType() + .GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.GetProperty); + Dictionary sourcePropertiesDict = properties.ToDictionary(p => p.Name); - PropertyInfo[] targetProperties = target.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.GetProperty); + PropertyInfo[] targetProperties = target.GetType() + .GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.SetProperty) + .Where(p => predict(p)).ToArray(); + for (int i = 0; i < targetProperties.Length; i++) { var p = targetProperties[i]; @@ -213,7 +232,7 @@ public static T CopyPropertiesTo(this T source, T target) if (!sourceProperty.PropertyType.IsSerializable) continue; - p.SetValue(target, sourceProperty.GetValue(source, m_EmptyObjectArray), m_EmptyObjectArray); + p.SetValue(target, sourceProperty.GetValue(source, null), null); } } diff --git a/Common/BinaryUtil.cs b/Common/BinaryUtil.cs index 11458d0a8..87c65013d 100644 --- a/Common/BinaryUtil.cs +++ b/Common/BinaryUtil.cs @@ -32,6 +32,20 @@ public static int IndexOf(this IList source, T target, int pos, int length return -1; } + /// + /// Searches the mark from source. + /// + /// + /// The source. + /// The mark. + /// Length of the parsed. + /// + public static int? SearchMark(this IList source, T[] mark, out int parsedLength) + where T : IEquatable + { + return SearchMark(source, 0, source.Count, mark, 0, out parsedLength); + } + /// /// Searches the mark from source. /// @@ -42,7 +56,8 @@ public static int IndexOf(this IList source, T target, int pos, int length public static int? SearchMark(this IList source, T[] mark) where T : IEquatable { - return SearchMark(source, 0, source.Count, mark, 0); + int parsedLength; + return SearchMark(source, 0, source.Count, mark, 0, out parsedLength); } /// @@ -57,7 +72,24 @@ public static int IndexOf(this IList source, T target, int pos, int length public static int? SearchMark(this IList source, int offset, int length, T[] mark) where T : IEquatable { - return SearchMark(source, offset, length, mark, 0); + int parsedLength; + return SearchMark(source, offset, length, mark, 0, out parsedLength); + } + + /// + /// Searches the mark from source. + /// + /// + /// The source. + /// The offset. + /// The length. + /// The mark. + /// Length of the parsed. + /// + public static int? SearchMark(this IList source, int offset, int length, T[] mark, out int parsedLength) + where T : IEquatable + { + return SearchMark(source, offset, length, mark, 0, out parsedLength); } /// @@ -72,10 +104,29 @@ public static int IndexOf(this IList source, T target, int pos, int length /// public static int? SearchMark(this IList source, int offset, int length, T[] mark, int matched) where T : IEquatable + { + int parsedLength; + return source.SearchMark(offset, length, mark, matched, out parsedLength); + } + + /// + /// Searches the mark from source. + /// + /// + /// The source. + /// The offset. + /// The length. + /// The mark. + /// The matched. + /// Length of the parsed. + /// + public static int? SearchMark(this IList source, int offset, int length, T[] mark, int matched, out int parsedLength) + where T : IEquatable { int pos = offset; int endOffset = offset + length - 1; int matchCount = matched; + parsedLength = 0; if (matched > 0) { @@ -89,14 +140,22 @@ public static int IndexOf(this IList source, T target, int pos, int length if (pos > endOffset) { if (matchCount == mark.Length) + { + parsedLength = mark.Length - matched; return offset; + } else + { return (0 - matchCount); + } } } if (matchCount == mark.Length) + { + parsedLength = mark.Length - matched; return offset; + } pos = offset; matchCount = 0; @@ -127,8 +186,12 @@ public static int IndexOf(this IList source, T target, int pos, int length matchCount++; } + //found the full end mark if (matchCount == mark.Length) + { + parsedLength = pos - offset + mark.Length; return pos; + } //Reset next round read pos pos += 1; @@ -145,11 +208,12 @@ public static int IndexOf(this IList source, T target, int pos, int length /// The offset. /// The length. /// State of the search. + /// Length of the parsed. /// - public static int SearchMark(this IList source, int offset, int length, SearchMarkState searchState) + public static int SearchMark(this IList source, int offset, int length, SearchMarkState searchState, out int parsedLength) where T : IEquatable { - int? result = source.SearchMark(offset, length, searchState.Mark, searchState.Matched); + int? result = source.SearchMark(offset, length, searchState.Mark, searchState.Matched, out parsedLength); if (!result.HasValue) { @@ -167,6 +231,22 @@ public static int SearchMark(this IList source, int offset, int length, Se return result.Value; } + /// + /// Searches the mark from source. + /// + /// + /// The source. + /// The offset. + /// The length. + /// State of the search. + /// + public static int SearchMark(this IList source, int offset, int length, SearchMarkState searchState) + where T : IEquatable + { + var parsedLen = 0; + return SearchMark(source, offset, length, searchState, out parsedLen); + } + /// /// Startses the with. /// diff --git a/Common/ConfigurationElementBase.cs b/Common/ConfigurationElementBase.cs index 7bf21c9ea..21daa1d11 100644 --- a/Common/ConfigurationElementBase.cs +++ b/Common/ConfigurationElementBase.cs @@ -60,7 +60,7 @@ protected override void DeserializeElement(XmlReader reader, bool serializeColle /// /// Gets the options. /// - public NameValueCollection Options { get; private set; } + public NameValueCollection Options { get; set; } /// /// Gets a value indicating whether an unknown attribute is encountered during deserialization. @@ -76,10 +76,71 @@ protected override bool OnDeserializeUnrecognizedAttribute(string name, string v return true; } + /// + /// Modifies the object to remove all values that should not be saved. + /// + /// A at the current level containing a merged view of the properties. + /// The parent , or null if this is the top level. + /// A that determines which property values to include. + protected override void Unmerge(ConfigurationElement sourceElement, ConfigurationElement parentElement, ConfigurationSaveMode saveMode) + { + base.Unmerge(sourceElement, parentElement, saveMode); + + var element = sourceElement as ConfigurationElementBase; + + if (element == null) + return; + + if (element.Options != this.Options) + this.Options = element.Options; + + if (element.OptionElements != this.OptionElements) + this.OptionElements = element.OptionElements; + } + + /// + /// Writes the contents of this configuration element to the configuration file when implemented in a derived class. + /// + /// The that writes to the configuration file. + /// true to serialize only the collection key properties; otherwise, false. + /// + /// true if any data was actually serialized; otherwise, false. + /// + protected override bool SerializeElement(XmlWriter writer, bool serializeCollectionKey) + { + if (!base.SerializeElement(writer, serializeCollectionKey)) + return false; + + if (writer == null) + return true; + + var options = Options; + + if (options != null && options.Count > 0) + { + for (var i = 0; i < options.Count; i++) + { + writer.WriteAttributeString(options.GetKey(i), options.Get(i)); + } + } + + var optionElements = OptionElements; + + if (optionElements != null && optionElements.Count > 0) + { + for (var i = 0; i < optionElements.Count; i++) + { + writer.WriteRaw(optionElements.Get(i)); + } + } + + return true; + } + /// /// Gets the option elements. /// - public NameValueCollection OptionElements { get; private set; } + public NameValueCollection OptionElements { get; set; } /// /// Gets a value indicating whether an unknown element is encountered during deserialization. diff --git a/Common/ConfigurationExtension.cs b/Common/ConfigurationExtension.cs index 116aae9f3..b220da80f 100644 --- a/Common/ConfigurationExtension.cs +++ b/Common/ConfigurationExtension.cs @@ -80,6 +80,28 @@ public static void Deserialize(this TElement section, XmlReader reader deserializeElementMethod.Invoke(section, new object[] { reader, false }); } + /// + /// Deserializes the child configuration. + /// + /// The type of the configuration. + /// The child configuration string. + /// + public static TConfig DeserializeChildConfig(string childConfig) + where TConfig : ConfigurationElement, new() + { + // removed extra namespace prefix + childConfig = childConfig.Replace("xmlns=\"http://schema.supersocket.net/supersocket\"", string.Empty); + + XmlReader reader = new XmlTextReader(new StringReader(childConfig)); + + var config = new TConfig(); + + reader.Read(); + config.Deserialize(reader); + + return config; + } + /// /// Gets the child config. /// @@ -95,14 +117,7 @@ public static TConfig GetChildConfig(this NameValueCollection childElem if (string.IsNullOrEmpty(childConfig)) return default(TConfig); - XmlReader reader = new XmlTextReader(new StringReader(childConfig)); - - var config = new TConfig(); - - reader.Read(); - config.Deserialize(reader); - - return config; + return DeserializeChildConfig(childConfig); } /// @@ -122,8 +137,153 @@ public static string GetConfigSource(this ConfigurationElement config) if (configProperty == null) return string.Empty; - var configuration = (Configuration)configProperty.GetValue(config, new object[0]); + var configuration = (Configuration)configProperty.GetValue(config, null); return configuration.FilePath; } + + /// + /// Loads configuration element's node information from a model. + /// + /// The config element. + /// The source. + /// Cannot find expected property 'Item' from the type 'ConfigurationElement'. + public static void LoadFrom(this ConfigurationElement configElement, object source) + { + // get index property setter + var indexPropertySetter = configElement.GetType().GetProperties(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.SetProperty) + .FirstOrDefault(p => + { + if (!p.Name.Equals("Item")) + return false; + + var parameters = p.GetIndexParameters(); + + if (parameters == null || parameters.Length != 1) + return false; + + return parameters[0].ParameterType == typeof(string); + }); + + if (indexPropertySetter == null) + throw new Exception("Cannot find expected property 'Item' from the type 'ConfigurationElement'."); + + // source properties + var properties = source.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.GetProperty); + + var targetProperties = configElement.GetType() + .GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.SetProperty) + .ToDictionary(p => p.Name, StringComparer.OrdinalIgnoreCase); + + var emptyObjectArr = new object[0]; + + var writableAttrs = new List>(); + + foreach (var sourceProperty in properties) + { + if (!sourceProperty.PropertyType.IsSerializable) + continue; + + PropertyInfo targetProperty; + + if (targetProperties.TryGetValue(sourceProperty.Name, out targetProperty)) + { + if (targetProperty.CanWrite) + { + writableAttrs.Add(new KeyValuePair(sourceProperty, targetProperty)); + continue; + } + } + + var value = sourceProperty.GetValue(source, emptyObjectArr); + + // lower the first char + var nameChars = sourceProperty.Name.ToArray(); + nameChars[0] = char.ToLower(nameChars[0]); + var propertyName = new string(nameChars); + + indexPropertySetter.SetValue(configElement, value, new object[] { propertyName }); + } + + foreach (var pair in writableAttrs) + { + var value = pair.Key.GetValue(source, emptyObjectArr); + pair.Value.SetValue(configElement, value, emptyObjectArr); + } + } + + /// + /// Gets the current configuration of the configuration element. + /// + /// The current configuration. + /// Configuration element. + public static Configuration GetCurrentConfiguration(this ConfigurationElement configElement) + { + var configElementType = typeof(ConfigurationElement); + + var configProperty = configElementType.GetProperty("CurrentConfiguration", BindingFlags.Instance | BindingFlags.Public); + + if(configProperty == null) + configProperty = configElementType.GetProperty("Configuration", BindingFlags.Instance | BindingFlags.NonPublic); + + if (configProperty == null) + return null; + + return (Configuration)configProperty.GetValue(configElement, null); + } + +#if !NETSTANDARD2_0 + private static void ResetConfigurationForMono(AppDomain appDomain, string configFilePath) + { + appDomain.SetupInformation.ConfigurationFile = configFilePath; + + var configSystem = typeof(ConfigurationManager) + .GetField("configSystem", BindingFlags.Static | BindingFlags.NonPublic) + .GetValue(null); + + // clear previous state + typeof(ConfigurationManager) + .Assembly.GetTypes() + .Where(x => x.FullName == "System.Configuration.ClientConfigurationSystem") + .First() + .GetField("cfg", BindingFlags.Instance | BindingFlags.NonPublic) + .SetValue(configSystem, null); + } + + private static void ResetConfigurationForDotNet(AppDomain appDomain, string configFilePath) + { + appDomain.SetData("APP_CONFIG_FILE", configFilePath); + + // clear previous state + BindingFlags flags = BindingFlags.NonPublic | BindingFlags.Static; + + typeof(ConfigurationManager) + .GetField("s_initState", flags) + .SetValue(null, 0); + + typeof(ConfigurationManager) + .GetField("s_configSystem", flags) + .SetValue(null, null); + + typeof(ConfigurationManager) + .Assembly.GetTypes() + .Where(x => x.FullName == "System.Configuration.ClientConfigPaths") + .First() + .GetField("s_current", flags) + .SetValue(null, null); + } + + /// + /// Reset application's configuration to a another config file + /// + /// the assosiated AppDomain + /// the config file path want to reset to + public static void ResetConfiguration(this AppDomain appDomain, string configFilePath) + { + if (Platform.IsMono) + ResetConfigurationForMono(appDomain, configFilePath); + else + ResetConfigurationForDotNet(appDomain, configFilePath); + } +#endif } } diff --git a/Common/SendingQueue.cs b/Common/SendingQueue.cs index e333a0006..6d0cb5992 100644 --- a/Common/SendingQueue.cs +++ b/Common/SendingQueue.cs @@ -27,6 +27,8 @@ public sealed class SendingQueue : IList> private ushort m_TrackID = 1; + private int m_InnerOffset = 0; + /// /// Gets the track ID. /// @@ -93,7 +95,7 @@ public bool Enqueue(ArraySegment item, ushort trackID) Interlocked.Increment(ref m_UpdatingCount); - while (true) + while (!m_ReadOnly) { bool conflict = false; @@ -127,20 +129,20 @@ public bool Enqueue(IList> items, ushort trackID) bool conflict; - while (true) + while (!m_ReadOnly) { if (TryEnqueue(items, out conflict, trackID)) - break; - - if (!conflict) { Interlocked.Decrement(ref m_UpdatingCount); - return false; + return true; } + + if (!conflict) + break; } Interlocked.Decrement(ref m_UpdatingCount); - return true; + return false; } private bool TryEnqueue(IList> items, out bool conflict, ushort trackID) @@ -250,7 +252,7 @@ public int Capacity /// The number of elements contained in the . public int Count { - get { return m_CurrentCount; } + get { return m_CurrentCount - m_InnerOffset; } } /// @@ -305,7 +307,7 @@ public ArraySegment this[int index] { get { - var targetIndex = m_Offset + index; + var targetIndex = m_Offset + m_InnerOffset + index; var value = m_GlobalQueue[targetIndex]; if (value.Array != null) @@ -358,6 +360,7 @@ public void Clear() } m_CurrentCount = 0; + m_InnerOffset = 0; Position = 0; } @@ -418,7 +421,10 @@ public bool Remove(ArraySegment item) /// public IEnumerator> GetEnumerator() { - throw new NotSupportedException(); + for (var i = 0; i < (m_CurrentCount - m_InnerOffset); i++) + { + yield return m_GlobalQueue[m_Offset + m_InnerOffset + i]; + } } /// @@ -430,7 +436,33 @@ public IEnumerator> GetEnumerator() /// System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { - throw new NotSupportedException(); + return GetEnumerator(); + } + + /// + /// Trim the internal segments at the begining by the binary data size. + /// + /// The binary data size should be trimed at the begining. + public void InternalTrim(int offset) + { + var innerCount = m_CurrentCount - m_InnerOffset; + var subTotal = 0; + + for (var i = m_InnerOffset; i < innerCount; i++) + { + var segment = m_GlobalQueue[m_Offset + i]; + subTotal += segment.Count; + + if (subTotal <= offset) + continue; + + m_InnerOffset = i; + + var rest = subTotal - offset; + m_GlobalQueue[m_Offset + i] = new ArraySegment(segment.Array, segment.Offset + segment.Count - rest, rest); + + break; + } } } diff --git a/Common/SmartPool.cs b/Common/SmartPool.cs index 2b01ea2a2..639456f36 100644 --- a/Common/SmartPool.cs +++ b/Common/SmartPool.cs @@ -332,7 +332,7 @@ private void IncreaseCapacity() var newItemsCount = Math.Min(m_TotalItemsCount, m_MaxPoolSize - m_TotalItemsCount); T[] items; - m_ItemsSource[m_CurrentSourceCount] = m_SourceCreator.Create(newItemsCount, out items); + m_ItemsSource[m_CurrentSourceCount++] = m_SourceCreator.Create(newItemsCount, out items); m_TotalItemsCount += newItemsCount; diff --git a/Common/SocketEx.cs b/Common/SocketEx.cs index 81f0b6281..417d31169 100644 --- a/Common/SocketEx.cs +++ b/Common/SocketEx.cs @@ -17,12 +17,10 @@ public static void SafeClose(this Socket socket) if (socket == null) return; - if (!socket.Connected) - return; - try { - socket.Shutdown(SocketShutdown.Both); + if (socket.Connected) + socket.Shutdown(SocketShutdown.Both); } catch { diff --git a/Common/SuperSocket.Common.Net35.csproj b/Common/SuperSocket.Common.Net35.csproj index fc1854979..98c967939 100644 --- a/Common/SuperSocket.Common.Net35.csproj +++ b/Common/SuperSocket.Common.Net35.csproj @@ -63,10 +63,6 @@ ..\supersocket.snk - - False - ..\Reference\log4net.dll - diff --git a/Common/SuperSocket.Common.Net40.csproj b/Common/SuperSocket.Common.Net40.csproj index 3f66efaea..e9eb1dd14 100644 --- a/Common/SuperSocket.Common.Net40.csproj +++ b/Common/SuperSocket.Common.Net40.csproj @@ -63,9 +63,6 @@ ..\supersocket.snk - - ..\Reference\log4net.dll - diff --git a/Common/SuperSocket.Common.Net45.csproj b/Common/SuperSocket.Common.Net45.csproj index c5c9f8768..b88421402 100644 --- a/Common/SuperSocket.Common.Net45.csproj +++ b/Common/SuperSocket.Common.Net45.csproj @@ -65,9 +65,6 @@ ..\supersocket.snk - - ..\Reference\log4net.dll - diff --git a/Dlr/DynamicCommand.cs b/Dlr/DynamicCommand.cs index beaad8149..d74aa0103 100644 --- a/Dlr/DynamicCommand.cs +++ b/Dlr/DynamicCommand.cs @@ -7,15 +7,18 @@ using SuperSocket.SocketBase; using SuperSocket.SocketBase.Command; using SuperSocket.SocketBase.Protocol; +using SuperSocket.SocketBase.Metadata; namespace SuperSocket.Dlr { - class DynamicCommand : ICommand + class DynamicCommand : ICommand, ICommandFilterProvider where TAppSession : IAppSession, IAppSession, new() where TRequestInfo : IRequestInfo { private Action m_DynamicExecuteCommand; + private IEnumerable m_Filters; + public DynamicCommand(ScriptRuntime scriptRuntime, IScriptSource source) { Source = source; @@ -33,6 +36,11 @@ public DynamicCommand(ScriptRuntime scriptRuntime, IScriptSource source) if (!scriptScope.TryGetVariable>("execute", out dynamicMethod)) throw new Exception("Failed to find a command execution method in source: " + source.Tag); + Func> filtersAction; + + if (scriptScope.TryGetVariable>>("getFilters", out filtersAction)) + m_Filters = filtersAction(); + CompiledTime = DateTime.Now; m_DynamicExecuteCommand = dynamicMethod; @@ -61,5 +69,10 @@ public override string ToString() { return Source.Tag; } + + public IEnumerable GetFilters() + { + return m_Filters; + } } } diff --git a/Facility/Protocol/BeginEndMarkReceiveFilter.cs b/Facility/Protocol/BeginEndMarkReceiveFilter.cs index 2b4d801bd..a45a3a82b 100644 --- a/Facility/Protocol/BeginEndMarkReceiveFilter.cs +++ b/Facility/Protocol/BeginEndMarkReceiveFilter.cs @@ -52,11 +52,12 @@ public override TRequestInfo Filter(byte[] readBuffer, int offset, int length, b //prev macthed begin mark length int prevMatched = 0; + int totalParsed = 0; if (!m_FoundBegin) { prevMatched = m_BeginSearchState.Matched; - int pos = readBuffer.SearchMark(offset, length, m_BeginSearchState); + int pos = readBuffer.SearchMark(offset, length, m_BeginSearchState, out totalParsed); if (pos < 0) { @@ -102,7 +103,8 @@ public override TRequestInfo Filter(byte[] readBuffer, int offset, int length, b while (true) { var prevEndMarkMatched = m_EndSearchState.Matched; - var endPos = readBuffer.SearchMark(searchEndMarkOffset, searchEndMarkLength, m_EndSearchState); + var parsedLen = 0; + var endPos = readBuffer.SearchMark(searchEndMarkOffset, searchEndMarkLength, m_EndSearchState, out parsedLen); //Haven't found end mark if (endPos < 0) @@ -114,11 +116,10 @@ public override TRequestInfo Filter(byte[] readBuffer, int offset, int length, b return NullRequestInfo; } - //Found end mark - int parsedLen = endPos - offset + m_EndSearchState.Mark.Length - prevEndMarkMatched; - rest = length - parsedLen; + totalParsed += parsedLen; + rest = length - totalParsed; - byte[] commandData = new byte[BufferSegments.Count + prevMatched + parsedLen]; + byte[] commandData = new byte[BufferSegments.Count + prevMatched + totalParsed]; if (BufferSegments.Count > 0) BufferSegments.CopyTo(commandData, 0, 0, BufferSegments.Count); @@ -126,7 +127,7 @@ public override TRequestInfo Filter(byte[] readBuffer, int offset, int length, b if(prevMatched > 0) Array.Copy(m_BeginSearchState.Mark, 0, commandData, BufferSegments.Count, prevMatched); - Array.Copy(readBuffer, offset, commandData, BufferSegments.Count + prevMatched, parsedLen); + Array.Copy(readBuffer, offset, commandData, BufferSegments.Count + prevMatched, totalParsed); var requestInfo = ProcessMatchedRequest(commandData, 0, commandData.Length); diff --git a/Facility/Protocol/FixedHeaderReceiveFilter.cs b/Facility/Protocol/FixedHeaderReceiveFilter.cs index 194a859d5..874fc72c7 100644 --- a/Facility/Protocol/FixedHeaderReceiveFilter.cs +++ b/Facility/Protocol/FixedHeaderReceiveFilter.cs @@ -121,7 +121,11 @@ protected override TRequestInfo ProcessMatchedRequest(byte[] buffer, int offset, else m_Header = new ArraySegment(buffer, offset, Size); - return NullRequestInfo; + if (m_BodyLength > 0) + return NullRequestInfo; + + m_FoundHeader = false; + return ResolveRequestInfo(m_Header, null, 0, 0);//Empty body } private TRequestInfo ResolveRequestInfo(ArraySegment header, byte[] bodyBuffer) diff --git a/Management/AgentClient.SL5/Behaviors/SelectRowOnRightClickBehavior.cs b/Management/AgentClient.SL5/Behaviors/SelectRowOnRightClickBehavior.cs index cf108b755..86a75d337 100644 --- a/Management/AgentClient.SL5/Behaviors/SelectRowOnRightClickBehavior.cs +++ b/Management/AgentClient.SL5/Behaviors/SelectRowOnRightClickBehavior.cs @@ -33,18 +33,21 @@ void AssociatedObject_LoadingRow(object sender, DataGridRowEventArgs e) var contextMenu = new ContextMenu(); contextMenu.DataContext = e.Row.DataContext; + + var commands = new []{ + new { h = "Start", c = "StartCommand" }, + new { h = "Stop", c = "StopCommand" }, + new { h = "Restart", c = "RestartCommand" } + }; - var newMenuItem = new MenuItem(); - newMenuItem.Header = "Start"; - newMenuItem.SetBinding(MenuItem.CommandProperty, new Binding("[StartCommand]")); - newMenuItem.SetBinding(MenuItem.CommandParameterProperty, new Binding()); - contextMenu.Items.Add(newMenuItem); - - newMenuItem = new MenuItem(); - newMenuItem.Header = "Stop"; - newMenuItem.SetBinding(MenuItem.CommandProperty, new Binding("[StopCommand]")); - newMenuItem.SetBinding(MenuItem.CommandParameterProperty, new Binding()); - contextMenu.Items.Add(newMenuItem); + foreach (var item in commands) + { + var newMenuItem = new MenuItem(); + newMenuItem.Header = item.h; + newMenuItem.SetBinding(MenuItem.CommandProperty, new Binding("[" + item.c + "]")); + newMenuItem.SetBinding(MenuItem.CommandParameterProperty, new Binding()); + contextMenu.Items.Add(newMenuItem); + } e.Row.MouseRightButtonDown += new MouseButtonEventHandler(Row_MouseRightButtonDown); ContextMenuService.SetContextMenu(e.Row, contextMenu); diff --git a/Management/AgentClient.SL5/SuperSocket.ServerManager.Client.SL5.csproj b/Management/AgentClient.SL5/SuperSocket.ServerManager.Client.SL5.csproj index 41e64fe12..eaab7bf1a 100644 --- a/Management/AgentClient.SL5/SuperSocket.ServerManager.Client.SL5.csproj +++ b/Management/AgentClient.SL5/SuperSocket.ServerManager.Client.SL5.csproj @@ -80,10 +80,12 @@ - - - ..\..\Reference\Blend\sl50\System.Windows.Interactivity + + ..\..\Reference\SilverlightToolkit\50\System.Windows.Controls.Input.Toolkit.dll + + + ..\..\Reference\Blend\sl50\System.Windows.Controls.Input.Toolkit.dll diff --git a/Management/AgentClient.WPF/MainPanel.xaml b/Management/AgentClient.WPF/MainPanel.xaml index fe680a258..d4c8cc3a7 100644 --- a/Management/AgentClient.WPF/MainPanel.xaml +++ b/Management/AgentClient.WPF/MainPanel.xaml @@ -38,16 +38,19 @@ Fill="#FFDACEEC" Height="5" VerticalAlignment="Bottom"> - + - - - - - - - + + + + + + + + diff --git a/Management/AgentClient.WPF/NodeTemplates.xaml b/Management/AgentClient.WPF/NodeTemplates.xaml index ddec14aa1..eef44debe 100644 --- a/Management/AgentClient.WPF/NodeTemplates.xaml +++ b/Management/AgentClient.WPF/NodeTemplates.xaml @@ -30,6 +30,7 @@ + diff --git a/Management/AgentClient.WPF/ViewModel/NodeMasterViewModel.cs b/Management/AgentClient.WPF/ViewModel/NodeMasterViewModel.cs index 9f59e3dae..9651108b7 100644 --- a/Management/AgentClient.WPF/ViewModel/NodeMasterViewModel.cs +++ b/Management/AgentClient.WPF/ViewModel/NodeMasterViewModel.cs @@ -58,7 +58,7 @@ void InitializeWebSocket(NodeConfig config) { m_WebSocket = new AgentWebSocket(config.Uri); } - catch (Exception e) + catch (Exception) { ErrorMessage = "Invalid server URI!"; State = NodeState.Offline; @@ -141,6 +141,7 @@ void OnLoggedIn(dynamic result) { var startCommand = new DelegateCommand(ExecuteStartCommand, CanExecuteStartCommand); var stopCommand = new DelegateCommand(ExecuteStopCommand, CanExecuteStopCommand); + var restartCommand = new DelegateCommand(ExecuteRestartCommand, CanExecuteRestartCommand); i.PropertyChanged += (s, e) => { @@ -149,11 +150,13 @@ void OnLoggedIn(dynamic result) { startCommand.RaiseCanExecuteChanged(); stopCommand.RaiseCanExecuteChanged(); + restartCommand.RaiseCanExecuteChanged(); } }; i.Set("StartCommand", startCommand); i.Set("StopCommand", stopCommand); + i.Set("RestartCommand", restartCommand); return i; })); @@ -199,6 +202,21 @@ private void ExecuteStopCommand(DynamicViewModel.DynamicViewModel target) #endif } + private bool CanExecuteRestartCommand(DynamicViewModel.DynamicViewModel target) + { + var isRunning = ((JValue)((DynamicViewModel.DynamicViewModel)target["Values"])["IsRunning"]).ToString(); + return "True".Equals(isRunning, StringComparison.OrdinalIgnoreCase); + } + + private void ExecuteRestartCommand(DynamicViewModel.DynamicViewModel target) + { +#if SILVERLIGHT + m_WebSocket.Query(CommandName.RESTART, ((JValue)target["Name"]).Value, OnActionCallbackAsync); +#else + m_WebSocket.Query(CommandName.RESTART, ((JValue)target["Name"]).Value, OnActionCallback); +#endif + } + void OnServerUpdated(string result) { dynamic nodeInfo = DynamicViewModelFactory.Create(result); @@ -221,6 +239,7 @@ void OnServerUpdated(dynamic nodeInfo) targetInstance.UpdateProperties(i); ((DelegateCommand)targetInstance["StartCommand"]).RaiseCanExecuteChanged(); ((DelegateCommand)targetInstance["StopCommand"]).RaiseCanExecuteChanged(); + ((DelegateCommand)targetInstance["RestartCommand"]).RaiseCanExecuteChanged(); } } @@ -253,7 +272,7 @@ void WebSocket_Error(object sender, ClientEngine.ErrorEventArgs e) if (e.Exception is SocketException && ((SocketException)e.Exception).ErrorCode == (int)SocketError.AccessDenied) ErrorMessage = (new SocketException((int)SocketError.ConnectionRefused)).Message; else - ErrorMessage = e.Exception.Message; + ErrorMessage = e.Exception.StackTrace; if (m_WebSocket.State == WebSocketState.None && State == NodeState.Connecting) { diff --git a/Management/Server/Command/RESTART.cs b/Management/Server/Command/RESTART.cs new file mode 100644 index 000000000..b9c4006f8 --- /dev/null +++ b/Management/Server/Command/RESTART.cs @@ -0,0 +1,80 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using SuperSocket.Common; +using SuperSocket.ServerManager.Model; +using SuperSocket.SocketBase.Protocol; +using SuperSocket.WebSocket.SubProtocol; +using SuperSocket.SocketBase.Metadata; +using System.Threading; + +namespace SuperSocket.ServerManager.Command +{ + /// + /// Stop command, which is used for stopping AppServer instance + /// + public class RESTART : AsyncJsonSubCommand + { + /// + /// Executes the async json command. + /// + /// The session. + /// The token. + /// The command info. + protected override void ExecuteAsyncJsonCommand(ManagementSession session, string token, string commandInfo) + { + if (!session.LoggedIn) + { + session.Close(); + return; + } + + var instanceName = commandInfo; + + var server = session.AppServer.GetServerByName(instanceName); + + if (server == null) + { + SendJsonMessage(session, token, + new CommandResult + { + Result = false, + Message = string.Format("The server instance \"{0}\" doesn't exist", commandInfo) + }); + return; + } + + if(server.State != SocketBase.ServerState.Running) + { + SendJsonMessage(session, token, + new CommandResult + { + Result = false, + Message = string.Format("The server instance \"{0}\" is not running now, so you needn't restart it. Try start command instead.", commandInfo) + }); + return; + } + + server.Stop(); + + while (server.State != SocketBase.ServerState.NotStarted) + { + Thread.Sleep(10); + //Wating for stop + //TODO:Timeout + } + if (server.Start()) + { + var nodeStatus = session.AppServer.CurrentNodeStatus; + var instance = nodeStatus.InstancesStatus.FirstOrDefault(i => i.Name.Equals(instanceName)); + instance[StatusInfoKeys.IsRunning] = true; + SendJsonMessage(session, token, new CommandResult { Result = true, NodeStatus = nodeStatus }); + } + else + { + SendJsonMessage(session, token, new CommandResult { Result = false, Message = "Application Error" }); + } + } + } +} diff --git a/Management/Server/Command/START.cs b/Management/Server/Command/START.cs index 7c41f62af..a56527b83 100644 --- a/Management/Server/Command/START.cs +++ b/Management/Server/Command/START.cs @@ -44,6 +44,17 @@ protected override void ExecuteAsyncJsonCommand(ManagementSession session, strin return; } + if(server.State != SocketBase.ServerState.NotStarted) + { + SendJsonMessage(session, token, + new CommandResult + { + Result = false, + Message = string.Format("The server instance \"{0}\" is working", commandInfo) + }); + return; + } + if (server.Start()) { var nodeStatus = session.AppServer.CurrentNodeStatus; diff --git a/Management/Server/Command/STOP.cs b/Management/Server/Command/STOP.cs index ff9cae305..ed52ed9d8 100644 --- a/Management/Server/Command/STOP.cs +++ b/Management/Server/Command/STOP.cs @@ -44,6 +44,17 @@ protected override void ExecuteAsyncJsonCommand(ManagementSession session, strin return; } + if(server.State != SocketBase.ServerState.Running) + { + SendJsonMessage(session, token, + new CommandResult + { + Result = false, + Message = string.Format("The server instance \"{0}\" is not running now, so you needn't stop it.", commandInfo) + }); + return; + } + server.Stop(); var nodeStatus = session.AppServer.CurrentNodeStatus; diff --git a/Management/Server/ManagementServer.cs b/Management/Server/ManagementServer.cs index aad6c0477..60b3510bb 100644 --- a/Management/Server/ManagementServer.cs +++ b/Management/Server/ManagementServer.cs @@ -2,7 +2,7 @@ using System.Collections.Generic; using System.Linq; using System.Text; -using Newtonsoft.Json; +using System.Threading; using SuperSocket.Common; using SuperSocket.ServerManager.Config; using SuperSocket.ServerManager.Model; @@ -13,6 +13,7 @@ using SuperSocket.WebSocket; using SuperSocket.WebSocket.Protocol; using SuperSocket.WebSocket.SubProtocol; +using Newtonsoft.Json; namespace SuperSocket.ServerManager { @@ -124,7 +125,7 @@ private void BroadcastServerUpdate() //Only push update to loged in sessions foreach (var s in this.GetSessions(s => s.Connected && s.LoggedIn)) { - s.Send(message); + s.TrySend(message); } } @@ -133,7 +134,22 @@ private void OnServerStatusCollected(object status) var nodeStatus = (NodeStatus)status; nodeStatus.InstancesStatus = nodeStatus.InstancesStatus.Where(s => !m_ExcludedServers.Contains(s.Name)).ToArray(); m_CurrentNodeStatus = nodeStatus; - BroadcastServerUpdate(); + + if (Monitor.TryEnter(m_CurrentNodeStatus)) + { + try + { + BroadcastServerUpdate(); + } + catch(Exception e) + { + Logger.Error("BroadcastServerUpdate error", e); + } + finally + { + Monitor.Exit(m_CurrentNodeStatus); + } + } } protected override void OnSystemMessageReceived(string messageType, object messageData) diff --git a/Management/Server/Model/CommandName.cs b/Management/Server/Model/CommandName.cs index 9d3d9be93..bc43d4358 100644 --- a/Management/Server/Model/CommandName.cs +++ b/Management/Server/Model/CommandName.cs @@ -29,5 +29,10 @@ public class CommandName /// Stop server instance /// public const string STOP = "STOP"; + + /// + /// Restart server instance + /// + public const string RESTART = "RESTART"; } } diff --git a/Management/Server/SuperSocket.ServerManager.Net35.csproj b/Management/Server/SuperSocket.ServerManager.Net35.csproj index e34089248..6f9c24c48 100644 --- a/Management/Server/SuperSocket.ServerManager.Net35.csproj +++ b/Management/Server/SuperSocket.ServerManager.Net35.csproj @@ -48,6 +48,7 @@ GlobalAssemblyInfo.cs + diff --git a/Management/Server/SuperSocket.ServerManager.Net40.csproj b/Management/Server/SuperSocket.ServerManager.Net40.csproj index f4d163056..f37158943 100644 --- a/Management/Server/SuperSocket.ServerManager.Net40.csproj +++ b/Management/Server/SuperSocket.ServerManager.Net40.csproj @@ -45,6 +45,7 @@ GlobalAssemblyInfo.cs + diff --git a/Management/Server/SuperSocket.ServerManager.Net45.csproj b/Management/Server/SuperSocket.ServerManager.Net45.csproj index 0b50a9f17..855893466 100644 --- a/Management/Server/SuperSocket.ServerManager.Net45.csproj +++ b/Management/Server/SuperSocket.ServerManager.Net45.csproj @@ -47,6 +47,7 @@ GlobalAssemblyInfo.cs + diff --git a/Management/SuperSocket.ServerManager.Net45.sln b/Management/SuperSocket.ServerManager.Net45.sln index 374ddfd3a..532c58c10 100644 --- a/Management/SuperSocket.ServerManager.Net45.sln +++ b/Management/SuperSocket.ServerManager.Net45.sln @@ -1,6 +1,8 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 2012 +# Visual Studio 2013 +VisualStudioVersion = 12.0.30723.0 +MinimumVisualStudioVersion = 10.0.40219.1 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{BEAF6CBD-16DD-456B-A889-421E0DFAB783}" ProjectSection(SolutionItems) = preProject ..\Solution Items\GlobalAssemblyInfo.cs = ..\Solution Items\GlobalAssemblyInfo.cs @@ -34,52 +36,122 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SuperSocket.WebSocket.Net45 EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SuperSocket.ServerManager.Net45", "Server\SuperSocket.ServerManager.Net45.csproj", "{49B6DFF1-D7A6-4BDE-90DB-9053416B0AA5}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SuperSocket.ServerManager.Client.SL5", "AgentClient.SL5\SuperSocket.ServerManager.Client.SL5.csproj", "{BF5E608C-6216-41C3-86AA-E6380B0A8888}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SuperSocket.ServerManager.Client.WPF", "AgentClient.WPF\SuperSocket.ServerManager.Client.WPF.csproj", "{5FF240A5-2C62-4322-A0E1-335A04148603}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU + Debug|Mixed Platforms = Debug|Mixed Platforms + Debug|x86 = Debug|x86 Release|Any CPU = Release|Any CPU + Release|Mixed Platforms = Release|Mixed Platforms + Release|x86 = Release|x86 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {A24F4D38-BA9C-4FD6-95B7-4980DE36131A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {A24F4D38-BA9C-4FD6-95B7-4980DE36131A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A24F4D38-BA9C-4FD6-95B7-4980DE36131A}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {A24F4D38-BA9C-4FD6-95B7-4980DE36131A}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {A24F4D38-BA9C-4FD6-95B7-4980DE36131A}.Debug|x86.ActiveCfg = Debug|Any CPU {A24F4D38-BA9C-4FD6-95B7-4980DE36131A}.Release|Any CPU.ActiveCfg = Release|Any CPU {A24F4D38-BA9C-4FD6-95B7-4980DE36131A}.Release|Any CPU.Build.0 = Release|Any CPU + {A24F4D38-BA9C-4FD6-95B7-4980DE36131A}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {A24F4D38-BA9C-4FD6-95B7-4980DE36131A}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {A24F4D38-BA9C-4FD6-95B7-4980DE36131A}.Release|x86.ActiveCfg = Release|Any CPU {40B77789-EA11-4C05-8F52-86711D7BCAAF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {40B77789-EA11-4C05-8F52-86711D7BCAAF}.Debug|Any CPU.Build.0 = Debug|Any CPU + {40B77789-EA11-4C05-8F52-86711D7BCAAF}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {40B77789-EA11-4C05-8F52-86711D7BCAAF}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {40B77789-EA11-4C05-8F52-86711D7BCAAF}.Debug|x86.ActiveCfg = Debug|Any CPU {40B77789-EA11-4C05-8F52-86711D7BCAAF}.Release|Any CPU.ActiveCfg = Release|Any CPU {40B77789-EA11-4C05-8F52-86711D7BCAAF}.Release|Any CPU.Build.0 = Release|Any CPU + {40B77789-EA11-4C05-8F52-86711D7BCAAF}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {40B77789-EA11-4C05-8F52-86711D7BCAAF}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {40B77789-EA11-4C05-8F52-86711D7BCAAF}.Release|x86.ActiveCfg = Release|Any CPU {153FEF72-191C-43D9-BE71-2B351C7AC760}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {153FEF72-191C-43D9-BE71-2B351C7AC760}.Debug|Any CPU.Build.0 = Debug|Any CPU + {153FEF72-191C-43D9-BE71-2B351C7AC760}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {153FEF72-191C-43D9-BE71-2B351C7AC760}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {153FEF72-191C-43D9-BE71-2B351C7AC760}.Debug|x86.ActiveCfg = Debug|Any CPU {153FEF72-191C-43D9-BE71-2B351C7AC760}.Release|Any CPU.ActiveCfg = Release|Any CPU {153FEF72-191C-43D9-BE71-2B351C7AC760}.Release|Any CPU.Build.0 = Release|Any CPU + {153FEF72-191C-43D9-BE71-2B351C7AC760}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {153FEF72-191C-43D9-BE71-2B351C7AC760}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {153FEF72-191C-43D9-BE71-2B351C7AC760}.Release|x86.ActiveCfg = Release|Any CPU {B9113694-7226-4152-938D-3172B11571A1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {B9113694-7226-4152-938D-3172B11571A1}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B9113694-7226-4152-938D-3172B11571A1}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {B9113694-7226-4152-938D-3172B11571A1}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {B9113694-7226-4152-938D-3172B11571A1}.Debug|x86.ActiveCfg = Debug|Any CPU {B9113694-7226-4152-938D-3172B11571A1}.Release|Any CPU.ActiveCfg = Release|Any CPU {B9113694-7226-4152-938D-3172B11571A1}.Release|Any CPU.Build.0 = Release|Any CPU + {B9113694-7226-4152-938D-3172B11571A1}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {B9113694-7226-4152-938D-3172B11571A1}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {B9113694-7226-4152-938D-3172B11571A1}.Release|x86.ActiveCfg = Release|Any CPU {01987BAC-C498-44DD-B274-62EA2506B51D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {01987BAC-C498-44DD-B274-62EA2506B51D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {01987BAC-C498-44DD-B274-62EA2506B51D}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {01987BAC-C498-44DD-B274-62EA2506B51D}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {01987BAC-C498-44DD-B274-62EA2506B51D}.Debug|x86.ActiveCfg = Debug|Any CPU {01987BAC-C498-44DD-B274-62EA2506B51D}.Release|Any CPU.ActiveCfg = Release|Any CPU {01987BAC-C498-44DD-B274-62EA2506B51D}.Release|Any CPU.Build.0 = Release|Any CPU + {01987BAC-C498-44DD-B274-62EA2506B51D}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {01987BAC-C498-44DD-B274-62EA2506B51D}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {01987BAC-C498-44DD-B274-62EA2506B51D}.Release|x86.ActiveCfg = Release|Any CPU {DB6DD6E8-ABDE-4C4A-A3E2-DF49C074433B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {DB6DD6E8-ABDE-4C4A-A3E2-DF49C074433B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {DB6DD6E8-ABDE-4C4A-A3E2-DF49C074433B}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {DB6DD6E8-ABDE-4C4A-A3E2-DF49C074433B}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {DB6DD6E8-ABDE-4C4A-A3E2-DF49C074433B}.Debug|x86.ActiveCfg = Debug|Any CPU {DB6DD6E8-ABDE-4C4A-A3E2-DF49C074433B}.Release|Any CPU.ActiveCfg = Release|Any CPU {DB6DD6E8-ABDE-4C4A-A3E2-DF49C074433B}.Release|Any CPU.Build.0 = Release|Any CPU + {DB6DD6E8-ABDE-4C4A-A3E2-DF49C074433B}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {DB6DD6E8-ABDE-4C4A-A3E2-DF49C074433B}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {DB6DD6E8-ABDE-4C4A-A3E2-DF49C074433B}.Release|x86.ActiveCfg = Release|Any CPU {49B6DFF1-D7A6-4BDE-90DB-9053416B0AA5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {49B6DFF1-D7A6-4BDE-90DB-9053416B0AA5}.Debug|Any CPU.Build.0 = Debug|Any CPU + {49B6DFF1-D7A6-4BDE-90DB-9053416B0AA5}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {49B6DFF1-D7A6-4BDE-90DB-9053416B0AA5}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {49B6DFF1-D7A6-4BDE-90DB-9053416B0AA5}.Debug|x86.ActiveCfg = Debug|Any CPU {49B6DFF1-D7A6-4BDE-90DB-9053416B0AA5}.Release|Any CPU.ActiveCfg = Release|Any CPU {49B6DFF1-D7A6-4BDE-90DB-9053416B0AA5}.Release|Any CPU.Build.0 = Release|Any CPU + {49B6DFF1-D7A6-4BDE-90DB-9053416B0AA5}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {49B6DFF1-D7A6-4BDE-90DB-9053416B0AA5}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {49B6DFF1-D7A6-4BDE-90DB-9053416B0AA5}.Release|x86.ActiveCfg = Release|Any CPU + {BF5E608C-6216-41C3-86AA-E6380B0A8888}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {BF5E608C-6216-41C3-86AA-E6380B0A8888}.Debug|Any CPU.Build.0 = Debug|Any CPU + {BF5E608C-6216-41C3-86AA-E6380B0A8888}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {BF5E608C-6216-41C3-86AA-E6380B0A8888}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {BF5E608C-6216-41C3-86AA-E6380B0A8888}.Debug|x86.ActiveCfg = Debug|Any CPU + {BF5E608C-6216-41C3-86AA-E6380B0A8888}.Release|Any CPU.ActiveCfg = Release|Any CPU + {BF5E608C-6216-41C3-86AA-E6380B0A8888}.Release|Any CPU.Build.0 = Release|Any CPU + {BF5E608C-6216-41C3-86AA-E6380B0A8888}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {BF5E608C-6216-41C3-86AA-E6380B0A8888}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {BF5E608C-6216-41C3-86AA-E6380B0A8888}.Release|x86.ActiveCfg = Release|Any CPU + {5FF240A5-2C62-4322-A0E1-335A04148603}.Debug|Any CPU.ActiveCfg = Debug|x86 + {5FF240A5-2C62-4322-A0E1-335A04148603}.Debug|Mixed Platforms.ActiveCfg = Debug|x86 + {5FF240A5-2C62-4322-A0E1-335A04148603}.Debug|Mixed Platforms.Build.0 = Debug|x86 + {5FF240A5-2C62-4322-A0E1-335A04148603}.Debug|x86.ActiveCfg = Debug|x86 + {5FF240A5-2C62-4322-A0E1-335A04148603}.Debug|x86.Build.0 = Debug|x86 + {5FF240A5-2C62-4322-A0E1-335A04148603}.Release|Any CPU.ActiveCfg = Release|x86 + {5FF240A5-2C62-4322-A0E1-335A04148603}.Release|Mixed Platforms.ActiveCfg = Release|x86 + {5FF240A5-2C62-4322-A0E1-335A04148603}.Release|Mixed Platforms.Build.0 = Release|x86 + {5FF240A5-2C62-4322-A0E1-335A04148603}.Release|x86.ActiveCfg = Release|x86 + {5FF240A5-2C62-4322-A0E1-335A04148603}.Release|x86.Build.0 = Release|x86 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection GlobalSection(NestedProjects) = preSolution + {FC181BA8-9DE5-444C-899F-9C554BC65629} = {BEAF6CBD-16DD-456B-A889-421E0DFAB783} + {ACCCC9EB-6386-44DC-8147-D69751FC6D93} = {BEAF6CBD-16DD-456B-A889-421E0DFAB783} {A24F4D38-BA9C-4FD6-95B7-4980DE36131A} = {4F5A2C5C-7813-4656-BB7B-5A4E9BCFCB26} {40B77789-EA11-4C05-8F52-86711D7BCAAF} = {4F5A2C5C-7813-4656-BB7B-5A4E9BCFCB26} {153FEF72-191C-43D9-BE71-2B351C7AC760} = {4F5A2C5C-7813-4656-BB7B-5A4E9BCFCB26} {B9113694-7226-4152-938D-3172B11571A1} = {4F5A2C5C-7813-4656-BB7B-5A4E9BCFCB26} {01987BAC-C498-44DD-B274-62EA2506B51D} = {4F5A2C5C-7813-4656-BB7B-5A4E9BCFCB26} {DB6DD6E8-ABDE-4C4A-A3E2-DF49C074433B} = {4F5A2C5C-7813-4656-BB7B-5A4E9BCFCB26} - {FC181BA8-9DE5-444C-899F-9C554BC65629} = {BEAF6CBD-16DD-456B-A889-421E0DFAB783} - {ACCCC9EB-6386-44DC-8147-D69751FC6D93} = {BEAF6CBD-16DD-456B-A889-421E0DFAB783} EndGlobalSection EndGlobal diff --git a/Protocols/WebSocket/Protocol/DraftHybi00Processor.cs b/Protocols/WebSocket/Protocol/DraftHybi00Processor.cs index 49756b5da..a2156ef2c 100644 --- a/Protocols/WebSocket/Protocol/DraftHybi00Processor.cs +++ b/Protocols/WebSocket/Protocol/DraftHybi00Processor.cs @@ -104,15 +104,30 @@ private byte[] GetResponseSecurityKey(string secKey1, string secKey2, byte[] sec return hash; } - public override void SendMessage(IWebSocketSession session, string message) + private ArraySegment GetPackageData(string message) { var maxByteCount = Encoding.UTF8.GetMaxByteCount(message.Length) + 2; var sendBuffer = new byte[maxByteCount]; sendBuffer[0] = WebSocketConstant.StartByte; int bytesCount = Encoding.UTF8.GetBytes(message, 0, message.Length, sendBuffer, 1); sendBuffer[1 + bytesCount] = WebSocketConstant.EndByte; + return new ArraySegment(sendBuffer, 0, bytesCount + 2); + } - session.SendRawData(sendBuffer, 0, bytesCount + 2); + public override IList> GetEncodedPackage(int opCode, byte[] data, int offset, int length) + { + throw new NotSupportedException(); + } + + public override IList> GetEncodedPackage(int opCode, string message) + { + return new ArraySegment[] { GetPackageData(message) }; + } + + public override void SendMessage(IWebSocketSession session, string message) + { + var packageData = GetPackageData(message); + session.SendRawData(packageData.Array, packageData.Offset, packageData.Count); } public override void SendCloseHandshake(IWebSocketSession session, int statusCode, string closeReason) @@ -144,5 +159,16 @@ public override bool IsValidCloseCode(int code) { throw new NotSupportedException(); } + + public override bool TrySendMessage(IWebSocketSession session, string message) + { + var packageData = GetPackageData(message); + return session.TrySendRawData(packageData.Array, packageData.Offset, packageData.Count); + } + + public override bool TrySendData(IWebSocketSession session, byte[] data, int offset, int length) + { + throw new NotImplementedException(); + } } -} +} \ No newline at end of file diff --git a/Protocols/WebSocket/Protocol/DraftHybi10Processor.cs b/Protocols/WebSocket/Protocol/DraftHybi10Processor.cs index 1bdf74c90..a938b14a3 100644 --- a/Protocols/WebSocket/Protocol/DraftHybi10Processor.cs +++ b/Protocols/WebSocket/Protocol/DraftHybi10Processor.cs @@ -137,7 +137,52 @@ public override void SendPing(IWebSocketSession session, byte[] ping) SendPackage(session, OpCode.Ping, ping, 0, ping.Length); } - private void SendPackage(IWebSocketSession session, int opCode, byte[] data, int offset, int length) + public override IList> GetEncodedPackage(int opCode, byte[] data, int offset, int length) + { + byte[] head; + + if (length < 126) + { + head = new byte[2]; + head[1] = (byte)length; + } + else if (length < 65536) + { + head = new byte[4]; + head[1] = (byte)126; + head[2] = (byte)(length / 256); + head[3] = (byte)(length % 256); + } + else + { + head = new byte[10]; + head[1] = (byte)127; + + int left = length; + int unit = 256; + + for (int i = 9; i > 1; i--) + { + head[i] = (byte)(left % unit); + left = left / unit; + + if (left == 0) + break; + } + } + + head[0] = (byte)(opCode | 0x80); //No mask by default + + return new ArraySegment[] { new ArraySegment(head), new ArraySegment(data, offset, length) }; + } + + public override IList> GetEncodedPackage(int opCode, string message) + { + byte[] playloadData = Encoding.UTF8.GetBytes(message); + return GetEncodedPackage(opCode, playloadData, 0, playloadData.Length); + } + + private byte[] GetPackageData(int opCode, byte[] data, int offset, int length) { byte[] fragment; @@ -178,15 +223,33 @@ private void SendPackage(IWebSocketSession session, int opCode, byte[] data, int Buffer.BlockCopy(data, offset, fragment, fragment.Length - length, length); } + return fragment; + } + + private void SendPackage(IWebSocketSession session, int opCode, byte[] data, int offset, int length) + { + var fragment = GetPackageData(opCode, data, offset, length); session.SendRawData(fragment, 0, fragment.Length); } + private bool TrySendPackage(IWebSocketSession session, int opCode, byte[] data, int offset, int length) + { + var fragment = GetPackageData(opCode, data, offset, length); + return session.TrySendRawData(fragment, 0, fragment.Length); + } + private void SendMessage(IWebSocketSession session, int opCode, string message) { byte[] playloadData = Encoding.UTF8.GetBytes(message); SendPackage(session, opCode, playloadData, 0, playloadData.Length); } + private bool TrySendMessage(IWebSocketSession session, int opCode, string message) + { + byte[] playloadData = Encoding.UTF8.GetBytes(message); + return TrySendPackage(session, opCode, playloadData, 0, playloadData.Length); + } + public override bool IsValidCloseCode(int code) { var closeCode = this.CloseStatusClode; @@ -214,5 +277,15 @@ public override bool IsValidCloseCode(int code) return false; } + + public override bool TrySendMessage(IWebSocketSession session, string message) + { + return TrySendMessage(session, OpCode.Text, message); + } + + public override bool TrySendData(IWebSocketSession session, byte[] data, int offset, int length) + { + return TrySendPackage(session, OpCode.Binary, data, offset, length); + } } -} +} \ No newline at end of file diff --git a/Protocols/WebSocket/Protocol/IProtocolProcessor.cs b/Protocols/WebSocket/Protocol/IProtocolProcessor.cs index 29b3199d7..d8dc3b82e 100644 --- a/Protocols/WebSocket/Protocol/IProtocolProcessor.cs +++ b/Protocols/WebSocket/Protocol/IProtocolProcessor.cs @@ -42,6 +42,24 @@ public interface IProtocolProcessor /// bool Handshake(IWebSocketSession session, WebSocketReceiveFilterBase previousFilter, out IReceiveFilter dataFrameReader); + /// + /// Gets the encoded package. + /// + /// The op code. + /// The data. + /// The offset. + /// The length. + /// + IList> GetEncodedPackage(int opCode, byte[] data, int offset, int length); + + /// + /// Gets the encoded package. + /// + /// The op code. + /// The message. + /// + IList> GetEncodedPackage(int opCode, string message); + /// /// Sends the message. /// @@ -49,6 +67,14 @@ public interface IProtocolProcessor /// The message. void SendMessage(IWebSocketSession session, string message); + /// + /// Try to send the message. + /// + /// The session. + /// The message. + /// if the messaged has been enqueued into the sending queue, return true; else if the message failed to be enqueued becuase the sending is full, then return false + bool TrySendMessage(IWebSocketSession session, string message); + /// /// Sends the data. /// @@ -58,6 +84,16 @@ public interface IProtocolProcessor /// The length. void SendData(IWebSocketSession session, byte[] data, int offset, int length); + /// + /// Try to send the data. + /// + /// The session. + /// The data. + /// The offset. + /// The length. + /// if the data has been enqueued into the sending queue, return true; else if the data failed to be enqueued becuase the sending is full, then return false + bool TrySendData(IWebSocketSession session, byte[] data, int offset, int length); + /// /// Sends the close handshake. /// diff --git a/Protocols/WebSocket/Protocol/MultipleProtocolSwitchProcessor.cs b/Protocols/WebSocket/Protocol/MultipleProtocolSwitchProcessor.cs index 665d93595..b160da3ee 100644 --- a/Protocols/WebSocket/Protocol/MultipleProtocolSwitchProcessor.cs +++ b/Protocols/WebSocket/Protocol/MultipleProtocolSwitchProcessor.cs @@ -73,5 +73,25 @@ public bool IsValidCloseCode(int code) { throw new NotImplementedException(); } + + public bool TrySendMessage(IWebSocketSession session, string message) + { + throw new NotImplementedException(); + } + + public bool TrySendData(IWebSocketSession session, byte[] data, int offset, int length) + { + throw new NotImplementedException(); + } + + public IList> GetEncodedPackage(int opCode, byte[] data, int offset, int length) + { + throw new NotImplementedException(); + } + + public IList> GetEncodedPackage(int opCode, string message) + { + throw new NotImplementedException(); + } } } diff --git a/Protocols/WebSocket/Protocol/ProtocolProcessorBase..cs b/Protocols/WebSocket/Protocol/ProtocolProcessorBase..cs index 86baf301f..f556f1101 100644 --- a/Protocols/WebSocket/Protocol/ProtocolProcessorBase..cs +++ b/Protocols/WebSocket/Protocol/ProtocolProcessorBase..cs @@ -21,6 +21,8 @@ protected ProtocolProcessorBase(int version, ICloseStatusCode closeStatusCode) public abstract void SendMessage(IWebSocketSession session, string message); + public abstract bool TrySendMessage(IWebSocketSession session, string message); + public abstract void SendCloseHandshake(IWebSocketSession session, int statusCode, string closeReason); public abstract void SendPong(IWebSocketSession session, byte[] pong); @@ -31,6 +33,8 @@ protected ProtocolProcessorBase(int version, ICloseStatusCode closeStatusCode) public abstract void SendData(IWebSocketSession session, byte[] data, int offset, int length); + public abstract bool TrySendData(IWebSocketSession session, byte[] data, int offset, int length); + public ICloseStatusCode CloseStatusClode { get; private set; } public int Version { get; private set; } @@ -38,5 +42,9 @@ protected ProtocolProcessorBase(int version, ICloseStatusCode closeStatusCode) protected string VersionTag { get; private set; } public abstract bool IsValidCloseCode(int code); + + public abstract IList> GetEncodedPackage(int opCode, byte[] data, int offset, int length); + + public abstract IList> GetEncodedPackage(int opCode, string message); } } diff --git a/Protocols/WebSocket/Protocol/WebSocketDataFrameReceiveFilter.cs b/Protocols/WebSocket/Protocol/WebSocketDataFrameReceiveFilter.cs index 24500e30e..cd8818ee5 100644 --- a/Protocols/WebSocket/Protocol/WebSocketDataFrameReceiveFilter.cs +++ b/Protocols/WebSocket/Protocol/WebSocketDataFrameReceiveFilter.cs @@ -17,7 +17,13 @@ class WebSocketDataFrameReceiveFilter : IReceiveFilter public int LeftBufferSize { - get { return m_Frame.InnerData.Count; } + get + { + if (m_Frame == null) + return 0; + + return m_Frame.InnerData.Count; + } } public IReceiveFilter NextReceiveFilter diff --git a/Protocols/WebSocket/SubProtocol/BasicSubProtocol.cs b/Protocols/WebSocket/SubProtocol/BasicSubProtocol.cs index 4963846e5..a3292aaea 100644 --- a/Protocols/WebSocket/SubProtocol/BasicSubProtocol.cs +++ b/Protocols/WebSocket/SubProtocol/BasicSubProtocol.cs @@ -209,16 +209,8 @@ private void DiscoverCommands() } #if DEBUG - var cmdbuilder = new StringBuilder(); - cmdbuilder.AppendLine(string.Format("SubProtocol {0} found the commands below:", this.Name)); - - foreach (var c in subCommands) - { - cmdbuilder.AppendLine(c.Name); - } - - - m_Logger.Debug(cmdbuilder.ToString()); + var commandNames = subCommands.Select(c => c.Name).ToArray(); + m_Logger.Debug(string.Format("SubProtocol {0} found the commands: [{1}]", this.Name, string.Join(", ", commandNames))); #endif m_CommandDict = new Dictionary>(subCommands.Count, StringComparer.OrdinalIgnoreCase); diff --git a/Protocols/WebSocket/SubProtocol/JsonSubCommandBase.cs b/Protocols/WebSocket/SubProtocol/JsonSubCommandBase.cs index 98583410d..fb62a9b66 100644 --- a/Protocols/WebSocket/SubProtocol/JsonSubCommandBase.cs +++ b/Protocols/WebSocket/SubProtocol/JsonSubCommandBase.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Linq; using System.Text; +using System.Threading; using SuperSocket.SocketBase.Protocol; namespace SuperSocket.WebSocket.SubProtocol @@ -47,15 +48,26 @@ public override void ExecuteCommand(TWebSocketSession session, SubRequestInfo re TJsonCommandInfo jsonCommandInfo; + LocalDataStoreSlot tokenSlot = null; + if (!string.IsNullOrEmpty(requestInfo.Token)) - session.CurrentToken = requestInfo.Token; + tokenSlot = session.SetCurrentToken(requestInfo.Token); - if (!m_IsSimpleType) - jsonCommandInfo = (TJsonCommandInfo)session.AppServer.JsonDeserialize(requestInfo.Body, m_CommandInfoType); - else - jsonCommandInfo = (TJsonCommandInfo)Convert.ChangeType(requestInfo.Body, m_CommandInfoType); + try + { + + if (!m_IsSimpleType) + jsonCommandInfo = (TJsonCommandInfo)session.AppServer.JsonDeserialize(requestInfo.Body, m_CommandInfoType); + else + jsonCommandInfo = (TJsonCommandInfo)Convert.ChangeType(requestInfo.Body, m_CommandInfoType); - ExecuteJsonCommand(session, jsonCommandInfo); + ExecuteJsonCommand(session, jsonCommandInfo); + } + finally + { + if (tokenSlot != null) + Thread.SetData(tokenSlot, null); + } } /// diff --git a/Protocols/WebSocket/SuperSocket.WebSocket.Net35.csproj b/Protocols/WebSocket/SuperSocket.WebSocket.Net35.csproj index a17bbc7ba..f3e9d7738 100644 --- a/Protocols/WebSocket/SuperSocket.WebSocket.Net35.csproj +++ b/Protocols/WebSocket/SuperSocket.WebSocket.Net35.csproj @@ -31,6 +31,12 @@ prompt 4 + + true + + + ..\..\supersocket.snk + ..\..\Reference\Json.NET\Net35\Newtonsoft.Json.dll diff --git a/Protocols/WebSocket/SuperSocket.WebSocket.Net40.csproj b/Protocols/WebSocket/SuperSocket.WebSocket.Net40.csproj index 409a98f62..6630cb2c2 100644 --- a/Protocols/WebSocket/SuperSocket.WebSocket.Net40.csproj +++ b/Protocols/WebSocket/SuperSocket.WebSocket.Net40.csproj @@ -30,6 +30,12 @@ prompt 4 + + true + + + ..\..\supersocket.snk + ..\..\Reference\Json.NET\Net40\Newtonsoft.Json.dll diff --git a/Protocols/WebSocket/SuperSocket.WebSocket.Net45.csproj b/Protocols/WebSocket/SuperSocket.WebSocket.Net45.csproj index 4c4364825..32355311e 100644 --- a/Protocols/WebSocket/SuperSocket.WebSocket.Net45.csproj +++ b/Protocols/WebSocket/SuperSocket.WebSocket.Net45.csproj @@ -33,6 +33,12 @@ 4 false + + true + + + ..\..\supersocket.snk + ..\..\Reference\Json.NET\Net40\Newtonsoft.Json.dll diff --git a/Protocols/WebSocket/WebSocketServer.cs b/Protocols/WebSocket/WebSocketServer.cs index d61ce3288..e825210e0 100644 --- a/Protocols/WebSocket/WebSocketServer.cs +++ b/Protocols/WebSocket/WebSocketServer.cs @@ -10,6 +10,7 @@ using System.Text; using System.Text.RegularExpressions; using System.Threading; +using System.Threading.Tasks; using Newtonsoft.Json; using SuperSocket.Common; using SuperSocket.SocketBase; @@ -706,6 +707,100 @@ private void ExecuteSubCommand(TWebSocketSession session, SubRequestInfo request session.LastActiveTime = DateTime.Now; } + #region broadcast + + /// + /// Broadcasts data to the specified sessions. + /// + /// The sessions. + /// The data. + /// The offset. + /// The length. + /// The send feedback. + public void Broadcast(IEnumerable sessions, byte[] data, int offset, int length, Action sendFeedback) + { + IList> encodedPackage = null; + + foreach (var s in sessions) + { + if (encodedPackage == null) + { + if (!s.ProtocolProcessor.CanSendBinaryData) + continue; + + encodedPackage = s.ProtocolProcessor.GetEncodedPackage(OpCode.Binary, data, offset, length); + } + else + { + if (!s.ProtocolProcessor.CanSendBinaryData) + continue; + } + + Task.Factory.StartNew(SendRawDataToSession, new BroadcastState(s, encodedPackage, sendFeedback)); + } + } + + /// + /// Broadcasts message to the specified sessions. + /// + /// The sessions. + /// The message. + /// The send feedback. + public void Broadcast(IEnumerable sessions, string message, Action sendFeedback) + { + IList> encodedPackage = null; + IProtocolProcessor encodingProcessor = null; + + foreach (var s in sessions) + { + if (encodedPackage == null || encodingProcessor != s.ProtocolProcessor) + { + encodedPackage = s.ProtocolProcessor.GetEncodedPackage(OpCode.Text, message); + encodingProcessor = s.ProtocolProcessor; + } + + Task.Factory.StartNew(SendRawDataToSession, new BroadcastState(s, encodedPackage, sendFeedback)); + } + } + + private void SendRawDataToSession(object state) + { + var param = state as BroadcastState; + var session = param.Session; + var sendFeedback = param.FeedbackFunc; + var sendOk = false; + + try + { + sendOk = session.TrySendRawData(param.Data); + } + catch (Exception e) + { + session.Logger.Error(e); + } + + sendFeedback(session, sendOk); + } + + class BroadcastState + { + public TWebSocketSession Session { get; private set; } + + public IList> Data { get; private set; } + + public Action FeedbackFunc { get; private set; } + + public BroadcastState(TWebSocketSession session, IList> data, Action feedbackFunc) + { + Session = session; + Data = data; + FeedbackFunc = feedbackFunc; + } + } + + + #endregion broadcast + #region JSON serialize/deserialize /// diff --git a/Protocols/WebSocket/WebSocketSession.cs b/Protocols/WebSocket/WebSocketSession.cs index 0b03c404d..5cefd966b 100644 --- a/Protocols/WebSocket/WebSocketSession.cs +++ b/Protocols/WebSocket/WebSocketSession.cs @@ -5,6 +5,7 @@ using System.Net; using System.Security.Authentication; using System.Text; +using System.Threading; using SuperSocket.Common; using SuperSocket.SocketBase; using SuperSocket.SocketBase.Command; @@ -79,6 +80,15 @@ public interface IWebSocketSession : IAppSession /// The length. void SendRawData(byte[] data, int offset, int length); + /// + /// Try to send the raw binary response. + /// + /// The data. + /// The offset. + /// The length. + /// if the data to be sent is queued, return true, else the queue is full, then return false + bool TrySendRawData(byte[] data, int offset, int length); + /// /// Gets the app server. /// @@ -179,10 +189,25 @@ public class WebSocketSession : AppSession /// Gets the current token. /// - public string CurrentToken { get; internal set; } + public string CurrentToken + { + get + { + return Thread.GetData(Thread.GetNamedDataSlot(m_CurrentTokenSlotName)) as string; + } + } /// /// Gets the app server. @@ -198,6 +223,11 @@ IWebSocketServer IWebSocketSession.AppServer } string IWebSocketSession.GetAvailableSubProtocol(string protocol) + { + return GetAvailableSubProtocol(protocol); + } + + protected virtual string GetAvailableSubProtocol(string protocol) { if (string.IsNullOrEmpty(protocol)) { @@ -207,11 +237,11 @@ string IWebSocketSession.GetAvailableSubProtocol(string protocol) var arrNames = protocol.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries); - foreach(var name in arrNames) + foreach (var name in arrNames) { var subProtocol = AppServer.GetSubProtocol(name); - if(subProtocol != null) + if (subProtocol != null) { SubProtocol = subProtocol; return name; @@ -290,33 +320,48 @@ private void SetCookie() { string cookieValue = this.Items.GetValue(WebSocketConstant.Cookie, string.Empty); + if (string.IsNullOrEmpty(cookieValue)) + return; + var cookies = new StringDictionary(); + this.Cookies = cookies; + + string[] pairs = cookieValue.Split(';'); - if (!string.IsNullOrEmpty(cookieValue)) + int pos; + string key, value; + + foreach (var p in pairs) { - string[] pairs = cookieValue.Split(';'); + pos = p.IndexOf('='); + + if (pos <= 0) + continue; + + key = p.Substring(0, pos).Trim(); - int pos; - string key, value; + pos += 1; + + if (pos < p.Length) + value = p.Substring(pos).Trim(); + else + value = string.Empty; - foreach (var p in pairs) + if (string.IsNullOrEmpty(value)) { - pos = p.IndexOf('='); - if (pos > 0) - { - key = p.Substring(0, pos).Trim(); - pos += 1; - if (pos < p.Length) - value = p.Substring(pos).Trim(); - else - value = string.Empty; - - cookies[key] = Uri.UnescapeDataString(value); - } + cookies[key] = string.Empty; + continue; } - } - this.Cookies = cookies; + try + { + cookies[key] = Uri.UnescapeDataString(value); + } + catch (Exception e) + { + Logger.Error(this, string.Format("Failed to read cookie, key: {0}, value: {1}.", key, value), e); + } + } } @@ -335,6 +380,16 @@ public override void Send(string message) ProtocolProcessor.SendMessage(this, message); } + /// + /// Tries to send. + /// + /// The message to be sent. + /// + public override bool TrySend(string message) + { + return ProtocolProcessor.TrySendMessage(this, message); + } + /// /// Sends the response. /// @@ -345,7 +400,7 @@ public override void Send(byte[] data, int offset, int length) { if (!ProtocolProcessor.CanSendBinaryData) { - if(Logger.IsErrorEnabled) + if (Logger.IsErrorEnabled) Logger.Error("The websocket of this version cannot used for sending binary data!"); return; } @@ -353,6 +408,42 @@ public override void Send(byte[] data, int offset, int length) ProtocolProcessor.SendData(this, data, offset, length); } + /// + /// Tries to send the data over the websocket connection. + /// + /// The segment to be sent. + /// + public override bool TrySend(ArraySegment segment) + { + if (!ProtocolProcessor.CanSendBinaryData) + { + if (Logger.IsErrorEnabled) + Logger.Error("The websocket of this version cannot used for sending binary data!"); + return false; + } + + return ProtocolProcessor.TrySendData(this, segment.Array, segment.Offset, segment.Count); + } + + /// + /// Tries to send the data over the websocket connection. + /// + /// The data. + /// The offset. + /// The length. + /// + public override bool TrySend(byte[] data, int offset, int length) + { + if (!ProtocolProcessor.CanSendBinaryData) + { + if (Logger.IsErrorEnabled) + Logger.Error("The websocket of this version cannot used for sending binary data!"); + return false; + } + + return ProtocolProcessor.TrySendData(this, data, offset, length); + } + /// /// Sends the response. /// @@ -374,6 +465,30 @@ void IWebSocketSession.SendRawData(byte[] data, int offset, int length) } + /// + /// Try to send the raw binary response. + /// + /// The data. + /// The offset. + /// The length. + /// + /// if the data to be sent is queued, return true, else the queue is full, then return false + /// + bool IWebSocketSession.TrySendRawData(byte[] data, int offset, int length) + { + return base.TrySend(new ArraySegment(data, offset, length)); + } + + /// + /// Tries the send raw data segments. + /// + /// The segments. + /// + internal bool TrySendRawData(IList> segments) + { + return base.TrySend(segments); + } + /// /// Closes the with handshake. /// @@ -392,8 +507,14 @@ public void CloseWithHandshake(int statusCode, string reasonText) { if (!InClosing) InClosing = true; - - ProtocolProcessor.SendCloseHandshake(this, statusCode, reasonText); + + try + { + ProtocolProcessor.SendCloseHandshake(this, statusCode, reasonText); + } + catch (TimeoutException) + { + } StartClosingHandshakeTime = DateTime.Now; AppServer.PushToCloseHandshakeQueue(this); diff --git a/Push.bat b/Push.bat index 441890b5b..a2facee92 100644 --- a/Push.bat +++ b/Push.bat @@ -1,3 +1,3 @@ -git push origin master:master -git push github master:master +git push origin v1.6:v1.6 +git push github v1.6:v1.6 pause \ No newline at end of file diff --git a/QuickStart/AppDomainIsolation/SampleA/SampleA.csproj b/QuickStart/AppDomainIsolation/SampleA/SampleA.csproj index 87780ac76..d57f11ca5 100644 --- a/QuickStart/AppDomainIsolation/SampleA/SampleA.csproj +++ b/QuickStart/AppDomainIsolation/SampleA/SampleA.csproj @@ -35,6 +35,7 @@ ..\..\..\Reference\log4net.dll + diff --git a/QuickStart/AppDomainIsolation/SampleB/SampleB.csproj b/QuickStart/AppDomainIsolation/SampleB/SampleB.csproj index 936d95996..88233cb07 100644 --- a/QuickStart/AppDomainIsolation/SampleB/SampleB.csproj +++ b/QuickStart/AppDomainIsolation/SampleB/SampleB.csproj @@ -35,6 +35,7 @@ ..\..\..\Reference\log4net.dll + diff --git a/QuickStart/AppDomainIsolation/SampleC/SampleC.csproj b/QuickStart/AppDomainIsolation/SampleC/SampleC.csproj index 963165875..59f907f33 100644 --- a/QuickStart/AppDomainIsolation/SampleC/SampleC.csproj +++ b/QuickStart/AppDomainIsolation/SampleC/SampleC.csproj @@ -45,6 +45,7 @@ ..\..\..\Reference\DLR\Net40\Microsoft.Scripting.dll + diff --git a/QuickStart/IronSocketServer/IronSocketServer.csproj b/QuickStart/IronSocketServer/IronSocketServer.csproj index a5f2282e5..9da6a6ffe 100644 --- a/QuickStart/IronSocketServer/IronSocketServer.csproj +++ b/QuickStart/IronSocketServer/IronSocketServer.csproj @@ -35,6 +35,7 @@ ..\..\Reference\log4net.dll + diff --git a/QuickStart/MultipleAppServer/MultipleAppServer.csproj b/QuickStart/MultipleAppServer/MultipleAppServer.csproj index 0fe84b04a..0b40c7f05 100644 --- a/QuickStart/MultipleAppServer/MultipleAppServer.csproj +++ b/QuickStart/MultipleAppServer/MultipleAppServer.csproj @@ -31,7 +31,11 @@ 4 + + ..\..\Reference\log4net.dll + + @@ -45,10 +49,6 @@ - - {49BA8E71-4F97-40D6-BBE7-22B71D222486} - SuperSocket.Agent.Net40 - {A24F4D38-BA9C-4FD6-95B7-4980DE36131A} SuperSocket.Common.Net40 diff --git a/QuickStart/MultipleAppServer/MyAppServerB.cs b/QuickStart/MultipleAppServer/MyAppServerB.cs index 9d67f0c28..33782f0b7 100644 --- a/QuickStart/MultipleAppServer/MyAppServerB.cs +++ b/QuickStart/MultipleAppServer/MyAppServerB.cs @@ -10,7 +10,7 @@ public class MyAppServerB : AppServer, IDespatchServer { public void DispatchMessage(string sessionKey, string message) { - var session = GetAppSessionByID(sessionKey); + var session = GetSessionByID(sessionKey); if (session == null) return; diff --git a/QuickStart/MultipleCommandAssembly/SampleServer/SampleServer.csproj b/QuickStart/MultipleCommandAssembly/SampleServer/SampleServer.csproj index b785fc7ff..9746bdfdb 100644 --- a/QuickStart/MultipleCommandAssembly/SampleServer/SampleServer.csproj +++ b/QuickStart/MultipleCommandAssembly/SampleServer/SampleServer.csproj @@ -35,6 +35,7 @@ ..\..\..\Reference\log4net.dll + diff --git a/QuickStart/QuickStart.2012.sln b/QuickStart/QuickStart.2013.sln similarity index 97% rename from QuickStart/QuickStart.2012.sln rename to QuickStart/QuickStart.2013.sln index f7971d9dd..e63fc96ba 100644 --- a/QuickStart/QuickStart.2012.sln +++ b/QuickStart/QuickStart.2013.sln @@ -1,6 +1,8 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 2012 +# Visual Studio 2013 +VisualStudioVersion = 12.0.30723.0 +MinimumVisualStudioVersion = 10.0.40219.1 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "SuperSocket", "SuperSocket", "{2E6E656A-D747-4A69-B300-8C82573A76B5}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SuperSocket.Common.Net40", "..\Common\SuperSocket.Common.Net40.csproj", "{A24F4D38-BA9C-4FD6-95B7-4980DE36131A}" @@ -90,6 +92,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CommandAssemblyC", "Multipl EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SwitchReceiveFilter", "SwitchReceiveFilter\SwitchReceiveFilter.csproj", "{E0205F1B-0C46-4CCD-B9D6-4A05291849D1}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SuperSocket.Agent.Net40", "..\Agent\SuperSocket.Agent.Net40.csproj", "{49BA8E71-4F97-40D6-BBE7-22B71D222486}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -430,6 +434,16 @@ Global {E0205F1B-0C46-4CCD-B9D6-4A05291849D1}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU {E0205F1B-0C46-4CCD-B9D6-4A05291849D1}.Release|Mixed Platforms.Build.0 = Release|Any CPU {E0205F1B-0C46-4CCD-B9D6-4A05291849D1}.Release|x86.ActiveCfg = Release|Any CPU + {49BA8E71-4F97-40D6-BBE7-22B71D222486}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {49BA8E71-4F97-40D6-BBE7-22B71D222486}.Debug|Any CPU.Build.0 = Debug|Any CPU + {49BA8E71-4F97-40D6-BBE7-22B71D222486}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {49BA8E71-4F97-40D6-BBE7-22B71D222486}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {49BA8E71-4F97-40D6-BBE7-22B71D222486}.Debug|x86.ActiveCfg = Debug|Any CPU + {49BA8E71-4F97-40D6-BBE7-22B71D222486}.Release|Any CPU.ActiveCfg = Release|Any CPU + {49BA8E71-4F97-40D6-BBE7-22B71D222486}.Release|Any CPU.Build.0 = Release|Any CPU + {49BA8E71-4F97-40D6-BBE7-22B71D222486}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {49BA8E71-4F97-40D6-BBE7-22B71D222486}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {49BA8E71-4F97-40D6-BBE7-22B71D222486}.Release|x86.ActiveCfg = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -437,38 +451,39 @@ Global GlobalSection(NestedProjects) = preSolution {A24F4D38-BA9C-4FD6-95B7-4980DE36131A} = {2E6E656A-D747-4A69-B300-8C82573A76B5} {B9113694-7226-4152-938D-3172B11571A1} = {2E6E656A-D747-4A69-B300-8C82573A76B5} + {CA67AA73-42D0-4A08-85C4-61FB569231A8} = {E73AE70C-6D55-43E4-B1C2-FD3C0819BC2C} + {083155D6-A0A2-4703-AC43-7C6496A9B607} = {E73AE70C-6D55-43E4-B1C2-FD3C0819BC2C} {BDBB6CE9-C3CE-49C1-A05E-2D7628430A02} = {2E6E656A-D747-4A69-B300-8C82573A76B5} {40B77789-EA11-4C05-8F52-86711D7BCAAF} = {2E6E656A-D747-4A69-B300-8C82573A76B5} {153FEF72-191C-43D9-BE71-2B351C7AC760} = {2E6E656A-D747-4A69-B300-8C82573A76B5} + {FB261628-26FD-48B5-9D3D-C9E34B4728CF} = {B8EFC72D-E929-428D-9D2F-C47FF0E520DD} {01987BAC-C498-44DD-B274-62EA2506B51D} = {2E6E656A-D747-4A69-B300-8C82573A76B5} - {55BAA051-CE62-4D4A-81B6-68B042CC78E9} = {2E6E656A-D747-4A69-B300-8C82573A76B5} - {083155D6-A0A2-4703-AC43-7C6496A9B607} = {E73AE70C-6D55-43E4-B1C2-FD3C0819BC2C} + {2B1DD784-5A9D-4177-9291-ADEB3213A938} = {B8EFC72D-E929-428D-9D2F-C47FF0E520DD} {595A2664-BBC7-41CF-AA19-E34DEA3DBE6E} = {E73AE70C-6D55-43E4-B1C2-FD3C0819BC2C} {E286C017-49DB-4EA9-A6BC-EC6ADB51E0A8} = {E73AE70C-6D55-43E4-B1C2-FD3C0819BC2C} - {18457034-1FC3-4464-AA10-23B58EB1B0D5} = {E73AE70C-6D55-43E4-B1C2-FD3C0819BC2C} - {CA67AA73-42D0-4A08-85C4-61FB569231A8} = {E73AE70C-6D55-43E4-B1C2-FD3C0819BC2C} - {1EC9BF07-EDD2-4C51-A1FB-C2E76C9B0691} = {E73AE70C-6D55-43E4-B1C2-FD3C0819BC2C} - {31BECC3F-081C-464E-9420-EAD3D485062B} = {E73AE70C-6D55-43E4-B1C2-FD3C0819BC2C} - {065E2199-6D58-4251-9EA6-E40FE0058A74} = {E73AE70C-6D55-43E4-B1C2-FD3C0819BC2C} - {2B1DD784-5A9D-4177-9291-ADEB3213A938} = {B8EFC72D-E929-428D-9D2F-C47FF0E520DD} {22A610A1-5F55-449E-8831-A832D692DAEC} = {B8EFC72D-E929-428D-9D2F-C47FF0E520DD} + {55BAA051-CE62-4D4A-81B6-68B042CC78E9} = {2E6E656A-D747-4A69-B300-8C82573A76B5} {9510FE4C-52ED-419F-B32D-61F18C487A64} = {B8EFC72D-E929-428D-9D2F-C47FF0E520DD} - {FB261628-26FD-48B5-9D3D-C9E34B4728CF} = {B8EFC72D-E929-428D-9D2F-C47FF0E520DD} - {760FCE9F-9DBC-4485-977E-EDBD96CFE2D4} = {B8EFC72D-E929-428D-9D2F-C47FF0E520DD} - {5B555A9B-2CE7-4F13-B0F1-788B8582E61C} = {B8EFC72D-E929-428D-9D2F-C47FF0E520DD} - {E0205F1B-0C46-4CCD-B9D6-4A05291849D1} = {B8EFC72D-E929-428D-9D2F-C47FF0E520DD} + {18457034-1FC3-4464-AA10-23B58EB1B0D5} = {E73AE70C-6D55-43E4-B1C2-FD3C0819BC2C} {C71B100D-694D-4383-B7D8-4D9D3DCEF23E} = {AB1825DD-8588-418E-8043-70CEDB49265E} {DB923F73-B805-469C-AC2F-08C6540F2EB9} = {AB1825DD-8588-418E-8043-70CEDB49265E} {E5F6D1E1-FD82-4EBD-B75B-390A9D96405C} = {AB1825DD-8588-418E-8043-70CEDB49265E} {A6458E2D-1767-479B-8DFA-F2F4969A02E1} = {AB1825DD-8588-418E-8043-70CEDB49265E} {76D94FF3-AA2A-4351-B8E3-D5E273511C0F} = {AB1825DD-8588-418E-8043-70CEDB49265E} {674A646C-9F14-498A-9825-7AF8494A1C8C} = {AB1825DD-8588-418E-8043-70CEDB49265E} + {1EC9BF07-EDD2-4C51-A1FB-C2E76C9B0691} = {E73AE70C-6D55-43E4-B1C2-FD3C0819BC2C} + {760FCE9F-9DBC-4485-977E-EDBD96CFE2D4} = {B8EFC72D-E929-428D-9D2F-C47FF0E520DD} + {31BECC3F-081C-464E-9420-EAD3D485062B} = {E73AE70C-6D55-43E4-B1C2-FD3C0819BC2C} + {5B555A9B-2CE7-4F13-B0F1-788B8582E61C} = {B8EFC72D-E929-428D-9D2F-C47FF0E520DD} {AF2F6B65-9B04-44D0-9325-6918A3C22860} = {5B555A9B-2CE7-4F13-B0F1-788B8582E61C} {7123AE78-0C7B-4E7B-9F38-B5044271F6F4} = {5B555A9B-2CE7-4F13-B0F1-788B8582E61C} {1D82DB60-8411-4000-845A-537644C4C2A9} = {5B555A9B-2CE7-4F13-B0F1-788B8582E61C} + {065E2199-6D58-4251-9EA6-E40FE0058A74} = {E73AE70C-6D55-43E4-B1C2-FD3C0819BC2C} {A1367348-D34B-47C0-B0F8-A3E26BA7A28C} = {065E2199-6D58-4251-9EA6-E40FE0058A74} {FB2504E2-B888-4E16-BF43-FF1236CF4256} = {065E2199-6D58-4251-9EA6-E40FE0058A74} {83D6B654-0722-4C28-8814-3AA491C457C1} = {065E2199-6D58-4251-9EA6-E40FE0058A74} {9B905DD7-061E-4CE0-8B28-257040F7D166} = {065E2199-6D58-4251-9EA6-E40FE0058A74} + {E0205F1B-0C46-4CCD-B9D6-4A05291849D1} = {B8EFC72D-E929-428D-9D2F-C47FF0E520DD} + {49BA8E71-4F97-40D6-BBE7-22B71D222486} = {2E6E656A-D747-4A69-B300-8C82573A76B5} EndGlobalSection EndGlobal diff --git a/QuickStart/ServerPush/PushServer.cs b/QuickStart/ServerPush/PushServer.cs index 405ca9505..33ef3998f 100644 --- a/QuickStart/ServerPush/PushServer.cs +++ b/QuickStart/ServerPush/PushServer.cs @@ -4,6 +4,7 @@ using System.Text; using System.Threading; using SuperSocket.SocketBase; +using SuperSocket.SocketBase.Config; namespace SuperSocket.QuickStart.ServerPush { @@ -11,7 +12,24 @@ public class PushServer : AppServer { private Timer m_PushTimer; - private int m_Interval = 60 * 1000; //1 minute + private int m_Interval; //1 minute + + protected override bool Setup(IRootConfig rootConfig, IServerConfig config) + { + RegisterConfigHandler(config, "pushInterval", (value) => + { + var interval = 0; + int.TryParse(value, out interval); + + if (interval <= 0) + interval = 60;// 60 seconds by default + + m_Interval = interval * 1000; + return true; + }); + + return true; + } protected override void OnStarted() { diff --git a/QuickStart/ServerPush/ServerPush.csproj b/QuickStart/ServerPush/ServerPush.csproj index da1679858..7a0a18f34 100644 --- a/QuickStart/ServerPush/ServerPush.csproj +++ b/QuickStart/ServerPush/ServerPush.csproj @@ -31,7 +31,11 @@ 4 + + ..\..\Reference\log4net.dll + + diff --git a/README.TXT b/README.TXT deleted file mode 100644 index 92f022cee..000000000 --- a/README.TXT +++ /dev/null @@ -1,30 +0,0 @@ -SuperSocket -=========================================== - -SuperSocket is a light weight extensible socket application framework. -You can use it to build a command based server side socket application easily without thinking about how to use socket, -how to maintain the socket connections and how socket works. - -It is a pure C# project which is designed to be extended, so it is easy to be integrated to your existing system. -As long as your systems (like forum/CRM/MIS/HRM/ERP) are developed in .NET language, -you must be able to use SuperSocket to build your socket application as a part of your current system perfectly. - - -Project homepage: http://supersocket.codeplex.com/ -Documentation: http://docs.supersocket.net/ -Git URL: https://git01.codeplex.com/supersocket -Author email address: kerry-jiang@hotmail.com -Releases download: http://supersocket.codeplex.com/releases/ - - -Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. -You may obtain a copy of the License at - -http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an -"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and limitations under the License. - - -Copyright 2010-2013 Kerry Jiang (kerry-jiang@hotmail.com) \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 000000000..bdc4ff517 --- /dev/null +++ b/README.md @@ -0,0 +1,35 @@ +# SuperSocket [![Build Status](https://travis-ci.org/kerryjiang/SuperSocket.svg?branch=v1.6)](https://travis-ci.org/kerryjiang/SuperSocket) + + +**SuperSocket** is a light weight extensible socket application framework. +You can use it to build a server side socket application easily without thinking about how to use socket, how to maintain the socket connections and how socket works. + +It is a pure C# project which is designed to be extended, so it is easy to be integrated to your existing system. +As long as your systems are developed in .NET language, +you must be able to use SuperSocket to build your socket application as a part of your current system perfectly. + + +- **Project homepage**: [http://www.supersocket.net/](http://www.supersocket.net/) +- **Documentation**: [http://docs.supersocket.net/](http://docs.supersocket.net/) +- **Releases download**: [http://supersocket.codeplex.com/releases/](http://supersocket.codeplex.com/releases/) +- **License**: [http://www.apache.org/licenses/LICENSE-2.0](http://www.apache.org/licenses/LICENSE-2.0) + + +**NuGet Packages** + +| Name | Package | +|---------------------------|-----------------------------------| +| **SuperSocket** | [![SuperSocket][1]][2] | +| **SuperSocket.Engine** | [![SuperSocket.Engine][3]][4] | +| **SuperSocket.WebSocket** | [![SuperSocket.WebSocket][5]][6] | + + +[1]: https://img.shields.io/nuget/v/SuperSocket.svg?style=flat +[2]: https://www.nuget.org/packages/SuperSocket +[3]: https://img.shields.io/nuget/v/SuperSocket.Engine.svg?style=flat +[4]: https://www.nuget.org/packages/SuperSocket.Engine +[5]: https://img.shields.io/nuget/v/SuperSocket.WebSocket.svg?style=flat +[6]: https://www.nuget.org/packages/SuperSocket.WebSocket + + +*Copyright 2010-2015 Kerry Jiang (kerry-jiang@hotmail.com)* diff --git a/Reference/WebSocket4Net/net20/Debug/WebSocket4Net.dll b/Reference/WebSocket4Net/net20/Debug/WebSocket4Net.dll index f8afa9a7e..c50641d88 100644 Binary files a/Reference/WebSocket4Net/net20/Debug/WebSocket4Net.dll and b/Reference/WebSocket4Net/net20/Debug/WebSocket4Net.dll differ diff --git a/Reference/WebSocket4Net/net20/Release/WebSocket4Net.dll b/Reference/WebSocket4Net/net20/Release/WebSocket4Net.dll index 1905177cb..ebabe02ca 100644 Binary files a/Reference/WebSocket4Net/net20/Release/WebSocket4Net.dll and b/Reference/WebSocket4Net/net20/Release/WebSocket4Net.dll differ diff --git a/Reference/WebSocket4Net/net35/Debug/WebSocket4Net.dll b/Reference/WebSocket4Net/net35/Debug/WebSocket4Net.dll index 13c70ea95..aafe1a9fc 100644 Binary files a/Reference/WebSocket4Net/net35/Debug/WebSocket4Net.dll and b/Reference/WebSocket4Net/net35/Debug/WebSocket4Net.dll differ diff --git a/Reference/WebSocket4Net/net35/Release/WebSocket4Net.dll b/Reference/WebSocket4Net/net35/Release/WebSocket4Net.dll index e06ec7f1a..4840c2e03 100644 Binary files a/Reference/WebSocket4Net/net35/Release/WebSocket4Net.dll and b/Reference/WebSocket4Net/net35/Release/WebSocket4Net.dll differ diff --git a/Reference/WebSocket4Net/net40/Debug/WebSocket4Net.dll b/Reference/WebSocket4Net/net40/Debug/WebSocket4Net.dll index b148eae89..14f405d3d 100644 Binary files a/Reference/WebSocket4Net/net40/Debug/WebSocket4Net.dll and b/Reference/WebSocket4Net/net40/Debug/WebSocket4Net.dll differ diff --git a/Reference/WebSocket4Net/net40/Release/WebSocket4Net.dll b/Reference/WebSocket4Net/net40/Release/WebSocket4Net.dll index 3fc981462..f3eb42b7b 100644 Binary files a/Reference/WebSocket4Net/net40/Release/WebSocket4Net.dll and b/Reference/WebSocket4Net/net40/Release/WebSocket4Net.dll differ diff --git a/Reference/WebSocket4Net/net45/Debug/WebSocket4Net.dll b/Reference/WebSocket4Net/net45/Debug/WebSocket4Net.dll index 1b9e47734..8186900c6 100644 Binary files a/Reference/WebSocket4Net/net45/Debug/WebSocket4Net.dll and b/Reference/WebSocket4Net/net45/Debug/WebSocket4Net.dll differ diff --git a/Reference/WebSocket4Net/net45/Release/WebSocket4Net.dll b/Reference/WebSocket4Net/net45/Release/WebSocket4Net.dll index ec943b15f..eb554aa27 100644 Binary files a/Reference/WebSocket4Net/net45/Release/WebSocket4Net.dll and b/Reference/WebSocket4Net/net45/Release/WebSocket4Net.dll differ diff --git a/Reference/WebSocket4Net/sl40-windowsphone71/Debug/WebSocket4Net.dll b/Reference/WebSocket4Net/sl40-windowsphone71/Debug/WebSocket4Net.dll index ad624ed2e..ac4572f40 100644 Binary files a/Reference/WebSocket4Net/sl40-windowsphone71/Debug/WebSocket4Net.dll and b/Reference/WebSocket4Net/sl40-windowsphone71/Debug/WebSocket4Net.dll differ diff --git a/Reference/WebSocket4Net/sl40-windowsphone71/Release/WebSocket4Net.dll b/Reference/WebSocket4Net/sl40-windowsphone71/Release/WebSocket4Net.dll index bccbf6d59..4db5c6038 100644 Binary files a/Reference/WebSocket4Net/sl40-windowsphone71/Release/WebSocket4Net.dll and b/Reference/WebSocket4Net/sl40-windowsphone71/Release/WebSocket4Net.dll differ diff --git a/Reference/WebSocket4Net/sl40/Debug/WebSocket4Net.dll b/Reference/WebSocket4Net/sl40/Debug/WebSocket4Net.dll index ab9afcf09..ca5090877 100644 Binary files a/Reference/WebSocket4Net/sl40/Debug/WebSocket4Net.dll and b/Reference/WebSocket4Net/sl40/Debug/WebSocket4Net.dll differ diff --git a/Reference/WebSocket4Net/sl40/Release/WebSocket4Net.dll b/Reference/WebSocket4Net/sl40/Release/WebSocket4Net.dll index fccf9b20b..0bc7236b1 100644 Binary files a/Reference/WebSocket4Net/sl40/Release/WebSocket4Net.dll and b/Reference/WebSocket4Net/sl40/Release/WebSocket4Net.dll differ diff --git a/Reference/WebSocket4Net/sl50/Debug/WebSocket4Net.dll b/Reference/WebSocket4Net/sl50/Debug/WebSocket4Net.dll index 078b563a6..1c29e904a 100644 Binary files a/Reference/WebSocket4Net/sl50/Debug/WebSocket4Net.dll and b/Reference/WebSocket4Net/sl50/Debug/WebSocket4Net.dll differ diff --git a/Reference/WebSocket4Net/sl50/Release/WebSocket4Net.dll b/Reference/WebSocket4Net/sl50/Release/WebSocket4Net.dll index 8fc7af319..6010060dc 100644 Binary files a/Reference/WebSocket4Net/sl50/Release/WebSocket4Net.dll and b/Reference/WebSocket4Net/sl50/Release/WebSocket4Net.dll differ diff --git a/Reference/log4net.dll b/Reference/log4net.dll index c3ced3548..a7f77376d 100644 Binary files a/Reference/log4net.dll and b/Reference/log4net.dll differ diff --git a/SocketBase/AppServer.cs b/SocketBase/AppServer.cs index 172deb165..d61a6f0ae 100644 --- a/SocketBase/AppServer.cs +++ b/SocketBase/AppServer.cs @@ -151,7 +151,7 @@ protected override bool RegisterSession(string sessionID, TAppSession appSession /// /// The session ID. /// - [Obsolete] + [Obsolete("Use the method GetSessionByID instead")] public TAppSession GetAppSessionByID(string sessionID) { return GetSessionByID(sessionID); @@ -225,10 +225,15 @@ private void ClearIdleSession(object state) { try { + var sessionSource = SessionSource; + + if (sessionSource == null) + return; + DateTime now = DateTime.Now; DateTime timeOut = now.AddSeconds(0 - Config.IdleSessionTimeOut); - var timeOutSessions = SessionSource.Where(s => s.Value.LastActiveTime <= timeOut).Select(s => s.Value); + var timeOutSessions = sessionSource.Where(s => s.Value.LastActiveTime <= timeOut).Select(s => s.Value); System.Threading.Tasks.Parallel.ForEach(timeOutSessions, s => { @@ -294,7 +299,12 @@ private void TakeSessionSnapshot(object state) /// public override IEnumerable GetSessions(Func critera) { - return SessionSource.Select(p => p.Value).Where(critera); + var sessionSource = SessionSource; + + if (sessionSource == null) + return null; + + return sessionSource.Select(p => p.Value).Where(critera); } /// @@ -303,7 +313,12 @@ public override IEnumerable GetSessions(Func cri /// public override IEnumerable GetAllSessions() { - return SessionSource.Select(p => p.Value); + var sessionSource = SessionSource; + + if (sessionSource == null) + return null; + + return sessionSource.Select(p => p.Value); } /// @@ -327,6 +342,8 @@ public override void Stop() m_ClearIdleSessionTimer = null; } + m_SessionsSnapshot = null; + var sessions = m_SessionDict.ToArray(); if (sessions.Length > 0) diff --git a/SocketBase/AppServerBase.ConfigHotUpdate.cs b/SocketBase/AppServerBase.ConfigHotUpdate.cs new file mode 100644 index 000000000..4f9895719 --- /dev/null +++ b/SocketBase/AppServerBase.ConfigHotUpdate.cs @@ -0,0 +1,159 @@ +using System; +using System.Collections.Generic; +using System.Collections.Specialized; +using System.Configuration; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using SuperSocket.Common; +using SuperSocket.SocketBase.Config; +using SuperSocket.SocketBase.Protocol; + +namespace SuperSocket.SocketBase +{ + interface IConfigValueChangeNotifier + { + bool Notify(string newValue); + } + + class ConfigValueChangeNotifier : IConfigValueChangeNotifier + { + Func m_Handler; + + public ConfigValueChangeNotifier(Func handler) + { + m_Handler = handler; + } + + public bool Notify(string newValue) + { + return m_Handler(newValue); + } + } + class ConfigValueChangeNotifier : IConfigValueChangeNotifier + where TConfigOption : ConfigurationElement, new() + { + Func m_Handler; + + public ConfigValueChangeNotifier(Func handler) + { + m_Handler = handler; + } + public bool Notify(string newValue) + { + if (string.IsNullOrEmpty(newValue)) + return m_Handler(default(TConfigOption)); + else + return m_Handler(ConfigurationExtension.DeserializeChildConfig(newValue)); + } + } + + public abstract partial class AppServerBase + where TRequestInfo : class, IRequestInfo + where TAppSession : AppSession, IAppSession, new() + { + private Dictionary m_ConfigUpdatedNotifiers = new Dictionary(StringComparer.OrdinalIgnoreCase); + + /// + /// Registers the configuration option value handler, it is used for reading configuration value and reload it after the configuration is changed; + /// + /// The type of the configuration option. + /// The server configuration. + /// The changed config option's name. + /// The handler. + protected bool RegisterConfigHandler(IServerConfig config, string name, Func handler) + where TConfigOption : ConfigurationElement, new() + { + var notifier = new ConfigValueChangeNotifier(handler); + m_ConfigUpdatedNotifiers.Add(name, notifier); + return notifier.Notify(config.Options.GetValue(name)); + } + + /// + /// Registers the configuration option value handler, it is used for reading configuration value and reload it after the configuration is changed; + /// + /// The server configuration. + /// The changed config option name. + /// The handler. + protected bool RegisterConfigHandler(IServerConfig config, string name, Func handler) + { + var notifier = new ConfigValueChangeNotifier(handler); + m_ConfigUpdatedNotifiers.Add(name, notifier); + return notifier.Notify(config.OptionElements.GetValue(name)); + } + + int CheckConfigOptionsChange(NameValueCollection oldOptions, NameValueCollection newOptions) + { + var changed = 0; + + if (oldOptions == null && newOptions == null) + return changed; + + var oldOptionsDict = oldOptions == null + ? new Dictionary(StringComparer.OrdinalIgnoreCase) + : Enumerable.Range(0, oldOptions.Count) + .Select(i => new KeyValuePair(oldOptions.GetKey(i), oldOptions.Get(i))) + .ToDictionary(p => p.Key, p => p.Value, StringComparer.OrdinalIgnoreCase); + + foreach(var key in newOptions.AllKeys) + { + var newValue = newOptions[key]; + + var oldValue = string.Empty; + + if (oldOptionsDict.TryGetValue(key, out oldValue)) + oldOptionsDict.Remove(key); + + if (string.Compare(newValue, oldValue) == 0) + continue; + + NotifyConfigUpdated(key, newValue); + changed++; + } + + if (oldOptionsDict.Count > 0) + { + foreach (var p in oldOptionsDict) + { + NotifyConfigUpdated(p.Key, string.Empty); + changed++; + } + } + + return changed; + } + + private void NotifyConfigUpdated(string key, string newValue) + { + IConfigValueChangeNotifier notifier; + + if (!m_ConfigUpdatedNotifiers.TryGetValue(key, out notifier)) + return; + + try + { + if (!notifier.Notify(newValue)) + throw new Exception("returned false in the handling logic"); + } + catch (Exception e) + { + Logger.Error("Failed to handle custom configuration reading, name: " + key, e); + } + } + + void IWorkItemBase.ReportPotentialConfigChange(IServerConfig config) + { + var oldConfig = this.Config; + + CheckConfigOptionsChange(oldConfig.Options, config.Options); + CheckConfigOptionsChange(oldConfig.OptionElements, config.OptionElements); + + var updatableConfig = oldConfig as ServerConfig; + + if (updatableConfig == null) + return; + + config.CopyPropertiesTo(p => p.GetCustomAttributes(typeof(HotUpdateAttribute), true).Length > 0, updatableConfig); + } + } +} diff --git a/SocketBase/AppServerBase.cs b/SocketBase/AppServerBase.cs index f5eb2c961..b2175882e 100644 --- a/SocketBase/AppServerBase.cs +++ b/SocketBase/AppServerBase.cs @@ -1245,13 +1245,32 @@ protected virtual void ExecuteCommand(TAppSession session, TRequestInfo requestI if (!cancelled) { - command.ExecuteCommand(session, requestInfo); + 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 + { + + } + } } } @@ -1358,13 +1377,23 @@ IAppSession IAppServer.CreateAppSession(ISocketSession socketSession) if (!ExecuteConnectionFilters(socketSession.RemoteEndPoint)) return NullAppSession; - var appSession = new TAppSession(); + 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. /// @@ -1380,7 +1409,7 @@ bool IAppServer.RegisterSession(IAppSession session) appSession.SocketSession.Closed += OnSocketSessionClosed; if (Config.LogBasicSessionActivity && Logger.IsInfoEnabled) - Logger.InfoFormat("A new session connected!"); + Logger.Info(session, "A new session connected!"); OnNewSessionConnected(appSession); return true; @@ -1568,16 +1597,28 @@ public string GetFilePath(string relativeFilePath) /// Connect the remote endpoint actively. /// /// The target end point. + /// The local end point. /// /// This server cannot support active connect. - Task IActiveConnector.ActiveConnect(EndPoint targetEndPoint) + 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); + 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 @@ -1638,8 +1679,18 @@ protected virtual void UpdateServerStatus(StatusInfoCollection serverStatus) serverStatus[StatusInfoKeys.RequestHandlingSpeed] = ((totalHandledRequests - totalHandledRequests0) / now.Subtract(serverStatus.CollectedTime).TotalSeconds); serverStatus[StatusInfoKeys.TotalHandledRequests] = totalHandledRequests; - serverStatus[StatusInfoKeys.AvialableSendingQueueItems] = m_SocketServer.SendingQueuePool.AvialableItemsCount; - serverStatus[StatusInfoKeys.TotalSendingQueueItems] = m_SocketServer.SendingQueuePool.TotalItemsCount; + + 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; } diff --git a/SocketBase/AppSession.cs b/SocketBase/AppSession.cs index 2156610a5..a1b592ddb 100644 --- a/SocketBase/AppSession.cs +++ b/SocketBase/AppSession.cs @@ -361,7 +361,7 @@ private void InternalSend(ArraySegment segment) //Don't retry, timeout directly if (sendTimeOut < 0) { - throw new Exception("The sending attempt timed out"); + throw new TimeoutException("The sending attempt timed out"); } var timeOutTime = sendTimeOut > 0 ? DateTime.Now.AddMilliseconds(sendTimeOut) : DateTime.Now; @@ -378,7 +378,7 @@ private void InternalSend(ArraySegment segment) //If sendTimeOut = 0, don't have timeout check if (sendTimeOut > 0 && DateTime.Now >= timeOutTime) { - throw new Exception("The sending attempt timed out"); + throw new TimeoutException("The sending attempt timed out"); } } } @@ -402,7 +402,7 @@ private bool InternalTrySend(IList> segments) } /// - /// Try to send the data segments to clinet. + /// Try to send the data segments to client. /// /// The segments. /// Indicate whether the message was pushed into the sending queue; if it returns false, the sending queue may be full or the socket is not connected @@ -427,7 +427,7 @@ private void InternalSend(IList> segments) //Don't retry, timeout directly if (sendTimeOut < 0) { - throw new Exception("The sending attempt timed out"); + throw new TimeoutException("The sending attempt timed out"); } var timeOutTime = sendTimeOut > 0 ? DateTime.Now.AddMilliseconds(sendTimeOut) : DateTime.Now; @@ -444,13 +444,13 @@ private void InternalSend(IList> segments) //If sendTimeOut = 0, don't have timeout check if (sendTimeOut > 0 && DateTime.Now >= timeOutTime) { - throw new Exception("The sending attempt timed out"); + throw new TimeoutException("The sending attempt timed out"); } } } /// - /// Sends the data segments to clinet. + /// Sends the data segments to client. /// /// The segments. public virtual void Send(IList> segments) @@ -482,6 +482,15 @@ protected void SetNextReceiveFilter(IReceiveFilter nextReceiveFilt m_ReceiveFilter = nextReceiveFilter; } + /// + /// Gets the maximum allowed length of the request. + /// + /// + protected virtual int GetMaxRequestLength() + { + return AppServer.Config.MaxRequestLength; + } + /// /// Filters the request. /// @@ -501,6 +510,8 @@ TRequestInfo FilterRequest(byte[] readBuffer, int offset, int length, bool toBeC return null; } + var currentRequestLength = m_ReceiveFilter.LeftBufferSize; + var requestInfo = m_ReceiveFilter.Filter(readBuffer, offset, length, toBeCopied, out rest); if (m_ReceiveFilter.State == FilterState.Error) @@ -517,14 +528,24 @@ TRequestInfo FilterRequest(byte[] readBuffer, int offset, int length, bool toBeC if (requestInfo == null) { - int leftBufferCount = m_ReceiveFilter.LeftBufferSize; - if (leftBufferCount >= AppServer.Config.MaxRequestLength) - { - if (Logger.IsErrorEnabled) - Logger.Error(this, string.Format("Max request length: {0}, current processed length: {1}", AppServer.Config.MaxRequestLength, leftBufferCount)); - Close(CloseReason.ProtocolError); - return null; - } + //current buffered length + currentRequestLength = m_ReceiveFilter.LeftBufferSize; + } + else + { + //current request length + currentRequestLength = currentRequestLength + length - rest; + } + + var maxRequestLength = GetMaxRequestLength(); + + if (currentRequestLength >= maxRequestLength) + { + if (Logger.IsErrorEnabled) + Logger.Error(this, string.Format("Max request length: {0}, current processed length: {1}", maxRequestLength, currentRequestLength)); + + Close(CloseReason.ProtocolError); + return null; } //If next Receive filter wasn't set, still use current Receive filter in next round received data processing diff --git a/SocketBase/Command/CommandInfo.cs b/SocketBase/Command/CommandInfo.cs index c1de7bd99..2cc56be4f 100644 --- a/SocketBase/Command/CommandInfo.cs +++ b/SocketBase/Command/CommandInfo.cs @@ -24,12 +24,11 @@ public CommandInfo(TCommand command, IEnumerable globalF allFilters.AddRange(globalFilters); } - var filters = AppServer.GetCommandFilterAttributes(command.GetType()); + IEnumerable filters = command is ICommandFilterProvider ? + (command as ICommandFilterProvider).GetFilters() : AppServer.GetCommandFilterAttributes(command.GetType()); - if (filters.Any()) - { + if (filters != null && filters.Any()) allFilters.AddRange(filters); - } if (allFilters.Any()) { diff --git a/SocketBase/Command/ICommandFilterProvider.cs b/SocketBase/Command/ICommandFilterProvider.cs new file mode 100644 index 000000000..0f04f80ce --- /dev/null +++ b/SocketBase/Command/ICommandFilterProvider.cs @@ -0,0 +1,20 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using SuperSocket.SocketBase.Metadata; + +namespace SuperSocket.SocketBase.Command +{ + /// + /// The basic interface for CommandFilter + /// + public interface ICommandFilterProvider + { + /// + /// Gets the filters which assosiated with this command object. + /// + /// + IEnumerable GetFilters(); + } +} diff --git a/SocketBase/CommandExecutingContext.cs b/SocketBase/CommandExecutingContext.cs index 98dfe4cb9..2afb4d114 100644 --- a/SocketBase/CommandExecutingContext.cs +++ b/SocketBase/CommandExecutingContext.cs @@ -27,6 +27,22 @@ public class CommandExecutingContext /// public ICommand CurrentCommand { get; private set; } + /// + /// Gets the exception. + /// + /// + /// The exception. + /// + public Exception Exception { get; internal set; } + + /// + /// Gets a value indicating whether [exception handled]. + /// + /// + /// true if [exception handled]; otherwise, false. + /// + public bool ExceptionHandled { get; internal set; } + /// /// Gets or sets a value indicating whether this command executing is cancelled. /// diff --git a/SocketBase/Config/CertificateConfig.cs b/SocketBase/Config/CertificateConfig.cs index 1509eacd6..40c9f4f3d 100644 --- a/SocketBase/Config/CertificateConfig.cs +++ b/SocketBase/Config/CertificateConfig.cs @@ -53,6 +53,11 @@ public class CertificateConfig : ICertificateConfig /// public bool ClientCertificateRequired { get; set; } + /// + /// Gets/sets a value that will be used to instantiate the X509Certificate2 object in the CertificateManager + /// + public X509KeyStorageFlags KeyStorageFlags { get; set; } + #endregion } } diff --git a/SocketBase/Config/HotUpdateAttribute.cs b/SocketBase/Config/HotUpdateAttribute.cs new file mode 100644 index 000000000..188dffb68 --- /dev/null +++ b/SocketBase/Config/HotUpdateAttribute.cs @@ -0,0 +1,17 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace SuperSocket.SocketBase.Config +{ + /// + /// the attribute to mark which property of ServerConfig support hot update + /// + [AttributeUsage(AttributeTargets.Property, AllowMultiple = false)] + public class HotUpdateAttribute : Attribute + { + + } +} diff --git a/SocketBase/Config/ICertificateConfig.cs b/SocketBase/Config/ICertificateConfig.cs index aa58ccfb1..d83fcbabc 100644 --- a/SocketBase/Config/ICertificateConfig.cs +++ b/SocketBase/Config/ICertificateConfig.cs @@ -50,5 +50,10 @@ public interface ICertificateConfig /// true if [client certificate required]; otherwise, false. /// bool ClientCertificateRequired { get; } + + /// + /// Gets a value that will be used to instantiate the X509Certificate2 object in the CertificateManager + /// + X509KeyStorageFlags KeyStorageFlags { get; } } } diff --git a/SocketBase/Config/ServerConfig.cs b/SocketBase/Config/ServerConfig.cs index b21b29b94..404f810d8 100644 --- a/SocketBase/Config/ServerConfig.cs +++ b/SocketBase/Config/ServerConfig.cs @@ -54,6 +54,36 @@ public partial class ServerConfig : IServerConfig /// public const int DefaultIdleSessionTimeOut = 300; + + /// + /// The default send buffer size + /// + public const int DefaultSendBufferSize = 2048; + + + /// + /// The default session snapshot interval + /// + public const int DefaultSessionSnapshotInterval = 5; + + /// + /// The default keep alive time + /// + public const int DefaultKeepAliveTime = 600; // 60 * 10 = 10 minutes + + + /// + /// The default keep alive interval + /// + public const int DefaultKeepAliveInterval = 60; // 60 seconds + + + /// + /// The default listen backlog + /// + public const int DefaultListenBacklog = 100; + + /// /// Initializes a new instance of the class. /// @@ -88,14 +118,17 @@ public ServerConfig() MaxConnectionNumber = DefaultMaxConnectionNumber; Mode = SocketMode.Tcp; MaxRequestLength = DefaultMaxRequestLength; - KeepAliveTime = 10 * 60;// 10 minutes - KeepAliveInterval = 60;// 60 seconds - ListenBacklog = 100; + KeepAliveTime = DefaultKeepAliveTime; + KeepAliveInterval = DefaultKeepAliveInterval; + ListenBacklog = DefaultListenBacklog; ReceiveBufferSize = DefaultReceiveBufferSize; SendingQueueSize = DefaultSendingQueueSize; SendTimeOut = DefaultSendTimeout; ClearIdleSessionInterval = DefaultClearIdleSessionInterval; IdleSessionTimeOut = DefaultIdleSessionTimeOut; + SendBufferSize = DefaultSendBufferSize; + LogBasicSessionActivity = true; + SessionSnapshotInterval = DefaultSessionSnapshotInterval; } #region IServerConfig Members @@ -135,11 +168,13 @@ public ServerConfig() /// /// Gets/sets the options. /// + [HotUpdate] public NameValueCollection Options { get; set; } /// /// Gets the option elements. /// + [HotUpdate] public NameValueCollection OptionElements { get; set; } /// @@ -201,6 +236,7 @@ public ServerConfig() /// /// true if log command; otherwise, false. /// + [HotUpdate] public bool LogCommand { get; set; } /// @@ -225,6 +261,7 @@ public ServerConfig() /// /// The idle session time out. /// + [HotUpdate] public int IdleSessionTimeOut { get; set; } /// @@ -246,6 +283,7 @@ public ServerConfig() /// /// The length of the max request. /// + [HotUpdate] public int MaxRequestLength { get; set; } /// @@ -330,6 +368,7 @@ public virtual TConfig GetChildConfig(string childConfigName) /// /// true if [log basic session activity]; otherwise, false. /// + [HotUpdate] public bool LogBasicSessionActivity { get; set; } /// @@ -338,6 +377,7 @@ public virtual TConfig GetChildConfig(string childConfigName) /// /// true if [log all socket exception]; otherwise, false. /// + [HotUpdate] public bool LogAllSocketException { get; set; } /// diff --git a/SocketBase/IActiveConnector.cs b/SocketBase/IActiveConnector.cs index c600bccac..c604c5d14 100644 --- a/SocketBase/IActiveConnector.cs +++ b/SocketBase/IActiveConnector.cs @@ -40,5 +40,13 @@ public interface IActiveConnector /// The target end point. /// Task ActiveConnect(EndPoint targetEndPoint); + + /// + /// Connect the target endpoint actively. + /// + /// The target end point. + /// The local end point. + /// + Task ActiveConnect(EndPoint targetEndPoint, EndPoint localEndPoint); } } diff --git a/SocketBase/IAppServer.cs b/SocketBase/IAppServer.cs index 706da6ca1..6b2dada2b 100644 --- a/SocketBase/IAppServer.cs +++ b/SocketBase/IAppServer.cs @@ -41,12 +41,6 @@ public interface IAppServer : IWorkItem, ILoggerProvider /// Gets the Receive filter factory. /// object ReceiveFilterFactory { get; } - - - /// - /// Gets the server's config. - /// - IServerConfig Config { get; } /// /// Gets the certificate of current server. diff --git a/SocketBase/IBootstrap.cs b/SocketBase/IBootstrap.cs index 1d5be57f5..fc8109fa8 100644 --- a/SocketBase/IBootstrap.cs +++ b/SocketBase/IBootstrap.cs @@ -111,4 +111,31 @@ public interface IBootstrap /// string BaseDirectory { get; } } + + /// + /// The bootstrap interface to support add new server instance in runtime + /// + public interface IDynamicBootstrap + { + /// + /// Adds a new server into the bootstrap. + /// + /// The new server's config. + /// + bool Add(IServerConfig config); + + /// + /// Adds a new server into the bootstrap and then start it. + /// + /// The new server's config. + /// + bool AddAndStart(IServerConfig config); + + + /// + /// Removes the server instance which is specified by name. + /// + /// The name of the server instance to be removed. + void Remove(string name); + } } diff --git a/SocketBase/IWorkItem.cs b/SocketBase/IWorkItem.cs index d5f499426..21298fc1a 100644 --- a/SocketBase/IWorkItem.cs +++ b/SocketBase/IWorkItem.cs @@ -17,6 +17,14 @@ public interface IWorkItemBase : IStatusInfoSource, ISystemEndPoint /// string Name { get; } + /// + /// Gets the server's config. + /// + /// + /// The server's config. + /// + IServerConfig Config { get; } + /// /// Starts this server instance. @@ -24,6 +32,12 @@ public interface IWorkItemBase : IStatusInfoSource, ISystemEndPoint /// return true if start successfull, else false bool Start(); + /// + /// Reports the potential configuration change. + /// + /// The server config which may be changed. + void ReportPotentialConfigChange(IServerConfig config); + /// /// Stops this server instance. /// diff --git a/SocketBase/Protocol/BasicRequestInfoParser.cs b/SocketBase/Protocol/BasicRequestInfoParser.cs index a69c65dce..efcc986c2 100644 --- a/SocketBase/Protocol/BasicRequestInfoParser.cs +++ b/SocketBase/Protocol/BasicRequestInfoParser.cs @@ -56,7 +56,7 @@ public StringRequestInfo ParseRequestInfo(string source) if (pos > 0) { name = source.Substring(0, pos); - param = source.Substring(pos + 1); + param = source.Substring(pos + m_Spliter.Length); } else { diff --git a/SocketBase/Protocol/TerminatorReceiveFilter.cs b/SocketBase/Protocol/TerminatorReceiveFilter.cs index 3d9cb55df..997d91d87 100644 --- a/SocketBase/Protocol/TerminatorReceiveFilter.cs +++ b/SocketBase/Protocol/TerminatorReceiveFilter.cs @@ -92,8 +92,17 @@ public override TRequestInfo Filter(byte[] readBuffer, int offset, int length, b } var findLen = result - offset; + var currentMatched = m_SearchState.Mark.Length - prevMatched; - rest = length - findLen - (m_SearchState.Mark.Length - prevMatched); + //The prev matched part is not belong to the current matched terminator mark + if (prevMatched > 0 && findLen != 0) + { + //rest prevMatched to 0 + prevMatched = 0; + currentMatched = m_SearchState.Mark.Length; + } + + rest = length - findLen - currentMatched; TRequestInfo requestInfo; diff --git a/SocketBase/Security/CertificateManager.cs b/SocketBase/Security/CertificateManager.cs index 8c1a472a2..c8a9799c7 100644 --- a/SocketBase/Security/CertificateManager.cs +++ b/SocketBase/Security/CertificateManager.cs @@ -26,7 +26,8 @@ internal static X509Certificate Initialize(ICertificateConfig cerConfig, FuncGlobalAssemblyInfo.cs + @@ -70,12 +71,14 @@ + + diff --git a/SocketBase/SuperSocket.SocketBase.Net40.csproj b/SocketBase/SuperSocket.SocketBase.Net40.csproj index ca5beaee0..3d2260ef0 100644 --- a/SocketBase/SuperSocket.SocketBase.Net40.csproj +++ b/SocketBase/SuperSocket.SocketBase.Net40.csproj @@ -71,8 +71,11 @@ GlobalAssemblyInfo.cs + + + diff --git a/SocketBase/SuperSocket.SocketBase.Net45.csproj b/SocketBase/SuperSocket.SocketBase.Net45.csproj index 41cd62a19..d70d2c699 100644 --- a/SocketBase/SuperSocket.SocketBase.Net45.csproj +++ b/SocketBase/SuperSocket.SocketBase.Net45.csproj @@ -75,12 +75,14 @@ GlobalAssemblyInfo.cs + + @@ -90,6 +92,7 @@ + diff --git a/SocketEngine/AppDomainAppServer.cs b/SocketEngine/AppDomainAppServer.cs index cafa3cdb4..8cc2fc63f 100644 --- a/SocketEngine/AppDomainAppServer.cs +++ b/SocketEngine/AppDomainAppServer.cs @@ -53,7 +53,7 @@ protected override IWorkItemBase Start() null, new object[0]); - if (!appServer.Setup(Bootstrap, ServerConfig, Factories)) + if (!appServer.Setup(Bootstrap, Config, Factories)) { OnExceptionThrown(new Exception("Failed to setup MarshalAppServer")); return null; diff --git a/SocketEngine/AppDomainBootstrap.Net40.cs b/SocketEngine/AppDomainBootstrap.Net40.cs new file mode 100644 index 000000000..5fc36a18d --- /dev/null +++ b/SocketEngine/AppDomainBootstrap.Net40.cs @@ -0,0 +1,31 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using SuperSocket.SocketBase; +using SuperSocket.SocketBase.Config; + +namespace SuperSocket.SocketEngine +{ + partial class AppDomainBootstrap : IDynamicBootstrap + { + bool IDynamicBootstrap.Add(IServerConfig config) + { + var dynamicBootstrap = m_InnerBootstrap as IDynamicBootstrap; + return dynamicBootstrap.Add(config); + } + + bool IDynamicBootstrap.AddAndStart(IServerConfig config) + { + var dynamicBootstrap = m_InnerBootstrap as IDynamicBootstrap; + return dynamicBootstrap.AddAndStart(config); + } + + void IDynamicBootstrap.Remove(string name) + { + var dynamicBootstrap = m_InnerBootstrap as IDynamicBootstrap; + dynamicBootstrap.Remove(name); + } + } +} diff --git a/SocketEngine/AppDomainBootstrap.cs b/SocketEngine/AppDomainBootstrap.cs index 134befa22..eca76953c 100644 --- a/SocketEngine/AppDomainBootstrap.cs +++ b/SocketEngine/AppDomainBootstrap.cs @@ -93,7 +93,7 @@ internal override WorkItemFactoryInfoLoader GetWorkItemFactoryInfoLoader(IConfig /// /// AppDomainBootstrap /// - class AppDomainBootstrap : MarshalByRefObject, IBootstrap + partial class AppDomainBootstrap : MarshalByRefObject, ILoggerProvider, IBootstrap, IDisposable { private IBootstrap m_InnerBootstrap; @@ -113,6 +113,21 @@ public IRootConfig Config get { return m_InnerBootstrap.Config; } } + /// + /// Gets the bootstrap logger. + /// + ILog ILoggerProvider.Logger + { + get + { + var loggerProvider = m_InnerBootstrap as ILoggerProvider; + + if (loggerProvider == null) + return null; + + return loggerProvider.Logger; + } + } /// /// Gets the startup config file. /// @@ -225,5 +240,12 @@ public string BaseDirectory { get { return m_InnerBootstrap.BaseDirectory; } } + + void IDisposable.Dispose() + { + var disposableBootstrap = m_InnerBootstrap as IDisposable; + if (disposableBootstrap != null) + disposableBootstrap.Dispose(); + } } } diff --git a/SocketEngine/AsyncSocket/SocketAsyncEventArgsProxy.cs b/SocketEngine/AsyncSocket/SocketAsyncEventArgsProxy.cs index df73667c9..c675ec1e0 100644 --- a/SocketEngine/AsyncSocket/SocketAsyncEventArgsProxy.cs +++ b/SocketEngine/AsyncSocket/SocketAsyncEventArgsProxy.cs @@ -14,16 +14,25 @@ class SocketAsyncEventArgsProxy public int OrigOffset { get; private set; } + public bool IsRecyclable { get; private set; } + private SocketAsyncEventArgsProxy() { } public SocketAsyncEventArgsProxy(SocketAsyncEventArgs socketEventArgs) + : this(socketEventArgs, true) + { + + } + + public SocketAsyncEventArgsProxy(SocketAsyncEventArgs socketEventArgs, bool isRecyclable) { SocketEventArgs = socketEventArgs; OrigOffset = socketEventArgs.Offset; SocketEventArgs.Completed += new EventHandler(SocketEventArgs_Completed); + IsRecyclable = isRecyclable; } static void SocketEventArgs_Completed(object sender, SocketAsyncEventArgs e) diff --git a/SocketEngine/AsyncSocketServer.cs b/SocketEngine/AsyncSocketServer.cs index 26fbc855a..a87a10fd0 100644 --- a/SocketEngine/AsyncSocketServer.cs +++ b/SocketEngine/AsyncSocketServer.cs @@ -182,20 +182,36 @@ public override void ResetSessionSecurity(IAppSession session, SslProtocols secu void SessionClosed(ISocketSession session, CloseReason reason) { var socketSession = session as IAsyncSocketSessionBase; + if (socketSession == null) + return; + + var proxy = socketSession.SocketAsyncProxy; + proxy.Reset(); + var args = proxy.SocketEventArgs; + + var serverState = AppServer.State; + var pool = this.m_ReadWritePool; - if (socketSession != null && this.m_ReadWritePool != null) + if (pool == null || serverState == ServerState.Stopping || serverState == ServerState.NotStarted) { - var proxy = socketSession.SocketAsyncProxy; - proxy.Reset(); + if(!Environment.HasShutdownStarted && !AppDomain.CurrentDomain.IsFinalizingForUnload()) + args.Dispose(); + return; + } - if (proxy.OrigOffset != proxy.SocketEventArgs.Offset) - { - proxy.SocketEventArgs.SetBuffer(proxy.OrigOffset, AppServer.Config.ReceiveBufferSize); - } + if (proxy.OrigOffset != args.Offset) + { + args.SetBuffer(proxy.OrigOffset, AppServer.Config.ReceiveBufferSize); + } - if (m_ReadWritePool != null) - m_ReadWritePool.Push(proxy); + if (!proxy.IsRecyclable) + { + //cannot be recycled, so release the resource and don't return it to the pool + args.Dispose(); + return; } + + pool.Push(proxy); } public override void Stop() @@ -210,6 +226,9 @@ public override void Stop() base.Stop(); + foreach (var item in m_ReadWritePool) + item.SocketEventArgs.Dispose(); + m_ReadWritePool = null; m_BufferManager = null; IsRunning = false; @@ -230,9 +249,22 @@ public ActiveConnectState(TaskCompletionSource taskSource, } Task IActiveConnector.ActiveConnect(EndPoint targetEndPoint) + { + return ((IActiveConnector)this).ActiveConnect(targetEndPoint, null); + } + + Task IActiveConnector.ActiveConnect(EndPoint targetEndPoint, EndPoint localEndPoint) { var taskSource = new TaskCompletionSource(); var socket = new Socket(targetEndPoint.AddressFamily, SocketType.Stream, ProtocolType.Tcp); + + if (localEndPoint != null) + { + socket.ExclusiveAddressUse = false; + socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true); + socket.Bind(localEndPoint); + } + socket.BeginConnect(targetEndPoint, OnActiveConnectCallback, new ActiveConnectState(taskSource, socket)); return taskSource.Task; } diff --git a/SocketEngine/AsyncSocketSession.cs b/SocketEngine/AsyncSocketSession.cs index a5605c02a..3568fb22b 100644 --- a/SocketEngine/AsyncSocketSession.cs +++ b/SocketEngine/AsyncSocketSession.cs @@ -90,6 +90,17 @@ void OnSendingCompleted(object sender, SocketAsyncEventArgs e) return; } + var count = queue.Sum(q => q.Count); + + if (count != e.BytesTransferred) + { + queue.InternalTrim(e.BytesTransferred); + AppSession.Logger.InfoFormat("{0} of {1} were transferred, send the rest {2} bytes right now.", e.BytesTransferred, count, queue.Sum(q => q.Count)); + ClearPrevSendState(e); + SendAsync(queue); + return; + } + ClearPrevSendState(e); base.OnSendingCompleted(queue); } @@ -130,17 +141,16 @@ private void StartReceive(SocketAsyncEventArgs e, int offsetDelta) e.SetBuffer(predictOffset, Config.ReceiveBufferSize - offsetDelta); } - if (IsInClosingOrClosed) + // the connection is closing or closed + if (!OnReceiveStarted()) return; - OnReceiveStarted(); willRaiseEvent = Client.ReceiveAsync(e); } catch (Exception exc) { LogError(exc); - - OnReceiveError(CloseReason.SocketError); + OnReceiveTerminated(CloseReason.SocketError); return; } @@ -217,7 +227,7 @@ public void ProcessReceive(SocketAsyncEventArgs e) { if (!ProcessCompleted(e)) { - OnReceiveError(CloseReason.ClientClosing); + OnReceiveTerminated(e.SocketError == SocketError.Success ? CloseReason.ClientClosing : CloseReason.SocketError); return; } @@ -238,7 +248,24 @@ public void ProcessReceive(SocketAsyncEventArgs e) //read the next block of data sent from the client StartReceive(e, offsetDelta); - } + } + + protected override void OnClosed(CloseReason reason) + { + var sae = m_SocketEventArgSend; + + if (sae == null) + { + base.OnClosed(reason); + return; + } + + if (Interlocked.CompareExchange(ref m_SocketEventArgSend, null, sae) == sae) + { + sae.Dispose(); + base.OnClosed(reason); + } + } public override void ApplySecureProtocol() { diff --git a/SocketEngine/AsyncStreamSocketSession.cs b/SocketEngine/AsyncStreamSocketSession.cs index f2e4f33a4..af430d876 100644 --- a/SocketEngine/AsyncStreamSocketSession.cs +++ b/SocketEngine/AsyncStreamSocketSession.cs @@ -100,8 +100,7 @@ private void OnSessionStarting() catch (Exception e) { LogError(e); - - OnReceiveError(CloseReason.SocketError); + OnReceiveTerminated(CloseReason.SocketError); return; } @@ -122,14 +121,13 @@ private void OnStreamEndRead(IAsyncResult result) catch (Exception e) { LogError(e); - - OnReceiveError(CloseReason.SocketError); + OnReceiveTerminated(CloseReason.SocketError); return; } if (thisRead <= 0) { - OnReceiveError(CloseReason.ClientClosing); + OnReceiveTerminated(CloseReason.ClientClosing); return; } @@ -162,7 +160,7 @@ private void OnStreamEndRead(IAsyncResult result) catch (Exception exc) { LogError(exc); - OnReceiveError(CloseReason.SocketError); + OnReceiveTerminated(CloseReason.SocketError); return; } } @@ -198,9 +196,13 @@ private IAsyncResult BeginInitStream(AsyncCallback asyncCallback) IAsyncResult result = null; var certConfig = AppSession.Config.Certificate; + var secureProtocol = SecureProtocol; - switch (SecureProtocol) + switch (secureProtocol) { + case (SslProtocols.None): + m_Stream = new NetworkStream(Client); + break; case (SslProtocols.Default): case (SslProtocols.Tls): case (SslProtocols.Ssl3): @@ -212,7 +214,8 @@ private IAsyncResult BeginInitStream(AsyncCallback asyncCallback) result = ssl2Stream.BeginAuthenticateAsServer(AppSession.AppServer.Certificate, certConfig.ClientCertificateRequired, SslProtocols.Ssl2, false, asyncCallback, ssl2Stream); break; default: - m_Stream = new NetworkStream(Client); + var unknownSslStream = CreateSslStream(certConfig); + result = unknownSslStream.BeginAuthenticateAsServer(AppSession.AppServer.Certificate, certConfig.ClientCertificateRequired, secureProtocol, false, asyncCallback, unknownSslStream); break; } diff --git a/SocketEngine/BootstrapFactory.cs b/SocketEngine/BootstrapFactory.cs index bb2daf5f6..702bd2c7f 100644 --- a/SocketEngine/BootstrapFactory.cs +++ b/SocketEngine/BootstrapFactory.cs @@ -24,12 +24,21 @@ public static IBootstrap CreateBootstrap(IConfigurationSource config) if (config == null) throw new ArgumentNullException("config"); + IBootstrap bootstrap; + if (config.Isolation == IsolationMode.AppDomain) - return new AppDomainBootstrap(config); + bootstrap = new AppDomainBootstrap(config); else if (config.Isolation == IsolationMode.Process) - return new ProcessBootstrap(config); + bootstrap = new ProcessBootstrap(config); else - return new DefaultBootstrap(config); + bootstrap = new DefaultBootstrap(config); + + var section = config as ConfigurationSection; + + if (section != null) + ConfigurationWatcher.Watch(section, bootstrap); + + return bootstrap; } /// diff --git a/SocketEngine/Configuration/CertificateConfig.cs b/SocketEngine/Configuration/CertificateConfig.cs index 6b6f6233b..6c89df7d0 100644 --- a/SocketEngine/Configuration/CertificateConfig.cs +++ b/SocketEngine/Configuration/CertificateConfig.cs @@ -95,6 +95,18 @@ public bool ClientCertificateRequired } } + /// + /// Gets a value that will be used to instantiate the X509Certificate2 object in the CertificateManager + /// + [ConfigurationProperty("keyStorageFlags", IsRequired = false, DefaultValue = X509KeyStorageFlags.DefaultKeySet)] + public X509KeyStorageFlags KeyStorageFlags + { + get + { + return (X509KeyStorageFlags)this["keyStorageFlags"]; + } + } + #endregion ICertificateConfig Members } } diff --git a/SocketEngine/Configuration/Server.cs b/SocketEngine/Configuration/Server.cs index 6774c2f87..fa09ec483 100644 --- a/SocketEngine/Configuration/Server.cs +++ b/SocketEngine/Configuration/Server.cs @@ -126,7 +126,7 @@ public int ReceiveBufferSize /// /// The size of the send buffer. /// - [ConfigurationProperty("sendBufferSize", IsRequired = false, DefaultValue = 2048)] + [ConfigurationProperty("sendBufferSize", IsRequired = false, DefaultValue = ServerConfig.DefaultSendBufferSize)] public int SendBufferSize { get { return (int)this["sendBufferSize"]; } @@ -276,7 +276,7 @@ public bool DisableSessionSnapshot /// /// Gets the interval to taking snapshot for all live sessions. /// - [ConfigurationProperty("sessionSnapshotInterval", IsRequired = false, DefaultValue = 5)] + [ConfigurationProperty("sessionSnapshotInterval", IsRequired = false, DefaultValue = ServerConfig.DefaultSessionSnapshotInterval)] public int SessionSnapshotInterval { get @@ -315,7 +315,7 @@ public string CommandLoader /// /// Gets the start keep alive time, in seconds /// - [ConfigurationProperty("keepAliveTime", IsRequired = false, DefaultValue = 600)] + [ConfigurationProperty("keepAliveTime", IsRequired = false, DefaultValue = ServerConfig.DefaultKeepAliveTime)] public int KeepAliveTime { get @@ -327,7 +327,7 @@ public int KeepAliveTime /// /// Gets the keep alive interval, in seconds. /// - [ConfigurationProperty("keepAliveInterval", IsRequired = false, DefaultValue = 60)] + [ConfigurationProperty("keepAliveInterval", IsRequired = false, DefaultValue = ServerConfig.DefaultKeepAliveInterval)] public int KeepAliveInterval { get @@ -339,7 +339,7 @@ public int KeepAliveInterval /// /// Gets the backlog size of socket listening. /// - [ConfigurationProperty("listenBacklog", IsRequired = false, DefaultValue = 100)] + [ConfigurationProperty("listenBacklog", IsRequired = false, DefaultValue = ServerConfig.DefaultListenBacklog)] public int ListenBacklog { get diff --git a/SocketEngine/Configuration/ServerCollection.cs b/SocketEngine/Configuration/ServerCollection.cs index 8ee849605..c2b782784 100644 --- a/SocketEngine/Configuration/ServerCollection.cs +++ b/SocketEngine/Configuration/ServerCollection.cs @@ -13,5 +13,22 @@ namespace SuperSocket.SocketEngine.Configuration [ConfigurationCollection(typeof(Server), AddItemName = "server")] public class ServerCollection : GenericConfigurationElementCollection { + /// + /// Adds the new server element. + /// + /// The new server. + public void AddNew(Server newServer) + { + base.BaseAdd(newServer); + } + + /// + /// Removes the specified server from the configuration. + /// + /// The name. + public void Remove(string name) + { + base.BaseRemove(name); + } } } diff --git a/SocketEngine/Configuration/SocketServiceConfig.cs b/SocketEngine/Configuration/SocketServiceConfig.cs index b8f36e42c..acd76ffab 100644 --- a/SocketEngine/Configuration/SocketServiceConfig.cs +++ b/SocketEngine/Configuration/SocketServiceConfig.cs @@ -219,6 +219,27 @@ protected override bool OnDeserializeUnrecognizedElement(string elementName, Sys return true; } + /// + /// Gets a value indicating whether an unknown attribute is encountered during deserialization. + /// + /// The name of the unrecognized attribute. + /// The value of the unrecognized attribute. + /// + /// true when an unknown attribute is encountered while deserializing; otherwise, false. + /// + protected override bool OnDeserializeUnrecognizedAttribute(string name, string value) + { + const string xmlns = "xmlns"; + const string xmlnsPrefix = "xmlns:"; + const string xsiPrefix = "xsi:"; + + //for configuration intellisense, allow these unrecognized attributes: xmlns, xmlns:*, xsi:* + if (name.Equals(xmlns) || name.StartsWith(xmlnsPrefix) || name.StartsWith(xsiPrefix)) + return true; + + return false; + } + /// /// Gets the child config. /// diff --git a/SocketEngine/ConfigurationWatcher.cs b/SocketEngine/ConfigurationWatcher.cs new file mode 100644 index 000000000..be8dab3d8 --- /dev/null +++ b/SocketEngine/ConfigurationWatcher.cs @@ -0,0 +1,124 @@ +using System; +using System.Collections.Generic; +using System.Configuration; +using System.IO; +using System.Linq; +using System.Reflection; +using System.Text; +using SuperSocket.Common; +using SuperSocket.SocketBase; +using SuperSocket.SocketBase.Config; + +namespace SuperSocket.SocketEngine +{ + /// + /// The configuration file watcher, it is used for hot configuration updating + /// + public static class ConfigurationWatcher + { + private static FileSystemWatcher m_Watcher; + + private static DateTime m_LastUpdatedTime; + + /// + /// Watches the specified configuration section. + /// + /// The configuration section. + /// The bootstrap. + public static void Watch(ConfigurationSection configSection, IBootstrap bootstrap) + { + if (configSection == null) + throw new ArgumentNullException("configSection"); + + if (bootstrap == null) + throw new ArgumentNullException("bootstrap"); + + var sectionName = configSection.SectionInformation.Name; + + var configSourceFile = bootstrap.StartupConfigFile; + + if (string.IsNullOrEmpty(configSourceFile)) + throw new Exception("Cannot get your configuration file's location."); + + m_Watcher = new FileSystemWatcher(Path.GetDirectoryName(configSourceFile), Path.GetFileName(configSourceFile)); + m_Watcher.IncludeSubdirectories = false; + m_Watcher.NotifyFilter = NotifyFilters.LastWrite | NotifyFilters.Size; + m_Watcher.Changed += (s, e) => + { + var filePath = e.FullPath; + + if (!NeedsLoadConfig(filePath)) + return; + + lock (m_Watcher) + { + if (!NeedsLoadConfig(filePath)) + return; + + OnConfigFileUpdated(filePath, sectionName, bootstrap); + m_LastUpdatedTime = DateTime.Now; + } + }; + + m_Watcher.EnableRaisingEvents = true; + } + + internal static void Pause() + { + m_Watcher.EnableRaisingEvents = false; + } + + internal static void Resume() + { + m_Watcher.EnableRaisingEvents = true; + } + + private static bool NeedsLoadConfig(string filePath) + { + return File.GetLastWriteTime(filePath) > m_LastUpdatedTime; + } + + private static void OnConfigFileUpdated(string filePath, string sectionName, IBootstrap bootstrap) + { + var fileMap = new ExeConfigurationFileMap(); + fileMap.ExeConfigFilename = filePath; + + System.Configuration.Configuration config; + + try + { + config = ConfigurationManager.OpenMappedExeConfiguration(fileMap, ConfigurationUserLevel.None); + } + catch(Exception e) + { + var loggerProvider = bootstrap as ILoggerProvider; + + if (loggerProvider != null) + { + var logger = loggerProvider.Logger; + + if (logger != null) + logger.Error("Configuraton loading error.", e); + } + + return; + } + + var configSource = config.GetSection(sectionName) as IConfigurationSource; + + if (configSource == null) + return; + + foreach (var serverConfig in configSource.Servers) + { + var server = bootstrap.AppServers.FirstOrDefault(x => + x.Name.Equals(serverConfig.Name, StringComparison.OrdinalIgnoreCase)); + + if (server == null) + continue; + + server.ReportPotentialConfigChange(new ServerConfig(serverConfig)); + } + } + } +} diff --git a/SocketEngine/DefaultBootstrap.Net40.cs b/SocketEngine/DefaultBootstrap.Net40.cs new file mode 100644 index 000000000..a76f33cc0 --- /dev/null +++ b/SocketEngine/DefaultBootstrap.Net40.cs @@ -0,0 +1,123 @@ +using System; +using System.Collections.Generic; +using System.Configuration; +using System.Linq; +using System.Text; +using SuperSocket.Common; +using SuperSocket.SocketBase; +using SuperSocket.SocketBase.Config; +using SuperSocket.SocketEngine.Configuration; + +namespace SuperSocket.SocketEngine +{ + public partial class DefaultBootstrap : IDynamicBootstrap + { + IWorkItem AddNewServer(IServerConfig config) + { + if (config == null) + throw new ArgumentNullException("config"); + + if (string.IsNullOrEmpty(config.Name)) + throw new ArgumentException("The new server's name cannot be empty.", "config"); + + if (!m_Initialized) + throw new Exception("The bootstrap must be initialized already!"); + + if (m_AppServers.Any(s => config.Name.Equals(s.Name, StringComparison.OrdinalIgnoreCase))) + { + m_GlobalLog.ErrorFormat("The new server's name '{0}' has been taken by another server.", config.Name); + return null; + } + + var configSource = new ConfigurationSource(m_Config); + configSource.Servers = new IServerConfig[] { new ServerConfig(config) }; + + IEnumerable workItemFactories; + + using (var factoryInfoLoader = GetWorkItemFactoryInfoLoader(configSource, null)) + { + try + { + workItemFactories = factoryInfoLoader.LoadResult((c) => c); + } + catch (Exception e) + { + if (m_GlobalLog.IsErrorEnabled) + m_GlobalLog.Error(e); + + return null; + } + } + + var server = InitializeAndSetupWorkItem(workItemFactories.FirstOrDefault()); + + if (server != null) + { + m_AppServers.Add(server); + + if (!m_Config.DisablePerformanceDataCollector) + { + ResetPerfMoniter(); + } + + var section = m_Config as SocketServiceConfig; + + if (section != null) //file configuration + { + var serverConfig = new Server(); + serverConfig.LoadFrom(config); + section.Servers.AddNew(serverConfig); + ConfigurationWatcher.Pause(); + section.GetCurrentConfiguration().Save(ConfigurationSaveMode.Minimal); + ConfigurationWatcher.Resume(); + } + } + + return server; + } + + bool IDynamicBootstrap.Add(IServerConfig config) + { + var newWorkItem = AddNewServer(config); + return newWorkItem != null; + } + + bool IDynamicBootstrap.AddAndStart(IServerConfig config) + { + var newWorkItem = AddNewServer(config); + + if (newWorkItem == null) + return false; + + return newWorkItem.Start(); + } + + void IDynamicBootstrap.Remove(string name) + { + if (string.IsNullOrEmpty(name)) + throw new ArgumentNullException("name"); + + var server = m_AppServers.FirstOrDefault(s => s.Name.Equals(name, StringComparison.OrdinalIgnoreCase)); + + if (server == null) + throw new Exception("The server is not found."); + + if (server.State != ServerState.NotStarted) + throw new Exception("The server is running now, you cannot remove it. Please stop it at first."); + + m_AppServers.Remove(server); + + ResetPerfMoniter(); + + var section = m_Config as SocketServiceConfig; + + if (section != null) //file configuration + { + section.Servers.Remove(name); + ConfigurationWatcher.Pause(); + section.GetCurrentConfiguration().Save(ConfigurationSaveMode.Minimal); + ConfigurationWatcher.Resume(); + } + } + } +} diff --git a/SocketEngine/DefaultBootstrap.cs b/SocketEngine/DefaultBootstrap.cs index 5bfcb8115..9aef8ca4a 100644 --- a/SocketEngine/DefaultBootstrap.cs +++ b/SocketEngine/DefaultBootstrap.cs @@ -7,6 +7,7 @@ using System.Runtime.Remoting; using System.Runtime.Remoting.Channels; using System.Runtime.Remoting.Channels.Ipc; +using System.Runtime.Serialization.Formatters; using System.Text; using System.Threading; using SuperSocket.Common; @@ -22,10 +23,12 @@ namespace SuperSocket.SocketEngine /// /// SuperSocket default bootstrap /// - public partial class DefaultBootstrap : IBootstrap + public partial class DefaultBootstrap : IBootstrap, ILoggerProvider, IDisposable { private List m_AppServers; + private IWorkItem m_ServerManager; + /// /// Indicates whether the bootstrap is initialized /// @@ -41,6 +44,14 @@ public partial class DefaultBootstrap : IBootstrap /// private ILog m_GlobalLog; + /// + /// Gets the bootstrap logger. + /// + ILog ILoggerProvider.Logger + { + get { return m_GlobalLog; } + } + /// /// Gets the log factory. /// @@ -75,6 +86,11 @@ public IRootConfig Config /// public string StartupConfigFile { get; private set; } + /// + /// Gets the class. + /// + public IPerformanceMonitor PerfMonitor { get { return m_PerfMonitor; } } + private PerformanceMonitor m_PerfMonitor; private readonly string m_BaseDirectory = AppDomain.CurrentDomain.BaseDirectory; @@ -299,6 +315,56 @@ private IServerConfig ReplaceListenEndPoint(IServerConfig serverConfig, IDiction return config; } + private IWorkItem InitializeAndSetupWorkItem(WorkItemFactoryInfo factoryInfo) + { + IWorkItem appServer; + + try + { + appServer = CreateWorkItemInstance(factoryInfo.ServerType, factoryInfo.StatusInfoMetadata); + + if (m_GlobalLog.IsDebugEnabled) + m_GlobalLog.DebugFormat("The server instance {0} has been created!", factoryInfo.Config.Name); + } + catch (Exception e) + { + if (m_GlobalLog.IsErrorEnabled) + m_GlobalLog.Error(string.Format("Failed to create server instance {0}!", factoryInfo.Config.Name), e); + return null; + } + + var exceptionSource = appServer as IExceptionSource; + + if (exceptionSource != null) + exceptionSource.ExceptionThrown += new EventHandler(exceptionSource_ExceptionThrown); + + + var setupResult = false; + + try + { + setupResult = SetupWorkItemInstance(appServer, factoryInfo); + + if (m_GlobalLog.IsDebugEnabled) + m_GlobalLog.DebugFormat("The server instance {0} has been initialized!", appServer.Name); + } + catch (Exception e) + { + m_GlobalLog.Error(e); + setupResult = false; + } + + if (!setupResult) + { + if (m_GlobalLog.IsErrorEnabled) + m_GlobalLog.Error("Failed to setup server instance!"); + + return null; + } + + return appServer; + } + /// /// Initializes the bootstrap with the configuration, config resolver and log factory. @@ -349,62 +415,26 @@ public virtual bool Initialize(Func serverConfigRe //Initialize servers foreach (var factoryInfo in workItemFactories) { - IWorkItem appServer; - - try - { - appServer = CreateWorkItemInstance(factoryInfo.ServerType, factoryInfo.StatusInfoMetadata); - - if (factoryInfo.IsServerManager) - serverManager = appServer; - else if (!(appServer is IsolationAppServer))//No isolation - { - //In isolation mode, cannot check whether is server manager in the factory info loader - if (TypeValidator.IsServerManagerType(appServer.GetType())) - serverManager = appServer; - } + IWorkItem appServer = InitializeAndSetupWorkItem(factoryInfo); - if (m_GlobalLog.IsDebugEnabled) - m_GlobalLog.DebugFormat("The server instance {0} has been created!", factoryInfo.Config.Name); - } - catch (Exception e) - { - if (m_GlobalLog.IsErrorEnabled) - m_GlobalLog.Error(string.Format("Failed to create server instance {0}!", factoryInfo.Config.Name), e); + if (appServer == null) return false; - } - - var exceptionSource = appServer as IExceptionSource; - - if(exceptionSource != null) - exceptionSource.ExceptionThrown += new EventHandler(exceptionSource_ExceptionThrown); - - - var setupResult = false; - - try - { - setupResult = SetupWorkItemInstance(appServer, factoryInfo); - - if (m_GlobalLog.IsDebugEnabled) - m_GlobalLog.DebugFormat("The server instance {0} has been initialized!", appServer.Name); - } - catch (Exception e) - { - m_GlobalLog.Error(e); - setupResult = false; - } - if (!setupResult) + if (factoryInfo.IsServerManager) + serverManager = appServer; + else if (!(appServer is IsolationAppServer))//No isolation { - if (m_GlobalLog.IsErrorEnabled) - m_GlobalLog.Error("Failed to setup server instance!"); - return false; + //In isolation mode, cannot check whether is server manager in the factory info loader + if (TypeValidator.IsServerManagerType(appServer.GetType())) + serverManager = appServer; } m_AppServers.Add(appServer); } + if (serverManager != null) + m_ServerManager = serverManager; + if (!m_Config.DisablePerformanceDataCollector) { m_PerfMonitor = new PerformanceMonitor(m_Config, m_AppServers, serverManager, logFactory); @@ -535,7 +565,14 @@ public StartResult Start() /// public void Stop() { - foreach (var server in m_AppServers) + var servers = m_AppServers.ToArray(); + + if (servers.Any(s => s.Config != null && s.Config.StartupOrder != 0)) + { + Array.Reverse(servers); + } + + foreach (var server in servers) { if (server.State == ServerState.Running) { @@ -572,7 +609,7 @@ protected virtual void RegisterRemotingService() if (serverChannel != null) ChannelServices.UnregisterChannel(serverChannel); - serverChannel = new IpcServerChannel(serverChannelName, bootstrapIpcPort); + serverChannel = new IpcServerChannel(serverChannelName, bootstrapIpcPort, new BinaryServerFormatterSinkProvider { TypeFilterLevel = TypeFilterLevel.Full }); ChannelServices.RegisterChannel(serverChannel, false); AppDomain.CurrentDomain.SetData("BootstrapIpcPort", bootstrapIpcPort); @@ -582,5 +619,42 @@ protected virtual void RegisterRemotingService() if (!RemotingConfiguration.GetRegisteredWellKnownServiceTypes().Any(s => s.ObjectType == bootstrapProxyType)) RemotingConfiguration.RegisterWellKnownServiceType(bootstrapProxyType, "Bootstrap.rem", WellKnownObjectMode.Singleton); } + + /// + /// Releases unmanaged and - optionally - managed resources. + /// + /// true to release both managed and unmanaged resources; false to release only unmanaged resources. + protected virtual void Dispose(bool disposing) + { + if (disposing) + { + AppDomain.CurrentDomain.UnhandledException -= new UnhandledExceptionEventHandler(CurrentDomain_UnhandledException); + } + } + + /// + /// Releases unmanaged and - optionally - managed resources. + /// + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + private void ResetPerfMoniter() + { + if (m_PerfMonitor != null) + { + m_PerfMonitor.Stop(); + m_PerfMonitor.Dispose(); + m_PerfMonitor = null; + } + + m_PerfMonitor = new PerformanceMonitor(m_Config, m_AppServers, m_ServerManager, LogFactory); + m_PerfMonitor.Start(); + + if (m_GlobalLog.IsDebugEnabled) + m_GlobalLog.Debug("The PerformanceMonitor has been reset for new server has been added!"); + } } } diff --git a/SocketEngine/IPerformanceMonitor.cs b/SocketEngine/IPerformanceMonitor.cs new file mode 100644 index 000000000..9a97a32a4 --- /dev/null +++ b/SocketEngine/IPerformanceMonitor.cs @@ -0,0 +1,27 @@ +using SuperSocket.SocketBase; +using System; +namespace SuperSocket.SocketEngine +{ + /// + /// Interface of IPerformanceMonitor + /// + public interface IPerformanceMonitor : IDisposable + { + /// + /// Start PerformanceMonitor. + /// + void Start(); + /// + /// Stop PerformanceMonitor. + /// + void Stop(); + /// + /// Invokes when status update. + /// + event Action OnStatusUpdate; + /// + /// Get or Set status update time in seconds. + /// + int StatusUpdateInterval { get; set; } + } +} diff --git a/SocketEngine/IRemoteWorkItem.cs b/SocketEngine/IRemoteWorkItem.cs index 6ee8f5472..0b4861a64 100644 --- a/SocketEngine/IRemoteWorkItem.cs +++ b/SocketEngine/IRemoteWorkItem.cs @@ -22,7 +22,8 @@ public interface IRemoteWorkItem : IWorkItemBase, IStatusInfoSource /// The assembly import root. /// The config. /// The factories. + /// The startup configuration file path /// - bool Setup(string serverType, string bootstrapUri, string assemblyImportRoot, IServerConfig config, ProviderFactoryInfo[] factories); + bool Setup(string serverType, string bootstrapUri, string assemblyImportRoot, IServerConfig config, ProviderFactoryInfo[] factories, string startupConfigFile); } } diff --git a/SocketEngine/IsolationAppServer.cs b/SocketEngine/IsolationAppServer.cs index a9d260bad..905f567e3 100644 --- a/SocketEngine/IsolationAppServer.cs +++ b/SocketEngine/IsolationAppServer.cs @@ -20,7 +20,7 @@ abstract class IsolationAppServer : MarshalByRefObject, IWorkItem, IStatusInfoSo protected IBootstrap Bootstrap { get; private set; } - protected IServerConfig ServerConfig { get; private set; } + public IServerConfig Config { get; private set; } protected ProviderFactoryInfo[] Factories { get; private set; } @@ -111,13 +111,28 @@ public virtual bool Setup(IBootstrap bootstrap, IServerConfig config, ProviderFa State = ServerState.Initializing; Name = config.Name; Bootstrap = bootstrap; - ServerConfig = config; + Config = config; Factories = factories; State = ServerState.NotStarted; return true; } + public virtual void ReportPotentialConfigChange(IServerConfig config) + { + Config = config; + + if (State != ServerState.Stopping && State != ServerState.NotStarted) + return; + + var appServer = AppServer; + + if (appServer == null) + return; + + appServer.ReportPotentialConfigChange(config); + } + protected abstract IWorkItemBase Start(); bool IWorkItemBase.Start() @@ -191,7 +206,7 @@ private StatusInfoCollection GetStoppedStatus() m_StoppedStatus.Name = Name; m_StoppedStatus.Tag = Name; m_StoppedStatus[StatusInfoKeys.IsRunning] = false; - m_StoppedStatus[StatusInfoKeys.MaxConnectionNumber] = ServerConfig.MaxConnectionNumber; + m_StoppedStatus[StatusInfoKeys.MaxConnectionNumber] = Config.MaxConnectionNumber; if (m_PrevStatus != null) { diff --git a/SocketEngine/MarshalAppServer.cs b/SocketEngine/MarshalAppServer.cs index 1b0180be3..444ba55b8 100644 --- a/SocketEngine/MarshalAppServer.cs +++ b/SocketEngine/MarshalAppServer.cs @@ -44,6 +44,33 @@ public bool Setup(IBootstrap bootstrap, IServerConfig config, ProviderFactoryInf return m_AppServer.Setup(bootstrap, config, factories); } + /// + /// Gets the server's config. + /// + /// + /// The server's config. + /// + public IServerConfig Config + { + get + { + if(m_AppServer == null) + return null; + + return m_AppServer.Config; + } + } + + /// + /// Reports the potential configuration change. + /// + /// The server config which may be changed. + /// + public void ReportPotentialConfigChange(IServerConfig config) + { + m_AppServer.ReportPotentialConfigChange(config); + } + /// /// Starts this server instance. /// diff --git a/SocketEngine/PerformanceMonitor.cs b/SocketEngine/PerformanceMonitor.cs index d558f81b2..4326311fa 100644 --- a/SocketEngine/PerformanceMonitor.cs +++ b/SocketEngine/PerformanceMonitor.cs @@ -13,8 +13,10 @@ namespace SuperSocket.SocketEngine { - class PerformanceMonitor : IDisposable + class PerformanceMonitor : IPerformanceMonitor { + public event Action OnStatusUpdate; + private Timer m_PerformanceTimer; private int m_TimerInterval; private ILog m_PerfLog; @@ -43,31 +45,34 @@ public PerformanceMonitor(IRootConfig config, IEnumerable appServers, private void SetupServerStatusMetadata() { - m_ServerStatusMetadataSource = new List>(m_AppServers.Length + 1); - - m_ServerStatusMetadataSource.Add( - new KeyValuePair(string.Empty, - new StatusInfoAttribute[] - { - new StatusInfoAttribute(StatusInfoKeys.CpuUsage) { Name = "CPU Usage", Format = "{0:0.00}%", Order = 0 }, - new StatusInfoAttribute(StatusInfoKeys.MemoryUsage) { Name = "Physical Memory Usage", Format = "{0:N}", Order = 1 }, - new StatusInfoAttribute(StatusInfoKeys.TotalThreadCount) { Name = "Total Thread Count", Order = 2 }, - new StatusInfoAttribute(StatusInfoKeys.AvailableWorkingThreads) { Name = "Available Working Threads", Order = 3 }, - new StatusInfoAttribute(StatusInfoKeys.AvailableCompletionPortThreads) { Name = "Available Completion Port Threads", Order = 4 }, - new StatusInfoAttribute(StatusInfoKeys.MaxWorkingThreads) { Name = "Maximum Working Threads", Order = 5 }, - new StatusInfoAttribute(StatusInfoKeys.MaxCompletionPortThreads) { Name = "Maximum Completion Port Threads", Order = 6 } - })); - - for (var i = 0; i < m_AppServers.Length; i++) + if (m_ServerStatusMetadataSource == null) { - var server = m_AppServers[i]; + m_ServerStatusMetadataSource = new List>(m_AppServers.Length + 1); + m_ServerStatusMetadataSource.Add( - new KeyValuePair(server.Name, server.GetServerStatusMetadata().OrderBy(s => s.Order).ToArray())); - } + new KeyValuePair(string.Empty, + new StatusInfoAttribute[] + { + new StatusInfoAttribute(StatusInfoKeys.CpuUsage) { Name = "CPU Usage", Format = "{0:0.00}%", Order = 0 }, + new StatusInfoAttribute(StatusInfoKeys.MemoryUsage) { Name = "Physical Memory Usage", Format = "{0:N}", Order = 1 }, + new StatusInfoAttribute(StatusInfoKeys.TotalThreadCount) { Name = "Total Thread Count", Order = 2 }, + new StatusInfoAttribute(StatusInfoKeys.AvailableWorkingThreads) { Name = "Available Working Threads", Order = 3 }, + new StatusInfoAttribute(StatusInfoKeys.AvailableCompletionPortThreads) { Name = "Available Completion Port Threads", Order = 4 }, + new StatusInfoAttribute(StatusInfoKeys.MaxWorkingThreads) { Name = "Maximum Working Threads", Order = 5 }, + new StatusInfoAttribute(StatusInfoKeys.MaxCompletionPortThreads) { Name = "Maximum Completion Port Threads", Order = 6 } + })); + + for (var i = 0; i < m_AppServers.Length; i++) + { + var server = m_AppServers[i]; + m_ServerStatusMetadataSource.Add( + new KeyValuePair(server.Name, server.GetServerStatusMetadata().OrderBy(s => s.Order).ToArray())); + } - if (m_ServerManager != null && m_ServerManager.State == ServerState.Running) - { - m_ServerManager.TransferSystemMessage("ServerMetadataCollected", m_ServerStatusMetadataSource); + if (m_ServerManager != null && m_ServerManager.State == ServerState.Running) + { + m_ServerManager.TransferSystemMessage("ServerMetadataCollected", m_ServerStatusMetadataSource); + } } } @@ -113,7 +118,20 @@ private void OnPerformanceTimerCallback(object state) } else { - var serverStatus = s.CollectServerStatus(bootstrapStatus); + StatusInfoCollection serverStatus; + + try + { + serverStatus = s.CollectServerStatus(bootstrapStatus); + } + catch(Exception e) + { + m_PerfLog.Error("Failed to CollectServerStatus of " + s.Name, e); + + perfBuilder.AppendLine(string.Format("{0} ----------------------------------", s.Name)); + perfBuilder.AppendLine(string.Format("{0}: {1}", "State", s.State)); + continue; + } instancesStatus.Add(serverStatus); @@ -142,6 +160,8 @@ private void OnPerformanceTimerCallback(object state) nodeStatus.InstancesStatus = instancesStatus.ToArray(); + if (OnStatusUpdate != null) OnStatusUpdate(nodeStatus); + if (m_ServerManager != null && m_ServerManager.State == ServerState.Running) { m_ServerManager.TransferSystemMessage("ServerStatusCollected", nodeStatus); @@ -156,7 +176,29 @@ public void Dispose() m_PerformanceTimer = null; } - m_Helper = null; + if (m_Helper != null) + { + m_Helper.Dispose(); + m_Helper = null; + } + } + + public int StatusUpdateInterval + { + get { return m_TimerInterval / 1000; } + + set + { + var newTimerInterval = value * 1000; + + if (m_TimerInterval == newTimerInterval) + return; + + m_TimerInterval = newTimerInterval; + + Stop(); + Start(); + } } } } diff --git a/SocketEngine/ProcessAppServer.cs b/SocketEngine/ProcessAppServer.cs index 64596742e..08ddf23f7 100644 --- a/SocketEngine/ProcessAppServer.cs +++ b/SocketEngine/ProcessAppServer.cs @@ -46,6 +46,8 @@ public string ServerTag private ProcessPerformanceCounterHelper m_PerformanceCounterHelper; + private bool m_AutoStartAfterUnexpectedShutdown = true; + /// /// Initializes a new instance of the class. /// @@ -74,6 +76,17 @@ public int ProcessId } } + public override bool Setup(IBootstrap bootstrap, IServerConfig config, ProviderFactoryInfo[] factories) + { + if (!base.Setup(bootstrap, config, factories)) + return false; + + if ("false".Equals(config.Options.GetValue("autoStartAfterUnexpectedShutdown"), StringComparison.OrdinalIgnoreCase)) + m_AutoStartAfterUnexpectedShutdown = false; + + return true; + } + protected override IWorkItemBase Start() { var currentDomain = AppDomain.CurrentDomain; @@ -120,17 +133,19 @@ protected override IWorkItemBase Start() OnExceptionThrown(e); return null; } + + m_WorkingProcess.EnableRaisingEvents = true; + m_WorkingProcess.ErrorDataReceived += new DataReceivedEventHandler(m_WorkingProcess_ErrorDataReceived); + m_WorkingProcess.OutputDataReceived += new DataReceivedEventHandler(m_WorkingProcess_OutputDataReceived); + m_WorkingProcess.BeginErrorReadLine(); + m_WorkingProcess.BeginOutputReadLine(); } else { m_WorkingProcess = process; + m_WorkingProcess.EnableRaisingEvents = true; } - m_WorkingProcess.EnableRaisingEvents = true; - m_WorkingProcess.ErrorDataReceived += new DataReceivedEventHandler(m_WorkingProcess_ErrorDataReceived); - m_WorkingProcess.OutputDataReceived += new DataReceivedEventHandler(m_WorkingProcess_OutputDataReceived); - m_WorkingProcess.BeginErrorReadLine(); - m_WorkingProcess.BeginOutputReadLine(); portName = string.Format(portName, m_WorkingProcess.Id); m_ServerTag = portName; @@ -141,7 +156,16 @@ protected override IWorkItemBase Start() if (process == null) { - if (!m_ProcessWorkEvent.WaitOne(10000)) + var startTimeOut = 0; + + int.TryParse(Config.Options.GetValue("startTimeOut", "0"), out startTimeOut); + + if (startTimeOut <= 0) + { + startTimeOut = 10; + } + + if (!m_ProcessWorkEvent.WaitOne(startTimeOut * 1000)) { ShutdownProcess(); OnExceptionThrown(new Exception("The remote work item was timeout to setup!")); @@ -169,8 +193,16 @@ protected override IWorkItemBase Start() try { + var startupConfigFile = Bootstrap.StartupConfigFile; + + if (!string.IsNullOrEmpty(startupConfigFile)) + { + if (!Path.IsPathRooted(startupConfigFile)) + startupConfigFile = Path.Combine(currentDomain.BaseDirectory, startupConfigFile); + } + //Setup and then start the remote server instance - ret = appServer.Setup(ServerTypeName, "ipc://" + bootstrapIpcPort + "/Bootstrap.rem", currentDomain.BaseDirectory, ServerConfig, Factories); + ret = appServer.Setup(ServerTypeName, "ipc://" + bootstrapIpcPort + "/Bootstrap.rem", currentDomain.BaseDirectory, Config, Factories, startupConfigFile); } catch (Exception e) { @@ -269,7 +301,7 @@ protected override void OnStopped() m_WorkingProcess = null; m_ProcessWorkStatus = string.Empty; - if (unexpectedShutdown) + if (unexpectedShutdown && m_AutoStartAfterUnexpectedShutdown) { //auto restart if meet a unexpected shutdown ((IWorkItemBase)this).Start(); diff --git a/SocketEngine/ProcessPerformanceCounterHelper.cs b/SocketEngine/ProcessPerformanceCounterHelper.cs index ca1aaa7f4..4dd3cfb33 100644 --- a/SocketEngine/ProcessPerformanceCounterHelper.cs +++ b/SocketEngine/ProcessPerformanceCounterHelper.cs @@ -50,7 +50,17 @@ void SameNameProcess_Exited(object sender, EventArgs e) private void SetupPerformanceCounters() { var isUnix = Environment.OSVersion.Platform == PlatformID.Unix || Environment.OSVersion.Platform == PlatformID.MacOSX; - var instanceName = (isUnix || Platform.IsMono) ? string.Format("{0}/{1}", m_Process.Id, m_Process.ProcessName) : GetPerformanceCounterInstanceName(m_Process); + + var instanceName = string.Empty; + + if (isUnix || Platform.IsMono) + instanceName = string.Format("{0}/{1}", m_Process.Id, m_Process.ProcessName); + else + instanceName = GetPerformanceCounterInstanceName(m_Process); + + // the process has exited + if (string.IsNullOrEmpty(instanceName)) + return; SetupPerformanceCounters(instanceName); } @@ -62,7 +72,7 @@ private void SetupPerformanceCounters(string instanceName) m_WorkingSetPC = new PerformanceCounter("Process", "Working Set", instanceName); } - //Tt is only used in windows + //This method is only used in windows private static string GetPerformanceCounterInstanceName(Process process) { var processId = process.Id; @@ -74,9 +84,23 @@ private static string GetPerformanceCounterInstanceName(Process process) if (!runnedInstance.StartsWith(process.ProcessName, StringComparison.OrdinalIgnoreCase)) continue; + if (process.HasExited) + return string.Empty; + using (var performanceCounter = new PerformanceCounter("Process", "ID Process", runnedInstance, true)) { - if ((int)performanceCounter.RawValue == processId) + var counterProcessId = 0; + + try + { + counterProcessId = (int)performanceCounter.RawValue; + } + catch //that process has been shutdown + { + continue; + } + + if (counterProcessId == processId) { return runnedInstance; } @@ -124,6 +148,10 @@ public void Collect(StatusInfoCollection statusCollection) //If a same name process exited, this process's performance counters instance name could be changed, //so if the old performance counter cannot be access, get the performance counter's name again var newInstanceName = GetPerformanceCounterInstanceName(m_Process); + + if (string.IsNullOrEmpty(newInstanceName)) + break; + SetupPerformanceCounters(newInstanceName); retry = true; } diff --git a/SocketEngine/RemoteBootstrapProxy.cs b/SocketEngine/RemoteBootstrapProxy.cs index f794533f8..6ab8d58b5 100644 --- a/SocketEngine/RemoteBootstrapProxy.cs +++ b/SocketEngine/RemoteBootstrapProxy.cs @@ -34,6 +34,11 @@ public string Name get { return m_Server.Name; } } + public void ReportPotentialConfigChange(IServerConfig config) + { + m_Server.ReportPotentialConfigChange(config); + } + public bool Start() { return m_Server.Start(); @@ -63,6 +68,14 @@ public void TransferSystemMessage(string messageType, object messageData) { throw new NotSupportedException(); } + + public IServerConfig Config + { + get + { + throw new NotSupportedException(); + } + } } private IBootstrap m_Bootstrap; diff --git a/SocketEngine/SocketServerBase.cs b/SocketEngine/SocketServerBase.cs index 460db1df7..931e0dc51 100644 --- a/SocketEngine/SocketServerBase.cs +++ b/SocketEngine/SocketServerBase.cs @@ -143,6 +143,8 @@ public virtual void Stop() Listeners.Clear(); + SendingQueuePool = null; + IsRunning = false; } diff --git a/SocketEngine/SocketSession.cs b/SocketEngine/SocketSession.cs index 4012454ba..9fd4818c0 100644 --- a/SocketEngine/SocketSession.cs +++ b/SocketEngine/SocketSession.cs @@ -46,14 +46,29 @@ abstract partial class SocketSession : ISocketSession private int m_State = 0; private void AddStateFlag(int stateValue) + { + AddStateFlag(stateValue, false); + } + + private bool AddStateFlag(int stateValue, bool notClosing) { while(true) { var oldState = m_State; + + if (notClosing) + { + // don't update the state if the connection has entered the closing procedure + if (oldState >= SocketState.InClosing) + { + return false; + } + } + var newState = m_State | stateValue; if(Interlocked.CompareExchange(ref m_State, newState, oldState) == oldState) - return; + return true; } } @@ -119,7 +134,9 @@ public virtual void Initialize(IAppSession appSession) AppSession = appSession; Config = appSession.Config; SyncSend = Config.SyncSend; - m_SendingQueuePool = ((SocketServerBase)((ISocketServerAccessor)appSession.AppServer).SocketServer).SendingQueuePool; + + if (m_SendingQueuePool == null) + m_SendingQueuePool = ((SocketServerBase)((ISocketServerAccessor)appSession.AppServer).SocketServer).SendingQueuePool; SendingQueue queue; if (m_SendingQueuePool.TryGet(out queue)) @@ -204,7 +221,14 @@ protected virtual void OnClosed(CloseReason reason) /// public bool TrySend(IList> segments) { + if (IsClosed) + return false; + var queue = m_SendingQueue; + + if (queue == null) + return false; + var trackID = queue.TrackID; if (!queue.Enqueue(segments, trackID)) @@ -281,9 +305,11 @@ private void StartSend(SendingQueue queue, int sendingTrackID, bool initial) } } - if (IsInClosingOrClosed && m_Client == null) + Socket client; + + if (IsInClosingOrClosed && TryValidateClosedBySocket(out client)) { - OnSendEnd(true); + OnSendEnd(); return; } @@ -291,9 +317,8 @@ private void StartSend(SendingQueue queue, int sendingTrackID, bool initial) if (!m_SendingQueuePool.TryGet(out newQueue)) { + OnSendEnd(CloseReason.InternalError, true); AppSession.Logger.Error("There is no enougth sending queue can be used."); - OnSendEnd(false); - this.Close(CloseReason.InternalError); return; } @@ -306,13 +331,12 @@ private void StartSend(SendingQueue queue, int sendingTrackID, bool initial) if (IsInClosingOrClosed) { - OnSendEnd(true); + OnSendEnd(); } else { - OnSendEnd(false); + OnSendEnd(CloseReason.InternalError, true); AppSession.Logger.Error("Failed to switch the sending queue."); - this.Close(CloseReason.InternalError); } return; @@ -324,10 +348,10 @@ private void StartSend(SendingQueue queue, int sendingTrackID, bool initial) if (queue.Count == 0) { - AppSession.Logger.Error("There is no data to be sent in the queue."); + m_SendingQueuePool.Push(queue); - OnSendEnd(false); - this.Close(CloseReason.InternalError); + OnSendEnd(CloseReason.InternalError, true); + AppSession.Logger.Error("There is no data to be sent in the queue."); return; } @@ -336,35 +360,13 @@ private void StartSend(SendingQueue queue, int sendingTrackID, bool initial) private void OnSendEnd() { - OnSendEnd(IsInClosingOrClosed); + OnSendEnd(CloseReason.Unknown, false); } - private void OnSendEnd(bool isInClosingOrClosed) + private void OnSendEnd(CloseReason closeReason, bool forceClose) { RemoveStateFlag(SocketState.InSending); - - if (isInClosingOrClosed) - { - var client = m_Client; - //The socket has not been closed, close it now - if (client != null) - { - //No data to be sent - if (m_SendingQueue.Count == 0) - { - //Not can close it now - InternalClose(client, GetCloseReasonFromState(), false); - return; - } - - return; - } - - if (ValidateNotInSendingReceiving()) - { - FireCloseEvent(); - } - } + ValidateClosed(closeReason, forceClose, true); } protected virtual void OnSendingCompleted(SendingQueue queue) @@ -376,17 +378,19 @@ protected virtual void OnSendingCompleted(SendingQueue queue) if (IsInClosingOrClosed) { + Socket client; + //has data is being sent and the socket isn't closed - if (newQueue.Count > 0 && m_Client != null) + if (newQueue.Count > 0 && !TryValidateClosedBySocket(out client)) { StartSend(newQueue, newQueue.TrackID, false); return; } - OnSendEnd(true); + OnSendEnd(); return; } - + if (newQueue.Count == 0) { OnSendEnd(); @@ -447,16 +451,23 @@ protected bool IsClosed /// The secure protocol. public SslProtocols SecureProtocol { get; set; } + protected virtual bool TryValidateClosedBySocket(out Socket socket) + { + socket = m_Client; + //Already closed/closing + return socket == null; + } + public virtual void Close(CloseReason reason) { //Already in closing procedure if (!TryAddStateFlag(SocketState.InClosing)) return; - var client = m_Client; + Socket client; - //Already closed/closing - if (client == null) + //No need to clean the socket instance + if (TryValidateClosedBySocket(out client)) return; //Some data is in sending @@ -467,7 +478,11 @@ public virtual void Close(CloseReason reason) return; } - InternalClose(client, reason, true); + // In the udp mode, we needn't close the socket instance + if (client != null) + InternalClose(client, reason, true); + else //In Udp mode, and the socket is not in the sending state, then fire the closed event directly + OnClosed(reason); } private void InternalClose(Socket client, CloseReason reason, bool setCloseReason) @@ -490,19 +505,26 @@ protected void OnSendError(SendingQueue queue, CloseReason closeReason) { queue.Clear(); m_SendingQueuePool.Push(queue); - OnSendEnd(); - ValidateClosed(closeReason); + OnSendEnd(closeReason, true); } - protected void OnReceiveError(CloseReason closeReason) + // the receive action won't be started for this connection any more + protected void OnReceiveTerminated(CloseReason closeReason) { OnReceiveEnded(); - ValidateClosed(closeReason); + ValidateClosed(closeReason, true); } - protected void OnReceiveStarted() + + // return false if the connection has entered the closing procedure or has closed already + protected bool OnReceiveStarted() { - AddStateFlag(SocketState.InReceiving); + if (AddStateFlag(SocketState.InReceiving, true)) + return true; + + // the connection is in closing + ValidateClosed(CloseReason.Unknown, false); + return false; } protected void OnReceiveEnded() @@ -543,21 +565,59 @@ private void FireCloseEvent() OnClosed(GetCloseReasonFromState()); } - private void ValidateClosed(CloseReason closeReason) + private void ValidateClosed() { - if (IsClosed) - return; + // CloseReason.Unknown won't be used + ValidateClosed(CloseReason.Unknown, false); + } - if (CheckState(SocketState.InClosing)) + private void ValidateClosed(CloseReason closeReason, bool forceClose) + { + ValidateClosed(closeReason, forceClose, false); + } + + private void ValidateClosed(CloseReason closeReason, bool forceClose, bool forSend) + { + lock (this) { - if (ValidateNotInSendingReceiving()) + if (IsClosed) + return; + + if (CheckState(SocketState.InClosing)) { - FireCloseEvent(); + // we only keep socket instance after InClosing state when the it is sending + // so we check if the socket instance is alive now + if (forSend) + { + Socket client; + + if (!TryValidateClosedBySocket(out client)) + { + var sendingQueue = m_SendingQueue; + // No data to be sent + if (forceClose || (sendingQueue != null && sendingQueue.Count == 0)) + { + if (client != null)// the socket instance is not closed yet, do it now + InternalClose(client, GetCloseReasonFromState(), false); + else// The UDP mode, the socket instance always is null, fire the closed event directly + FireCloseEvent(); + + return; + } + + return; + } + } + + if (ValidateNotInSendingReceiving()) + { + FireCloseEvent(); + } + } + else if (forceClose) + { + Close(closeReason); } - } - else - { - Close(closeReason); } } diff --git a/SocketEngine/SuperSocket.SocketEngine.Net35.csproj b/SocketEngine/SuperSocket.SocketEngine.Net35.csproj index 276e4d17e..7ec81d54f 100644 --- a/SocketEngine/SuperSocket.SocketEngine.Net35.csproj +++ b/SocketEngine/SuperSocket.SocketEngine.Net35.csproj @@ -93,6 +93,7 @@ + @@ -103,6 +104,7 @@ + diff --git a/SocketEngine/SuperSocket.SocketEngine.Net40.csproj b/SocketEngine/SuperSocket.SocketEngine.Net40.csproj index f5ab17458..0f0cc336e 100644 --- a/SocketEngine/SuperSocket.SocketEngine.Net40.csproj +++ b/SocketEngine/SuperSocket.SocketEngine.Net40.csproj @@ -79,11 +79,15 @@ + + + + diff --git a/SocketEngine/SuperSocket.SocketEngine.Net45.csproj b/SocketEngine/SuperSocket.SocketEngine.Net45.csproj index 86c3866cf..a30c6edf0 100644 --- a/SocketEngine/SuperSocket.SocketEngine.Net45.csproj +++ b/SocketEngine/SuperSocket.SocketEngine.Net45.csproj @@ -81,13 +81,17 @@ + + + + diff --git a/SocketEngine/TcpAsyncSocketListener.cs b/SocketEngine/TcpAsyncSocketListener.cs index 5bf6cdc06..033934f66 100644 --- a/SocketEngine/TcpAsyncSocketListener.cs +++ b/SocketEngine/TcpAsyncSocketListener.cs @@ -19,6 +19,8 @@ class TcpAsyncSocketListener : SocketListenerBase private Socket m_ListenSocket; + private SocketAsyncEventArgs m_AcceptSAE; + public TcpAsyncSocketListener(ListenerInfo info) : base(info) { @@ -43,6 +45,7 @@ public override bool Start(IServerConfig config) m_ListenSocket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.DontLinger, true); SocketAsyncEventArgs acceptEventArg = new SocketAsyncEventArgs(); + m_AcceptSAE = acceptEventArg; acceptEventArg.Completed += new EventHandler(acceptEventArg_Completed); if (!m_ListenSocket.AcceptAsync(acceptEventArg)) @@ -129,6 +132,10 @@ public override void Stop() if (m_ListenSocket == null) return; + m_AcceptSAE.Completed -= new EventHandler(acceptEventArg_Completed); + m_AcceptSAE.Dispose(); + m_AcceptSAE = null; + try { m_ListenSocket.Close(); diff --git a/SocketEngine/UdpSocketListener.cs b/SocketEngine/UdpSocketListener.cs index c8b418a94..44850f946 100644 --- a/SocketEngine/UdpSocketListener.cs +++ b/SocketEngine/UdpSocketListener.cs @@ -14,6 +14,8 @@ class UdpSocketListener : SocketListenerBase { private Socket m_ListenSocket; + private SocketAsyncEventArgs m_ReceiveSAE; + public UdpSocketListener(ListenerInfo info) : base(info) { @@ -46,6 +48,7 @@ public override bool Start(IServerConfig config) } var eventArgs = new SocketAsyncEventArgs(); + m_ReceiveSAE = eventArgs; eventArgs.Completed += new EventHandler(eventArgs_Completed); eventArgs.RemoteEndPoint = new IPEndPoint(IPAddress.Any, 0); @@ -110,6 +113,10 @@ public override void Stop() if (m_ListenSocket == null) return; + m_ReceiveSAE.Completed -= new EventHandler(eventArgs_Completed); + m_ReceiveSAE.Dispose(); + m_ReceiveSAE = null; + if(!Platform.IsMono) { try diff --git a/SocketEngine/UdpSocketServer.cs b/SocketEngine/UdpSocketServer.cs index c5ab7d039..e608c1106 100644 --- a/SocketEngine/UdpSocketServer.cs +++ b/SocketEngine/UdpSocketServer.cs @@ -5,6 +5,7 @@ using System.Net.Sockets; using System.Text; using System.Threading; +using System.Threading.Tasks; using SuperSocket.Common; using SuperSocket.SocketBase; using SuperSocket.SocketBase.Command; @@ -13,7 +14,7 @@ namespace SuperSocket.SocketEngine { - class UdpSocketServer : SocketServerBase + class UdpSocketServer : SocketServerBase, IActiveConnector where TRequestInfo : IRequestInfo { private IPEndPoint m_EndPointIPv4; @@ -78,6 +79,35 @@ protected override void OnNewClientAccepted(ISocketListener listener, Socket cli } } + IAppSession CreateNewSession(Socket listenSocket, IPEndPoint remoteEndPoint, string sessionID) + { + if (!DetectConnectionNumber(remoteEndPoint)) + return null; + + var socketSession = new UdpSocketSession(listenSocket, remoteEndPoint, sessionID); + var appSession = AppServer.CreateAppSession(socketSession); + + if (appSession == null) + return null; + + if (!DetectConnectionNumber(remoteEndPoint)) + return null; + + if (!AppServer.RegisterSession(appSession)) + { + socketSession.Close(CloseReason.InternalError); + return null; + } + + Interlocked.Increment(ref m_ConnectionCount); + + socketSession.Closed += OnSocketSessionClosed; + socketSession.Start(); + + return appSession; + } + + void ProcessPackageWithSessionID(Socket listenSocket, IPEndPoint remoteEndPoint, byte[] receivedData) { TRequestInfo requestInfo; @@ -126,25 +156,11 @@ void ProcessPackageWithSessionID(Socket listenSocket, IPEndPoint remoteEndPoint, if (appSession == null) { - if (!DetectConnectionNumber(remoteEndPoint)) - return; - - var socketSession = new UdpSocketSession(listenSocket, remoteEndPoint, sessionID); - appSession = AppServer.CreateAppSession(socketSession); + appSession = CreateNewSession(listenSocket, remoteEndPoint, sessionID); + //Failed to create a new session if (appSession == null) return; - - if (!DetectConnectionNumber(remoteEndPoint)) - return; - - if (!AppServer.RegisterSession(appSession)) - return; - - Interlocked.Increment(ref m_ConnectionCount); - - socketSession.Closed += OnSocketSessionClosed; - socketSession.Start(); } else { @@ -163,26 +179,12 @@ void ProcessPackageWithoutSessionID(Socket listenSocket, IPEndPoint remoteEndPoi if (appSession == null) //New session { - if (!DetectConnectionNumber(remoteEndPoint)) - return; - - var socketSession = new UdpSocketSession(listenSocket, remoteEndPoint, sessionID); - - appSession = AppServer.CreateAppSession(socketSession); + appSession = CreateNewSession(listenSocket, remoteEndPoint, sessionID); + //Failed to create a new session if (appSession == null) return; - if (!DetectConnectionNumber(remoteEndPoint)) - return; - - if (!AppServer.RegisterSession(appSession)) - return; - - Interlocked.Increment(ref m_ConnectionCount); - socketSession.Closed += OnSocketSessionClosed; - socketSession.Start(); - appSession.ProcessRequest(receivedData, 0, receivedData.Length, false); } else //Existing session @@ -219,5 +221,32 @@ public override void ResetSessionSecurity(IAppSession session, System.Security.A { throw new NotSupportedException(); } + + Task IActiveConnector.ActiveConnect(EndPoint targetEndPoint) + { + return ((IActiveConnector)this).ActiveConnect(targetEndPoint, null); + } + + Task IActiveConnector.ActiveConnect(EndPoint targetEndPoint, EndPoint localEndPoint) + { + var taskSource = new TaskCompletionSource(); + var socket = new Socket(targetEndPoint.AddressFamily, SocketType.Dgram, ProtocolType.Udp); + + if (localEndPoint != null) + { + socket.ExclusiveAddressUse = false; + socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true); + socket.Bind(localEndPoint); + } + + var session = CreateNewSession(socket, (IPEndPoint)targetEndPoint, targetEndPoint.ToString()); + + if (session == null) + taskSource.SetException(new Exception("Failed to create session for this socket.")); + else + taskSource.SetResult(new ActiveConnectResult { Result = true, Session = session }); + + return taskSource.Task; + } } } diff --git a/SocketEngine/UdpSocketSession.cs b/SocketEngine/UdpSocketSession.cs index 016317fcf..c8b8b293c 100644 --- a/SocketEngine/UdpSocketSession.cs +++ b/SocketEngine/UdpSocketSession.cs @@ -10,6 +10,7 @@ using SuperSocket.SocketBase; using SuperSocket.SocketBase.Command; using SuperSocket.SocketBase.Protocol; +using System.Threading; namespace SuperSocket.SocketEngine { @@ -53,17 +54,26 @@ public override void Start() protected override void SendAsync(SendingQueue queue) { var e = new SocketAsyncEventArgs(); - e.Completed += new EventHandler(SendingCompleted); + + e.Completed += new EventHandler(OnSendingCompleted); e.RemoteEndPoint = RemoteEndPoint; e.UserToken = queue; var item = queue[queue.Position]; e.SetBuffer(item.Array, item.Offset, item.Count); - m_ServerSocket.SendToAsync(e); + if (!m_ServerSocket.SendToAsync(e)) + OnSendingCompleted(this, e); } - void SendingCompleted(object sender, SocketAsyncEventArgs e) + void CleanSocketAsyncEventArgs(SocketAsyncEventArgs e) + { + e.UserToken = null; + e.Completed -= new EventHandler(OnSendingCompleted); + e.Dispose(); + } + + void OnSendingCompleted(object sender, SocketAsyncEventArgs e) { var queue = e.UserToken as SendingQueue; @@ -74,16 +84,17 @@ void SendingCompleted(object sender, SocketAsyncEventArgs e) if (log.IsErrorEnabled) log.Error(new SocketException((int)e.SocketError)); - e.UserToken = null; + CleanSocketAsyncEventArgs(e); OnSendError(queue, CloseReason.SocketError); return; } + CleanSocketAsyncEventArgs(e); + var newPos = queue.Position + 1; if (newPos >= queue.Count) { - e.UserToken = null; OnSendingCompleted(queue); return; } @@ -108,10 +119,10 @@ public override void ApplySecureProtocol() throw new NotSupportedException(); } - public override void Close(CloseReason reason) + protected override bool TryValidateClosedBySocket(out Socket socket) { - if (!IsClosed) - OnClosed(reason); + socket = null; + return false; } public override int OrigReceiveOffset diff --git a/SocketEngine/WorkItemFactoryInfoLoader.cs b/SocketEngine/WorkItemFactoryInfoLoader.cs index c65e802d3..0cabda153 100644 --- a/SocketEngine/WorkItemFactoryInfoLoader.cs +++ b/SocketEngine/WorkItemFactoryInfoLoader.cs @@ -159,10 +159,14 @@ public List LoadResult(Func s } else { - workItemFactory.LogFactory = m_DefaultLogFactory; + if (m_DefaultLogFactory != null) + workItemFactory.LogFactory = m_DefaultLogFactory; + else + workItemFactory.LogFactory = GetBootstrapLogFactory(); } - factories.Add(workItemFactory.LogFactory); + if (workItemFactory.LogFactory != null) + factories.Add(workItemFactory.LogFactory); //Initialize Receive filter factory if (!string.IsNullOrEmpty(serverConfig.ReceiveFilterFactory)) diff --git a/SocketService/SocketServiceInstaller.cs b/SocketService/SocketServiceInstaller.cs index 7ed6d48bc..92a2049bd 100644 --- a/SocketService/SocketServiceInstaller.cs +++ b/SocketService/SocketServiceInstaller.cs @@ -24,6 +24,10 @@ public SocketServiceInstaller() serviceInstaller.StartType = ServiceStartMode.Automatic; serviceInstaller.ServiceName = ConfigurationManager.AppSettings["ServiceName"]; + var serviceDisplayName = ConfigurationManager.AppSettings["ServiceDisplayName"]; + if (!string.IsNullOrEmpty(serviceDisplayName)) + serviceInstaller.DisplayName = serviceDisplayName; + var serviceDescription = ConfigurationManager.AppSettings["ServiceDescription"]; if (!string.IsNullOrEmpty(serviceDescription)) serviceInstaller.Description = serviceDescription; @@ -36,8 +40,18 @@ public SocketServiceInstaller() serviceInstaller.ServicesDependedOn = servicesDependedOn.ToArray(); + var serviceStartAfterInstall = ConfigurationManager.AppSettings["ServiceStartAfterInstall"]; + if (!string.IsNullOrEmpty(serviceStartAfterInstall) && serviceStartAfterInstall.ToLower() == "true") + this.AfterInstall += new InstallEventHandler(ProjectInstaller_AfterInstall); + Installers.Add(serviceInstaller); Installers.Add(processInstaller); } + + private void ProjectInstaller_AfterInstall(object sender, InstallEventArgs e) + { + ServiceController sc = new ServiceController(serviceInstaller.ServiceName); + sc.Start(); + } } -} \ No newline at end of file +} diff --git a/Solution Items/GlobalAssemblyInfo.cs b/Solution Items/GlobalAssemblyInfo.cs index 7b6c3a232..ba47a9ad3 100644 --- a/Solution Items/GlobalAssemblyInfo.cs +++ b/Solution Items/GlobalAssemblyInfo.cs @@ -8,10 +8,10 @@ // Build Number // Revision // -[assembly: AssemblyVersion("1.6.0.3")] -[assembly: AssemblyFileVersion("1.6.0.3")] +[assembly: AssemblyVersion("1.6.7.0")] +[assembly: AssemblyFileVersion("1.6.7.0")] [assembly: AssemblyCompany("SuperSocket")] [assembly: AssemblyProduct("SuperSocket")] -[assembly: AssemblyCopyright("Copyright © supersocket.codeplex.com 2010-2013")] +[assembly: AssemblyCopyright("Copyright © supersocket.codeplex.com 2010-2016")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] \ No newline at end of file diff --git a/Solution Items/log4net.config b/Solution Items/log4net.config index efa786b71..c1ebe9571 100644 --- a/Solution Items/log4net.config +++ b/Solution Items/log4net.config @@ -5,8 +5,9 @@ - - + + + @@ -19,8 +20,9 @@ - - + + + @@ -33,8 +35,9 @@ - - + + + @@ -47,8 +50,9 @@ - - + + + diff --git a/Solution Items/log4net.unix.config b/Solution Items/log4net.unix.config index d6f357029..3b07772ac 100644 --- a/Solution Items/log4net.unix.config +++ b/Solution Items/log4net.unix.config @@ -6,7 +6,8 @@ - + + @@ -19,8 +20,9 @@ - - + + + @@ -33,8 +35,9 @@ - - + + + @@ -47,8 +50,9 @@ - - + + + diff --git a/SuperSocket.2012.NET40.sln b/SuperSocket.2013.NET40.sln similarity index 98% rename from SuperSocket.2012.NET40.sln rename to SuperSocket.2013.NET40.sln index 9e6f43ddb..3f2998215 100644 --- a/SuperSocket.2012.NET40.sln +++ b/SuperSocket.2013.NET40.sln @@ -1,6 +1,8 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 2012 +# Visual Studio 2013 +VisualStudioVersion = 12.0.30723.0 +MinimumVisualStudioVersion = 10.0.40219.1 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SuperSocket.SocketService.Net40", "SocketService\SuperSocket.SocketService.Net40.csproj", "{B9113694-7226-4152-938D-3172B11571A1}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SuperSocket.Common.Net40", "Common\SuperSocket.Common.Net40.csproj", "{A24F4D38-BA9C-4FD6-95B7-4980DE36131A}" diff --git a/SuperSocket.2012.sln b/SuperSocket.2013.sln similarity index 98% rename from SuperSocket.2012.sln rename to SuperSocket.2013.sln index ee9a4c25a..e8ce7749e 100644 --- a/SuperSocket.2012.sln +++ b/SuperSocket.2013.sln @@ -1,6 +1,8 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 2012 +# Visual Studio 2013 +VisualStudioVersion = 12.0.30723.0 +MinimumVisualStudioVersion = 10.0.40219.1 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SuperSocket.SocketService.Net45", "SocketService\SuperSocket.SocketService.Net45.csproj", "{B9113694-7226-4152-938D-3172B11571A1}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SuperSocket.Common.Net45", "Common\SuperSocket.Common.Net45.csproj", "{A24F4D38-BA9C-4FD6-95B7-4980DE36131A}" diff --git a/SuperSocket.Engine.nuspec b/SuperSocket.Engine.nuspec new file mode 100644 index 000000000..59fcef277 --- /dev/null +++ b/SuperSocket.Engine.nuspec @@ -0,0 +1,41 @@ + + + + SuperSocket.Engine + SuperSocket.Engine + $version$ + Kerry Jiang + Kerry Jiang + http://www.apache.org/licenses/LICENSE-2.0 + http://www.supersocket.net/ + true + SuperSocket is a light weight, cross platform and extensible socket server application framework. You can use it to build a server side socket application (like game server, GPS server, industrial control system, data acquisition server etc) easily without thinking about how to use socket, how to maintain the socket connections and how socket works. + SuperSocket, Socket Server, .Net Socket Server Framework + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/SuperSocket.WebSocket.nuspec b/SuperSocket.WebSocket.nuspec new file mode 100644 index 000000000..9c1c743c7 --- /dev/null +++ b/SuperSocket.WebSocket.nuspec @@ -0,0 +1,30 @@ + + + + SuperSocket.WebSocket + SuperSocket.WebSocket + $version$ + Kerry Jiang + Kerry Jiang + http://www.apache.org/licenses/LICENSE-2.0 + http://www.supersocket.net/ + true + WebSocket server implementation base on SuperSocket. It is just the same codebase with SuperWebSocket but has different assembly name and namespace. + SuperSocket, WebSocket, SuperWebSocket + + + + + + + + + + + + + + + + + diff --git a/SuperSocket.build b/SuperSocket.build index 6979c5fc7..e877e8e6f 100644 --- a/SuperSocket.build +++ b/SuperSocket.build @@ -2,6 +2,7 @@ + @@ -13,8 +14,13 @@ + + + + + @@ -35,13 +41,58 @@ - + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -50,44 +101,58 @@ - + - + + + - + - + + + - + - + + + - + - + + + - + - - + + + + - + - - + + + + + + diff --git a/SuperSocket.nuspec b/SuperSocket.nuspec new file mode 100644 index 000000000..b997f0003 --- /dev/null +++ b/SuperSocket.nuspec @@ -0,0 +1,46 @@ + + + + SuperSocket + SuperSocket + $version$ + Kerry Jiang + Kerry Jiang + http://www.apache.org/licenses/LICENSE-2.0 + http://www.supersocket.net/ + true + SuperSocket is a light weight, cross platform and extensible socket server application framework. You can use it to build a server side socket application (like game server, GPS server, industrial control system, data acquisition server etc) easily without thinking about how to use socket, how to maintain the socket connections and how socket works. + SuperSocket, Socket Server, .Net Socket Server Framework + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Test/BootstrapTestBase.cs b/Test/BootstrapTestBase.cs index 9d5274ced..d5410e09d 100644 --- a/Test/BootstrapTestBase.cs +++ b/Test/BootstrapTestBase.cs @@ -27,8 +27,11 @@ public void ClearBootstrap() if (m_BootStrap != null) { m_BootStrap.Stop(); + (m_BootStrap as IDisposable).Dispose(); m_BootStrap = null; OnBootstrapCleared(); + GC.Collect(); + GC.WaitForFullGCComplete(); } } diff --git a/Test/SocketServerTest.cs b/Test/SocketServerTest.cs index d68016dca..aeb3ecee6 100644 --- a/Test/SocketServerTest.cs +++ b/Test/SocketServerTest.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Collections.Specialized; using System.IO; using System.Linq; using System.Net; @@ -870,5 +871,75 @@ private byte[] ReadStreamToBytes(Stream stream, byte[] endMark) return ms.ToArray(); } + + [Test] + public void TestAddServerInRuntime() + { + var configDir = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Config"); + var filePath = Path.Combine(configDir, DefaultServerConfig); + var bakFilePath = Path.Combine(configDir, DefaultServerConfig + ".tmp"); + + // backup the configuration file for restore + File.Copy(filePath, bakFilePath, true); + + try + { + TestAddServerInRuntimeImplement(); + } + finally + { + ClearBootstrap(); + // restore the configiration file + File.Copy(bakFilePath, filePath, true); + } + } + + void TestAddServerInRuntimeImplement() + { + StartBootstrap(DefaultServerConfig); + var bootstrap = BootStrap as IDynamicBootstrap; + + var options = new NameValueCollection(); + options["testAtt1"] = "1"; + options["testAtt2"] = "2"; + + Assert.IsTrue(bootstrap.AddAndStart(new ServerConfig + { + Name = "TestDynamicServer", + ServerType = "SuperSocket.Test.TestServer, SuperSocket.Test", + Port = 2013, + Options = options + })); + + EndPoint serverAddress = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 2013); + + using (Socket socket = CreateClientSocket()) + { + try + { + socket.Connect(serverAddress); + } + catch + { + Assert.Fail("Failed to connect to the dynamic created server."); + } + + Stream socketStream = GetSocketStream(socket); + using (StreamReader reader = new StreamReader(socketStream, m_Encoding, true)) + using (ConsoleWriter writer = new ConsoleWriter(socketStream, m_Encoding, 1024 * 8)) + { + string welcomeString = reader.ReadLine(); + Assert.AreEqual(string.Format(TestSession.WelcomeMessageFormat, "TestDynamicServer"), welcomeString); + var line = Guid.NewGuid().ToString(); + writer.WriteLine("ECHO " + line); + writer.Flush(); + + Assert.AreEqual(line, reader.ReadLine()); + } + } + + BootStrap.GetServerByName("TestDynamicServer").Stop(); + bootstrap.Remove("TestDynamicServer"); + } } } diff --git a/Test/TcpSocketServerTest.cs b/Test/TcpSocketServerTest.cs index 7cb7af7a0..e27c56873 100644 --- a/Test/TcpSocketServerTest.cs +++ b/Test/TcpSocketServerTest.cs @@ -9,6 +9,9 @@ using System.Threading.Tasks; using System.Threading; using SuperSocket.Test.Command; +using System.Net.Sockets; +using System.IO; +using System.Collections.Specialized; namespace SuperSocket.Test { diff --git a/dotnet/SuperSocket.Common/SuperSocket.Common.csproj b/dotnet/SuperSocket.Common/SuperSocket.Common.csproj new file mode 100644 index 000000000..b8a92b7f2 --- /dev/null +++ b/dotnet/SuperSocket.Common/SuperSocket.Common.csproj @@ -0,0 +1,11 @@ + + + netstandard2.0 + + + + + + + + \ No newline at end of file diff --git a/dotnet/SuperSocket.Dlr/SuperSocket.Dlr.csproj b/dotnet/SuperSocket.Dlr/SuperSocket.Dlr.csproj new file mode 100644 index 000000000..9f5c4f4ab --- /dev/null +++ b/dotnet/SuperSocket.Dlr/SuperSocket.Dlr.csproj @@ -0,0 +1,7 @@ + + + + netstandard2.0 + + + diff --git a/dotnet/SuperSocket.Facility/SuperSocket.Facility.csproj b/dotnet/SuperSocket.Facility/SuperSocket.Facility.csproj new file mode 100644 index 000000000..9f5c4f4ab --- /dev/null +++ b/dotnet/SuperSocket.Facility/SuperSocket.Facility.csproj @@ -0,0 +1,7 @@ + + + + netstandard2.0 + + + diff --git a/dotnet/SuperSocket.SocketBase/SuperSocket.SocketBase.csproj b/dotnet/SuperSocket.SocketBase/SuperSocket.SocketBase.csproj new file mode 100644 index 000000000..9f5c4f4ab --- /dev/null +++ b/dotnet/SuperSocket.SocketBase/SuperSocket.SocketBase.csproj @@ -0,0 +1,7 @@ + + + + netstandard2.0 + + + diff --git a/dotnet/SuperSocket.SocketEngine/SuperSocket.SocketEngine.csproj b/dotnet/SuperSocket.SocketEngine/SuperSocket.SocketEngine.csproj new file mode 100644 index 000000000..9f5c4f4ab --- /dev/null +++ b/dotnet/SuperSocket.SocketEngine/SuperSocket.SocketEngine.csproj @@ -0,0 +1,7 @@ + + + + netstandard2.0 + + + diff --git a/dotnet/SuperSocket.SocketService/SuperSocket.SocketService.csproj b/dotnet/SuperSocket.SocketService/SuperSocket.SocketService.csproj new file mode 100644 index 000000000..9f5c4f4ab --- /dev/null +++ b/dotnet/SuperSocket.SocketService/SuperSocket.SocketService.csproj @@ -0,0 +1,7 @@ + + + + netstandard2.0 + + + diff --git a/dotnet/SuperSocket.sln b/dotnet/SuperSocket.sln new file mode 100644 index 000000000..732e7b96e --- /dev/null +++ b/dotnet/SuperSocket.sln @@ -0,0 +1,104 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +VisualStudioVersion = 15.0.26124.0 +MinimumVisualStudioVersion = 15.0.26124.0 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SuperSocket.Common", "SuperSocket.Common\SuperSocket.Common.csproj", "{F110A53D-0FC6-4360-804B-CEEE4CD7D65A}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SuperSocket.Dlr", "SuperSocket.Dlr\SuperSocket.Dlr.csproj", "{4137EDBA-8382-466D-9DA4-DA5CFD03DE1C}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SuperSocket.Facility", "SuperSocket.Facility\SuperSocket.Facility.csproj", "{89961069-EFA8-470F-AF43-2BE317C01970}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SuperSocket.SocketBase", "SuperSocket.SocketBase\SuperSocket.SocketBase.csproj", "{4EDFE80B-6BD5-413D-ACD0-35F03AF3F219}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SuperSocket.SocketEngine", "SuperSocket.SocketEngine\SuperSocket.SocketEngine.csproj", "{A6E01639-FE36-454B-899C-43F785272184}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SuperSocket.SocketService", "SuperSocket.SocketService\SuperSocket.SocketService.csproj", "{C2B3FA11-D51C-4AFC-BD26-D685C32CC477}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|Any CPU = Release|Any CPU + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {F110A53D-0FC6-4360-804B-CEEE4CD7D65A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F110A53D-0FC6-4360-804B-CEEE4CD7D65A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F110A53D-0FC6-4360-804B-CEEE4CD7D65A}.Debug|x64.ActiveCfg = Debug|Any CPU + {F110A53D-0FC6-4360-804B-CEEE4CD7D65A}.Debug|x64.Build.0 = Debug|Any CPU + {F110A53D-0FC6-4360-804B-CEEE4CD7D65A}.Debug|x86.ActiveCfg = Debug|Any CPU + {F110A53D-0FC6-4360-804B-CEEE4CD7D65A}.Debug|x86.Build.0 = Debug|Any CPU + {F110A53D-0FC6-4360-804B-CEEE4CD7D65A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F110A53D-0FC6-4360-804B-CEEE4CD7D65A}.Release|Any CPU.Build.0 = Release|Any CPU + {F110A53D-0FC6-4360-804B-CEEE4CD7D65A}.Release|x64.ActiveCfg = Release|Any CPU + {F110A53D-0FC6-4360-804B-CEEE4CD7D65A}.Release|x64.Build.0 = Release|Any CPU + {F110A53D-0FC6-4360-804B-CEEE4CD7D65A}.Release|x86.ActiveCfg = Release|Any CPU + {F110A53D-0FC6-4360-804B-CEEE4CD7D65A}.Release|x86.Build.0 = Release|Any CPU + {4137EDBA-8382-466D-9DA4-DA5CFD03DE1C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {4137EDBA-8382-466D-9DA4-DA5CFD03DE1C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {4137EDBA-8382-466D-9DA4-DA5CFD03DE1C}.Debug|x64.ActiveCfg = Debug|Any CPU + {4137EDBA-8382-466D-9DA4-DA5CFD03DE1C}.Debug|x64.Build.0 = Debug|Any CPU + {4137EDBA-8382-466D-9DA4-DA5CFD03DE1C}.Debug|x86.ActiveCfg = Debug|Any CPU + {4137EDBA-8382-466D-9DA4-DA5CFD03DE1C}.Debug|x86.Build.0 = Debug|Any CPU + {4137EDBA-8382-466D-9DA4-DA5CFD03DE1C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {4137EDBA-8382-466D-9DA4-DA5CFD03DE1C}.Release|Any CPU.Build.0 = Release|Any CPU + {4137EDBA-8382-466D-9DA4-DA5CFD03DE1C}.Release|x64.ActiveCfg = Release|Any CPU + {4137EDBA-8382-466D-9DA4-DA5CFD03DE1C}.Release|x64.Build.0 = Release|Any CPU + {4137EDBA-8382-466D-9DA4-DA5CFD03DE1C}.Release|x86.ActiveCfg = Release|Any CPU + {4137EDBA-8382-466D-9DA4-DA5CFD03DE1C}.Release|x86.Build.0 = Release|Any CPU + {89961069-EFA8-470F-AF43-2BE317C01970}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {89961069-EFA8-470F-AF43-2BE317C01970}.Debug|Any CPU.Build.0 = Debug|Any CPU + {89961069-EFA8-470F-AF43-2BE317C01970}.Debug|x64.ActiveCfg = Debug|Any CPU + {89961069-EFA8-470F-AF43-2BE317C01970}.Debug|x64.Build.0 = Debug|Any CPU + {89961069-EFA8-470F-AF43-2BE317C01970}.Debug|x86.ActiveCfg = Debug|Any CPU + {89961069-EFA8-470F-AF43-2BE317C01970}.Debug|x86.Build.0 = Debug|Any CPU + {89961069-EFA8-470F-AF43-2BE317C01970}.Release|Any CPU.ActiveCfg = Release|Any CPU + {89961069-EFA8-470F-AF43-2BE317C01970}.Release|Any CPU.Build.0 = Release|Any CPU + {89961069-EFA8-470F-AF43-2BE317C01970}.Release|x64.ActiveCfg = Release|Any CPU + {89961069-EFA8-470F-AF43-2BE317C01970}.Release|x64.Build.0 = Release|Any CPU + {89961069-EFA8-470F-AF43-2BE317C01970}.Release|x86.ActiveCfg = Release|Any CPU + {89961069-EFA8-470F-AF43-2BE317C01970}.Release|x86.Build.0 = Release|Any CPU + {4EDFE80B-6BD5-413D-ACD0-35F03AF3F219}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {4EDFE80B-6BD5-413D-ACD0-35F03AF3F219}.Debug|Any CPU.Build.0 = Debug|Any CPU + {4EDFE80B-6BD5-413D-ACD0-35F03AF3F219}.Debug|x64.ActiveCfg = Debug|Any CPU + {4EDFE80B-6BD5-413D-ACD0-35F03AF3F219}.Debug|x64.Build.0 = Debug|Any CPU + {4EDFE80B-6BD5-413D-ACD0-35F03AF3F219}.Debug|x86.ActiveCfg = Debug|Any CPU + {4EDFE80B-6BD5-413D-ACD0-35F03AF3F219}.Debug|x86.Build.0 = Debug|Any CPU + {4EDFE80B-6BD5-413D-ACD0-35F03AF3F219}.Release|Any CPU.ActiveCfg = Release|Any CPU + {4EDFE80B-6BD5-413D-ACD0-35F03AF3F219}.Release|Any CPU.Build.0 = Release|Any CPU + {4EDFE80B-6BD5-413D-ACD0-35F03AF3F219}.Release|x64.ActiveCfg = Release|Any CPU + {4EDFE80B-6BD5-413D-ACD0-35F03AF3F219}.Release|x64.Build.0 = Release|Any CPU + {4EDFE80B-6BD5-413D-ACD0-35F03AF3F219}.Release|x86.ActiveCfg = Release|Any CPU + {4EDFE80B-6BD5-413D-ACD0-35F03AF3F219}.Release|x86.Build.0 = Release|Any CPU + {A6E01639-FE36-454B-899C-43F785272184}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A6E01639-FE36-454B-899C-43F785272184}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A6E01639-FE36-454B-899C-43F785272184}.Debug|x64.ActiveCfg = Debug|Any CPU + {A6E01639-FE36-454B-899C-43F785272184}.Debug|x64.Build.0 = Debug|Any CPU + {A6E01639-FE36-454B-899C-43F785272184}.Debug|x86.ActiveCfg = Debug|Any CPU + {A6E01639-FE36-454B-899C-43F785272184}.Debug|x86.Build.0 = Debug|Any CPU + {A6E01639-FE36-454B-899C-43F785272184}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A6E01639-FE36-454B-899C-43F785272184}.Release|Any CPU.Build.0 = Release|Any CPU + {A6E01639-FE36-454B-899C-43F785272184}.Release|x64.ActiveCfg = Release|Any CPU + {A6E01639-FE36-454B-899C-43F785272184}.Release|x64.Build.0 = Release|Any CPU + {A6E01639-FE36-454B-899C-43F785272184}.Release|x86.ActiveCfg = Release|Any CPU + {A6E01639-FE36-454B-899C-43F785272184}.Release|x86.Build.0 = Release|Any CPU + {C2B3FA11-D51C-4AFC-BD26-D685C32CC477}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C2B3FA11-D51C-4AFC-BD26-D685C32CC477}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C2B3FA11-D51C-4AFC-BD26-D685C32CC477}.Debug|x64.ActiveCfg = Debug|Any CPU + {C2B3FA11-D51C-4AFC-BD26-D685C32CC477}.Debug|x64.Build.0 = Debug|Any CPU + {C2B3FA11-D51C-4AFC-BD26-D685C32CC477}.Debug|x86.ActiveCfg = Debug|Any CPU + {C2B3FA11-D51C-4AFC-BD26-D685C32CC477}.Debug|x86.Build.0 = Debug|Any CPU + {C2B3FA11-D51C-4AFC-BD26-D685C32CC477}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C2B3FA11-D51C-4AFC-BD26-D685C32CC477}.Release|Any CPU.Build.0 = Release|Any CPU + {C2B3FA11-D51C-4AFC-BD26-D685C32CC477}.Release|x64.ActiveCfg = Release|Any CPU + {C2B3FA11-D51C-4AFC-BD26-D685C32CC477}.Release|x64.Build.0 = Release|Any CPU + {C2B3FA11-D51C-4AFC-BD26-D685C32CC477}.Release|x86.ActiveCfg = Release|Any CPU + {C2B3FA11-D51C-4AFC-BD26-D685C32CC477}.Release|x86.Build.0 = Release|Any CPU + EndGlobalSection +EndGlobal