├── SSCore ├── .vs │ └── SSCore │ │ └── v15 │ │ └── .suo ├── SSCore.Common │ ├── config.json │ ├── SSCore.Common.csproj │ ├── JsonConfigManager.cs │ ├── BufferManager.cs │ ├── SmartPool.cs │ └── SendingQueue.cs ├── SSCore │ ├── SSCore.csproj.user │ ├── SSCore.csproj │ ├── RequestHandler.cs │ ├── SessionHandler.cs │ ├── IRequestInfo.cs │ ├── ISocketServer.cs │ ├── SocketAsyncEventArgsProxy.cs │ ├── SocketSession2.cs │ ├── ISocketSession.cs │ ├── IAppSession.cs │ ├── Async.cs │ ├── IAppServer.cs │ ├── AsyncSocketSession.cs │ ├── SocketServerBase.cs │ ├── IAsyncSocketSession.cs │ ├── SocketSession.cs │ └── AppSession.cs └── SSCore.sln ├── TestServer ├── config.json ├── TestServer.csproj ├── TestServer.csproj.user └── Program.cs ├── TestClient ├── TestClient.csproj └── Program.cs ├── LICENSE ├── README.md └── .gitignore /SSCore/.vs/SSCore/v15/.suo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/forrestly/SSCore/HEAD/SSCore/.vs/SSCore/v15/.suo -------------------------------------------------------------------------------- /TestServer/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "server name", 3 | "server_type": "", 4 | "ip": "any", 5 | "port": "2020", 6 | "max_connect": "1000" 7 | } 8 | -------------------------------------------------------------------------------- /SSCore/SSCore.Common/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "server name", 3 | "server_type": "", 4 | "ip": "any", 5 | "port": "2020", 6 | "max_connect": "1000" 7 | } 8 | -------------------------------------------------------------------------------- /TestClient/TestClient.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | netcoreapp1.0 6 | 7 | 8 | -------------------------------------------------------------------------------- /SSCore/SSCore/SSCore.csproj.user: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | true 5 | 6 | -------------------------------------------------------------------------------- /TestServer/TestServer.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | Exe 4 | netcoreapp1.0 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /TestServer/TestServer.csproj.user: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | ProjectDebugger 5 | 6 | 7 | TestServer 8 | 9 | -------------------------------------------------------------------------------- /SSCore/SSCore.Common/SSCore.Common.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | netcoreapp1.0 4 | 5 | 6 | 7 | 8 | 9 | 10 | PreserveNewest 11 | 12 | 13 | -------------------------------------------------------------------------------- /SSCore/SSCore/SSCore.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | netcoreapp1.0 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /SSCore/SSCore/RequestHandler.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace SSCore 6 | { 7 | /// 8 | /// Request handler 9 | /// 10 | /// The type of the app session. 11 | /// The type of the request info. 12 | /// The session. 13 | /// The request info. 14 | public delegate void RequestHandler(TAppSession session, TRequestInfo requestInfo) 15 | where TAppSession : IAppSession, IAppSession, new() 16 | where TRequestInfo : IRequestInfo; 17 | 18 | } 19 | -------------------------------------------------------------------------------- /SSCore/SSCore/SessionHandler.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace SSCore 6 | { 7 | /// 8 | /// Used for session level event handler 9 | /// 10 | /// the type of the target session 11 | /// the target session 12 | public delegate void SessionHandler(TAppSession session) 13 | where TAppSession : IAppSession; 14 | 15 | /// 16 | /// Used for session level event handler 17 | /// 18 | /// the type of the target session 19 | /// the target session 20 | /// the target session 21 | /// the event parameter 22 | public delegate void SessionHandler(TAppSession session, TParam value) 23 | where TAppSession : IAppSession; 24 | } 25 | -------------------------------------------------------------------------------- /TestServer/Program.cs: -------------------------------------------------------------------------------- 1 | using SSCore; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Net.Sockets; 5 | 6 | class Program 7 | { 8 | static void Main(string[] args) 9 | { 10 | SocketServerBase server = new SocketServerBase(); 11 | server.NewClientAccepted += Server_NewClientAccepted; 12 | server.Start(); 13 | 14 | Console.WriteLine("enter any key to exit."); 15 | Console.ReadKey(); 16 | } 17 | 18 | private static void Server_NewClientAccepted(Socket client, ISocketSession session) 19 | { 20 | Console.WriteLine("----- new client ------------"); 21 | AsyncSocketSession ass = session as AsyncSocketSession; 22 | 23 | ass.SetReceiveHandler(arg => 24 | { 25 | Console.WriteLine("----- new receive ------------"); 26 | string received = System.Text.Encoding.UTF8.GetString(arg.Buffer, arg.Offset, arg.BytesTransferred); 27 | Console.WriteLine(received); 28 | 29 | ass.Send(received); 30 | }); 31 | } 32 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 forrest 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /SSCore/SSCore/IRequestInfo.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace SSCore 6 | { 7 | /// 8 | /// Request information interface 9 | /// 10 | public interface IRequestInfo 11 | { 12 | /// 13 | /// Gets the key of this request. 14 | /// 15 | string Key { get; } 16 | } 17 | 18 | /// 19 | /// Request information interface 20 | /// 21 | /// The type of the request body. 22 | public interface IRequestInfo : IRequestInfo 23 | { 24 | /// 25 | /// Gets the body of this request. 26 | /// 27 | TRequestBody Body { get; } 28 | } 29 | 30 | 31 | /// 32 | /// Request information interface 33 | /// 34 | /// The type of the request header. 35 | /// The type of the request body. 36 | public interface IRequestInfo : IRequestInfo 37 | { 38 | /// 39 | /// Gets the header of the request. 40 | /// 41 | TRequestHeader Header { get; } 42 | } 43 | 44 | } 45 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # SSCore 2 | dotnet core version of SuperSocket. Pick up socket communication logic from SuperSocket, and exclude command concept. 3 | 4 | ## usage 5 | 6 | 1. Create SocketServerBase object 7 | 2. Add handler for new client connection. 8 | 3. Start the socket server object. 9 | 4. Implement the handler of new client connection. 10 | 5. Add handler for receiving message. 11 | 12 | ## Example 13 | 14 | 15 | class Program 16 | { 17 | static void Main(string[] args) 18 | { 19 | SocketServerBase server = new SocketServerBase(); 20 | server.NewClientAccepted += Server_NewClientAccepted; 21 | server.Start(); 22 | 23 | Console.WriteLine("enter any key to exit."); 24 | Console.ReadKey(); 25 | } 26 | 27 | private static void Server_NewClientAccepted(Socket client, ISocketSession session) 28 | { 29 | Console.WriteLine("----- new client ------------"); 30 | AsyncSocketSession ass = session as AsyncSocketSession; 31 | 32 | ass.SetReceiveHandler(arg => 33 | { 34 | Console.WriteLine("----- new receive ------------"); 35 | string received = System.Text.Encoding.UTF8.GetString(arg.Buffer, arg.Offset, arg.BytesTransferred); 36 | Console.WriteLine(received); 37 | 38 | ass.Send(received); 39 | }); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /SSCore/SSCore/ISocketServer.cs: -------------------------------------------------------------------------------- 1 | using SSCore.Common; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Security.Authentication; 5 | using System.Text; 6 | 7 | namespace SSCore 8 | { 9 | /// 10 | /// It is the basic interface of SocketServer, 11 | /// SocketServer is the abstract server who really listen the comming sockets directly. 12 | /// 13 | public interface ISocketServer 14 | { 15 | /// 16 | /// Starts this instance. 17 | /// 18 | /// 19 | bool Start(); 20 | 21 | /// 22 | /// Resets the session's security protocol. 23 | /// 24 | /// The session. 25 | /// The security protocol. 26 | void ResetSessionSecurity(IAppSession session, SslProtocols security); 27 | /// 28 | /// Gets a value indicating whether this instance is running. 29 | /// 30 | /// 31 | /// true if this instance is running; otherwise, false. 32 | /// 33 | bool IsRunning { get; } 34 | 35 | /// 36 | /// Gets the information of the sending queue pool. 37 | /// 38 | /// 39 | /// The sending queue pool. 40 | /// 41 | IPoolInfo SendingQueuePool { get; } 42 | 43 | /// 44 | /// Stops this instance. 45 | /// 46 | void Stop(); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /SSCore/SSCore.Common/JsonConfigManager.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using Newtonsoft.Json; 4 | using System.IO; 5 | 6 | namespace SSCore.Common 7 | { 8 | public class JsonConfigManager 9 | { 10 | private static JsonConfigManager _instance; 11 | public static JsonConfigManager Instance 12 | { 13 | get { 14 | if (_instance == null) 15 | _instance = new JsonConfigManager(); 16 | return _instance; 17 | } 18 | } 19 | 20 | private Dictionary _configures; 21 | public Dictionary Configures 22 | { 23 | get 24 | { 25 | if (_configures == null) 26 | _configures = ReadConfig(); 27 | 28 | 29 | return _configures; 30 | } 31 | } 32 | 33 | private Dictionary ReadConfig() 34 | { 35 | try 36 | { 37 | using (FileStream fs = new FileStream("config.json", FileMode.Open)) 38 | { 39 | using (StreamReader sr = new StreamReader(fs)) 40 | { 41 | return JsonConvert.DeserializeObject>(sr.ReadToEnd()); 42 | } 43 | } 44 | } 45 | catch (Exception ex) 46 | { 47 | throw ex; 48 | } 49 | 50 | 51 | } 52 | 53 | 54 | 55 | private JsonConfigManager() 56 | { 57 | } 58 | 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /SSCore/SSCore/SocketAsyncEventArgsProxy.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Net.Sockets; 4 | using System.Text; 5 | 6 | namespace SSCore 7 | { 8 | public class SocketAsyncEventArgsProxy 9 | { 10 | public SocketAsyncEventArgs SocketEventArgs { get; private set; } 11 | 12 | public int OrigOffset { get; private set; } 13 | 14 | public bool IsRecyclable { get; private set; } 15 | 16 | private SocketAsyncEventArgsProxy() 17 | { 18 | 19 | } 20 | 21 | public SocketAsyncEventArgsProxy(SocketAsyncEventArgs socketEventArgs) 22 | : this(socketEventArgs, true) 23 | { 24 | 25 | } 26 | 27 | public SocketAsyncEventArgsProxy(SocketAsyncEventArgs socketEventArgs, bool isRecyclable) 28 | { 29 | SocketEventArgs = socketEventArgs; 30 | OrigOffset = socketEventArgs.Offset; 31 | SocketEventArgs.Completed += new EventHandler(SocketEventArgs_Completed); 32 | IsRecyclable = isRecyclable; 33 | } 34 | 35 | static void SocketEventArgs_Completed(object sender, SocketAsyncEventArgs e) 36 | { 37 | var socketSession = e.UserToken as IAsyncSocketSession; 38 | 39 | if (socketSession == null) 40 | return; 41 | 42 | if (e.LastOperation == SocketAsyncOperation.Receive) 43 | { 44 | socketSession.AsyncRun(() => socketSession.ProcessReceive(e)); 45 | } 46 | else 47 | { 48 | throw new ArgumentException("The last operation completed on the socket was not a receive"); 49 | } 50 | } 51 | 52 | public void Initialize(IAsyncSocketSession socketSession) 53 | { 54 | SocketEventArgs.UserToken = socketSession; 55 | } 56 | 57 | public void Reset() 58 | { 59 | SocketEventArgs.UserToken = null; 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /TestClient/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Net.Sockets; 3 | 4 | class Program 5 | { 6 | static void Main(string[] args) 7 | { 8 | StartAsync(); 9 | } 10 | 11 | static void StartAsync() 12 | { 13 | 14 | Socket client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); 15 | 16 | try 17 | { 18 | client.Connect("127.0.0.1", 2020); 19 | } 20 | catch (Exception ex) 21 | { 22 | throw ex; 23 | } 24 | while (true) 25 | { 26 | //try 27 | //{ 28 | // client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); 29 | // client.Connect("127.0.0.1", 2020); 30 | //} 31 | //catch (Exception ex) 32 | //{ 33 | // throw ex; 34 | //} 35 | 36 | try 37 | { 38 | client.Send(System.Text.Encoding.UTF8.GetBytes("hello world!")); 39 | } 40 | catch (Exception) 41 | { 42 | Console.WriteLine("send error."); 43 | } 44 | Console.WriteLine("sent message."); 45 | var buffer = new byte[128]; 46 | try 47 | { 48 | client.Receive(buffer); 49 | } 50 | catch (Exception) 51 | { 52 | Console.WriteLine("receive error."); 53 | } 54 | Console.WriteLine("received message."); 55 | Console.WriteLine(System.Text.Encoding.UTF8.GetString(buffer, 0, 12)); 56 | 57 | var key = Console.ReadKey(); 58 | 59 | if (key.KeyChar.Equals('q')) 60 | break; 61 | 62 | Console.WriteLine("any key to continue, press q to exit."); 63 | //try 64 | //{ 65 | // Console.WriteLine("---Close Client.---"); 66 | // client.Shutdown(SocketShutdown.Both); 67 | // client.Dispose(); 68 | //} 69 | //catch (Exception) 70 | //{ 71 | // Console.WriteLine("Shundown Error"); 72 | //} 73 | 74 | } 75 | } 76 | } -------------------------------------------------------------------------------- /SSCore/SSCore.Common/BufferManager.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Net.Sockets; 4 | using System.Text; 5 | 6 | namespace SSCore.Common 7 | { 8 | /// 9 | /// This class creates a single large buffer which can be divided up and assigned to SocketAsyncEventArgs objects for use 10 | /// with each socket I/O operation. This enables bufffers to be easily reused and gaurds against fragmenting heap memory. 11 | /// 12 | /// The operations exposed on the BufferManager class are not thread safe. 13 | /// 14 | public class BufferManager 15 | { 16 | int m_numBytes; // the total number of bytes controlled by the buffer pool 17 | byte[] m_buffer; // the underlying byte array maintained by the Buffer Manager 18 | Stack m_freeIndexPool; // 19 | int m_currentIndex; 20 | int m_bufferSize; 21 | 22 | /// 23 | /// Initializes a new instance of the class. 24 | /// 25 | /// The total bytes. 26 | /// Size of the buffer. 27 | public BufferManager(int totalBytes, int bufferSize) 28 | { 29 | m_numBytes = totalBytes; 30 | m_currentIndex = 0; 31 | m_bufferSize = bufferSize; 32 | m_freeIndexPool = new Stack(); 33 | } 34 | 35 | /// 36 | /// Allocates buffer space used by the buffer pool 37 | /// 38 | public void InitBuffer() 39 | { 40 | // create one big large buffer and divide that out to each SocketAsyncEventArg object 41 | m_buffer = new byte[m_numBytes]; 42 | } 43 | 44 | /// 45 | /// Assigns a buffer from the buffer pool to the specified SocketAsyncEventArgs object 46 | /// 47 | /// true if the buffer was successfully set, else false 48 | public bool SetBuffer(SocketAsyncEventArgs args) 49 | { 50 | 51 | if (m_freeIndexPool.Count > 0) 52 | { 53 | args.SetBuffer(m_buffer, m_freeIndexPool.Pop(), m_bufferSize); 54 | } 55 | else 56 | { 57 | if ((m_numBytes - m_bufferSize) < m_currentIndex) 58 | { 59 | return false; 60 | } 61 | args.SetBuffer(m_buffer, m_currentIndex, m_bufferSize); 62 | m_currentIndex += m_bufferSize; 63 | } 64 | return true; 65 | } 66 | 67 | /// 68 | /// Removes the buffer from a SocketAsyncEventArg object. This frees the buffer back to the 69 | /// buffer pool 70 | /// 71 | public void FreeBuffer(SocketAsyncEventArgs args) 72 | { 73 | m_freeIndexPool.Push(args.Offset); 74 | args.SetBuffer(null, 0, 0); 75 | } 76 | 77 | } 78 | 79 | } 80 | -------------------------------------------------------------------------------- /SSCore/SSCore/SocketSession2.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Runtime.CompilerServices; 4 | using System.Text; 5 | 6 | namespace SSCore 7 | { 8 | public abstract partial class SocketSession 9 | { 10 | private const string m_GeneralErrorMessage = "Unexpected error"; 11 | private const string m_GeneralSocketErrorMessage = "Unexpected socket error: {0}"; 12 | private const string m_CallerInformation = "Caller: {0}, file path: {1}, line number: {2}"; 13 | 14 | /// 15 | /// Logs the error, skip the ignored exception 16 | /// 17 | /// The exception. 18 | /// The caller. 19 | /// The caller file path. 20 | /// The caller line number. 21 | protected void LogError(Exception exception, [CallerMemberName] string caller = "", [CallerFilePath] string callerFilePath = "", [CallerLineNumber] int callerLineNumber = -1) 22 | { 23 | int socketErrorCode; 24 | 25 | //This exception is ignored, needn't log it 26 | //if (IsIgnorableException(exception, out socketErrorCode)) 27 | // return; 28 | 29 | //var message = socketErrorCode > 0 ? string.Format(m_GeneralSocketErrorMessage, socketErrorCode) : m_GeneralErrorMessage; 30 | 31 | //AppSession.Logger.Error(this 32 | // , message + Environment.NewLine + string.Format(m_CallerInformation, caller, callerFilePath, callerLineNumber) 33 | // , exception); 34 | } 35 | 36 | /// 37 | /// Logs the error, skip the ignored exception 38 | /// 39 | /// The message. 40 | /// The exception. 41 | /// The caller. 42 | /// The caller file path. 43 | /// The caller line number. 44 | protected void LogError(string message, Exception exception, [CallerMemberName] string caller = "", [CallerFilePath] string callerFilePath = "", [CallerLineNumber] int callerLineNumber = -1) 45 | { 46 | int socketErrorCode; 47 | 48 | //This exception is ignored, needn't log it 49 | //if (IsIgnorableException(exception, out socketErrorCode)) 50 | // return; 51 | 52 | //AppSession.Logger.Error(this 53 | // , message + Environment.NewLine + string.Format(m_CallerInformation, caller, callerFilePath, callerLineNumber) 54 | // , exception); 55 | } 56 | 57 | /// 58 | /// Logs the socket error, skip the ignored error 59 | /// 60 | /// The socket error code. 61 | /// The caller. 62 | /// The caller file path. 63 | /// The caller line number. 64 | protected void LogError(int socketErrorCode, [CallerMemberName] string caller = "", [CallerFilePath] string callerFilePath = "", [CallerLineNumber] int callerLineNumber = -1) 65 | { 66 | //if (!Config.LogAllSocketException) 67 | //{ 68 | // //This error is ignored, needn't log it 69 | // if (IsIgnorableSocketError(socketErrorCode)) 70 | // return; 71 | //} 72 | 73 | //AppSession.Logger.Error(this 74 | // , string.Format(m_GeneralSocketErrorMessage, socketErrorCode) + Environment.NewLine + string.Format(m_CallerInformation, caller, callerFilePath, callerLineNumber) 75 | // , new SocketException(socketErrorCode)); 76 | } 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /SSCore/SSCore/ISocketSession.cs: -------------------------------------------------------------------------------- 1 | using SSCore.Common; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Net; 5 | using System.Net.Sockets; 6 | using System.Security.Authentication; 7 | using System.Text; 8 | 9 | namespace SSCore 10 | { 11 | /// 12 | /// CloseReason enum 13 | /// 14 | public enum CloseReason : int 15 | { 16 | /// 17 | /// The socket is closed for unknown reason 18 | /// 19 | Unknown = 0, 20 | 21 | /// 22 | /// Close for server shutdown 23 | /// 24 | ServerShutdown = 1, 25 | 26 | /// 27 | /// The client close the socket 28 | /// 29 | ClientClosing = 2, 30 | 31 | /// 32 | /// The server side close the socket 33 | /// 34 | ServerClosing = 3, 35 | 36 | /// 37 | /// Application error 38 | /// 39 | ApplicationError = 4, 40 | 41 | /// 42 | /// The socket is closed for a socket error 43 | /// 44 | SocketError = 5, 45 | 46 | /// 47 | /// The socket is closed by server for timeout 48 | /// 49 | TimeOut = 6, 50 | 51 | /// 52 | /// Protocol error 53 | /// 54 | ProtocolError = 7, 55 | 56 | /// 57 | /// SuperSocket internal error 58 | /// 59 | InternalError = 8, 60 | } 61 | 62 | /// 63 | /// The interface for socket session 64 | /// 65 | public interface ISocketSession : ISessionBase 66 | { 67 | /// 68 | /// Initializes the specified app session. 69 | /// 70 | /// The app session. 71 | void Initialize(IAppSession appSession); 72 | 73 | void InitializeSendingQueue(ISmartPool pool); 74 | 75 | /// 76 | /// Starts this instance. 77 | /// 78 | void Start(); 79 | 80 | /// 81 | /// Closes the socket session for the specified reason. 82 | /// 83 | /// The reason. 84 | void Close(CloseReason reason); 85 | 86 | 87 | /// 88 | /// Tries to send array segment. 89 | /// 90 | /// The segments. 91 | bool TrySend(IList> segments); 92 | 93 | /// 94 | /// Tries to send array segment. 95 | /// 96 | /// The segment. 97 | bool TrySend(ArraySegment segment); 98 | 99 | /// 100 | /// Applies the secure protocol. 101 | /// 102 | void ApplySecureProtocol(); 103 | 104 | /// 105 | /// Gets the client socket. 106 | /// 107 | Socket Client { get; } 108 | 109 | /// 110 | /// Gets the local listening endpoint. 111 | /// 112 | IPEndPoint LocalEndPoint { get; } 113 | 114 | /// 115 | /// Gets or sets the secure protocol. 116 | /// 117 | /// 118 | /// The secure protocol. 119 | /// 120 | SslProtocols SecureProtocol { get; set; } 121 | 122 | /// 123 | /// Occurs when [closed]. 124 | /// 125 | Action Closed { get; set; } 126 | 127 | /// 128 | /// Gets the app session assosiated with this socket session. 129 | /// 130 | IAppSession AppSession { get; } 131 | 132 | 133 | /// 134 | /// Gets the original receive buffer offset. 135 | /// 136 | /// 137 | /// The original receive buffer offset. 138 | /// 139 | int OrigReceiveOffset { get; } 140 | } 141 | } 142 | -------------------------------------------------------------------------------- /SSCore/SSCore/IAppSession.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Net; 4 | using System.Text; 5 | 6 | namespace SSCore 7 | { 8 | /// 9 | /// The basic session interface 10 | /// 11 | public interface ISessionBase 12 | { 13 | /// 14 | /// Gets the session ID. 15 | /// 16 | string SessionID { get; } 17 | 18 | /// 19 | /// Gets the remote endpoint. 20 | /// 21 | IPEndPoint RemoteEndPoint { get; } 22 | } 23 | 24 | /// 25 | /// The basic interface for appSession 26 | /// 27 | public interface IAppSession : ISessionBase 28 | { 29 | /// 30 | /// Gets the app server. 31 | /// 32 | IAppServer AppServer { get; } 33 | /// 34 | /// Gets the socket session of the AppSession. 35 | /// 36 | ISocketSession SocketSession { get; } 37 | 38 | /// 39 | /// Gets the items. 40 | /// 41 | IDictionary Items { get; } 42 | 43 | /// 44 | /// Gets the config of the server. 45 | /// 46 | //IServerConfig Config { get; } 47 | 48 | /// 49 | /// Gets the local listening endpoint. 50 | /// 51 | IPEndPoint LocalEndPoint { get; } 52 | 53 | /// 54 | /// Gets or sets the last active time of the session. 55 | /// 56 | /// 57 | /// The last active time. 58 | /// 59 | DateTime LastActiveTime { get; set; } 60 | 61 | /// 62 | /// Gets the start time of the session. 63 | /// 64 | DateTime StartTime { get; } 65 | 66 | /// 67 | /// Closes this session. 68 | /// 69 | void Close(); 70 | 71 | /// 72 | /// Closes the session by the specified reason. 73 | /// 74 | /// The close reason. 75 | void Close(CloseReason reason); 76 | 77 | /// 78 | /// Gets a value indicating whether this is connected. 79 | /// 80 | /// 81 | /// true if connected; otherwise, false. 82 | /// 83 | bool Connected { get; } 84 | 85 | /// 86 | /// Gets or sets the charset which is used for transfering text message. 87 | /// 88 | /// The charset. 89 | Encoding Charset { get; set; } 90 | 91 | /// 92 | /// Gets or sets the previous command. 93 | /// 94 | /// 95 | /// The prev command. 96 | /// 97 | string PrevCommand { get; set; } 98 | 99 | /// 100 | /// Gets or sets the current executing command. 101 | /// 102 | /// 103 | /// The current command. 104 | /// 105 | string CurrentCommand { get; set; } 106 | 107 | /// 108 | /// Gets the logger assosiated with this session. 109 | /// 110 | ILog Logger { get; } 111 | 112 | /// 113 | /// Processes the request. 114 | /// 115 | /// The read buffer. 116 | /// The offset. 117 | /// The length. 118 | /// if set to true [to be copied]. 119 | /// return offset delta of next receiving buffer 120 | int ProcessRequest(byte[] readBuffer, int offset, int length, bool toBeCopied); 121 | 122 | /// 123 | /// Starts the session. 124 | /// 125 | void StartSession(); 126 | } 127 | 128 | /// 129 | /// The interface for appSession 130 | /// 131 | /// The type of the app session. 132 | /// The type of the request info. 133 | public interface IAppSession : IAppSession 134 | //where TRequestInfo : IRequestInfo 135 | where TAppSession : IAppSession, IAppSession, new() 136 | { 137 | /// 138 | /// Initializes the specified session. 139 | /// 140 | /// The server. 141 | /// The socket session. 142 | //void Initialize(IAppServer server, ISocketSession socketSession); 143 | } 144 | } 145 | -------------------------------------------------------------------------------- /SSCore/SSCore.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 15 4 | VisualStudioVersion = 15.0.26127.3 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SSCore", "SSCore\SSCore.csproj", "{17604CBD-F1D6-4C88-9EE7-ECB8AEA75FB9}" 7 | EndProject 8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TestServer", "..\TestServer\TestServer.csproj", "{D468EFF9-4D10-4597-8FDC-EBE5C874C560}" 9 | EndProject 10 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TestClient", "..\TestClient\TestClient.csproj", "{3A1377B7-93F1-4FF8-9CCC-669A9145DBC9}" 11 | EndProject 12 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SSCore.Common", "SSCore.Common\SSCore.Common.csproj", "{9BCD9BE5-F666-438C-BB31-EBEF6D5ECE18}" 13 | EndProject 14 | Global 15 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 16 | Debug|Any CPU = Debug|Any CPU 17 | Debug|x64 = Debug|x64 18 | Debug|x86 = Debug|x86 19 | Release|Any CPU = Release|Any CPU 20 | Release|x64 = Release|x64 21 | Release|x86 = Release|x86 22 | EndGlobalSection 23 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 24 | {17604CBD-F1D6-4C88-9EE7-ECB8AEA75FB9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 25 | {17604CBD-F1D6-4C88-9EE7-ECB8AEA75FB9}.Debug|Any CPU.Build.0 = Debug|Any CPU 26 | {17604CBD-F1D6-4C88-9EE7-ECB8AEA75FB9}.Debug|x64.ActiveCfg = Debug|x64 27 | {17604CBD-F1D6-4C88-9EE7-ECB8AEA75FB9}.Debug|x64.Build.0 = Debug|x64 28 | {17604CBD-F1D6-4C88-9EE7-ECB8AEA75FB9}.Debug|x86.ActiveCfg = Debug|x86 29 | {17604CBD-F1D6-4C88-9EE7-ECB8AEA75FB9}.Debug|x86.Build.0 = Debug|x86 30 | {17604CBD-F1D6-4C88-9EE7-ECB8AEA75FB9}.Release|Any CPU.ActiveCfg = Release|Any CPU 31 | {17604CBD-F1D6-4C88-9EE7-ECB8AEA75FB9}.Release|Any CPU.Build.0 = Release|Any CPU 32 | {17604CBD-F1D6-4C88-9EE7-ECB8AEA75FB9}.Release|x64.ActiveCfg = Release|x64 33 | {17604CBD-F1D6-4C88-9EE7-ECB8AEA75FB9}.Release|x64.Build.0 = Release|x64 34 | {17604CBD-F1D6-4C88-9EE7-ECB8AEA75FB9}.Release|x86.ActiveCfg = Release|x86 35 | {17604CBD-F1D6-4C88-9EE7-ECB8AEA75FB9}.Release|x86.Build.0 = Release|x86 36 | {D468EFF9-4D10-4597-8FDC-EBE5C874C560}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 37 | {D468EFF9-4D10-4597-8FDC-EBE5C874C560}.Debug|Any CPU.Build.0 = Debug|Any CPU 38 | {D468EFF9-4D10-4597-8FDC-EBE5C874C560}.Debug|x64.ActiveCfg = Debug|x64 39 | {D468EFF9-4D10-4597-8FDC-EBE5C874C560}.Debug|x64.Build.0 = Debug|x64 40 | {D468EFF9-4D10-4597-8FDC-EBE5C874C560}.Debug|x86.ActiveCfg = Debug|x86 41 | {D468EFF9-4D10-4597-8FDC-EBE5C874C560}.Debug|x86.Build.0 = Debug|x86 42 | {D468EFF9-4D10-4597-8FDC-EBE5C874C560}.Release|Any CPU.ActiveCfg = Release|Any CPU 43 | {D468EFF9-4D10-4597-8FDC-EBE5C874C560}.Release|Any CPU.Build.0 = Release|Any CPU 44 | {D468EFF9-4D10-4597-8FDC-EBE5C874C560}.Release|x64.ActiveCfg = Release|x64 45 | {D468EFF9-4D10-4597-8FDC-EBE5C874C560}.Release|x64.Build.0 = Release|x64 46 | {D468EFF9-4D10-4597-8FDC-EBE5C874C560}.Release|x86.ActiveCfg = Release|x86 47 | {D468EFF9-4D10-4597-8FDC-EBE5C874C560}.Release|x86.Build.0 = Release|x86 48 | {3A1377B7-93F1-4FF8-9CCC-669A9145DBC9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 49 | {3A1377B7-93F1-4FF8-9CCC-669A9145DBC9}.Debug|Any CPU.Build.0 = Debug|Any CPU 50 | {3A1377B7-93F1-4FF8-9CCC-669A9145DBC9}.Debug|x64.ActiveCfg = Debug|x64 51 | {3A1377B7-93F1-4FF8-9CCC-669A9145DBC9}.Debug|x64.Build.0 = Debug|x64 52 | {3A1377B7-93F1-4FF8-9CCC-669A9145DBC9}.Debug|x86.ActiveCfg = Debug|x86 53 | {3A1377B7-93F1-4FF8-9CCC-669A9145DBC9}.Debug|x86.Build.0 = Debug|x86 54 | {3A1377B7-93F1-4FF8-9CCC-669A9145DBC9}.Release|Any CPU.ActiveCfg = Release|Any CPU 55 | {3A1377B7-93F1-4FF8-9CCC-669A9145DBC9}.Release|Any CPU.Build.0 = Release|Any CPU 56 | {3A1377B7-93F1-4FF8-9CCC-669A9145DBC9}.Release|x64.ActiveCfg = Release|x64 57 | {3A1377B7-93F1-4FF8-9CCC-669A9145DBC9}.Release|x64.Build.0 = Release|x64 58 | {3A1377B7-93F1-4FF8-9CCC-669A9145DBC9}.Release|x86.ActiveCfg = Release|x86 59 | {3A1377B7-93F1-4FF8-9CCC-669A9145DBC9}.Release|x86.Build.0 = Release|x86 60 | {9BCD9BE5-F666-438C-BB31-EBEF6D5ECE18}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 61 | {9BCD9BE5-F666-438C-BB31-EBEF6D5ECE18}.Debug|Any CPU.Build.0 = Debug|Any CPU 62 | {9BCD9BE5-F666-438C-BB31-EBEF6D5ECE18}.Debug|x64.ActiveCfg = Debug|x64 63 | {9BCD9BE5-F666-438C-BB31-EBEF6D5ECE18}.Debug|x64.Build.0 = Debug|x64 64 | {9BCD9BE5-F666-438C-BB31-EBEF6D5ECE18}.Debug|x86.ActiveCfg = Debug|x86 65 | {9BCD9BE5-F666-438C-BB31-EBEF6D5ECE18}.Debug|x86.Build.0 = Debug|x86 66 | {9BCD9BE5-F666-438C-BB31-EBEF6D5ECE18}.Release|Any CPU.ActiveCfg = Release|Any CPU 67 | {9BCD9BE5-F666-438C-BB31-EBEF6D5ECE18}.Release|Any CPU.Build.0 = Release|Any CPU 68 | {9BCD9BE5-F666-438C-BB31-EBEF6D5ECE18}.Release|x64.ActiveCfg = Release|x64 69 | {9BCD9BE5-F666-438C-BB31-EBEF6D5ECE18}.Release|x64.Build.0 = Release|x64 70 | {9BCD9BE5-F666-438C-BB31-EBEF6D5ECE18}.Release|x86.ActiveCfg = Release|x86 71 | {9BCD9BE5-F666-438C-BB31-EBEF6D5ECE18}.Release|x86.Build.0 = Release|x86 72 | EndGlobalSection 73 | GlobalSection(SolutionProperties) = preSolution 74 | HideSolutionNode = FALSE 75 | EndGlobalSection 76 | EndGlobal 77 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | ## 4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore 5 | 6 | # User-specific files 7 | *.suo 8 | *.user 9 | *.userosscache 10 | *.sln.docstates 11 | 12 | # User-specific files (MonoDevelop/Xamarin Studio) 13 | *.userprefs 14 | 15 | # Build results 16 | [Dd]ebug/ 17 | [Dd]ebugPublic/ 18 | [Rr]elease/ 19 | [Rr]eleases/ 20 | x64/ 21 | x86/ 22 | bld/ 23 | [Bb]in/ 24 | [Oo]bj/ 25 | [Ll]og/ 26 | 27 | # Visual Studio 2015 cache/options directory 28 | .vs/ 29 | # Uncomment if you have tasks that create the project's static files in wwwroot 30 | #wwwroot/ 31 | 32 | # MSTest test Results 33 | [Tt]est[Rr]esult*/ 34 | [Bb]uild[Ll]og.* 35 | 36 | # NUNIT 37 | *.VisualState.xml 38 | TestResult.xml 39 | 40 | # Build Results of an ATL Project 41 | [Dd]ebugPS/ 42 | [Rr]eleasePS/ 43 | dlldata.c 44 | 45 | # .NET Core 46 | project.lock.json 47 | project.fragment.lock.json 48 | artifacts/ 49 | **/Properties/launchSettings.json 50 | 51 | *_i.c 52 | *_p.c 53 | *_i.h 54 | *.ilk 55 | *.meta 56 | *.obj 57 | *.pch 58 | *.pdb 59 | *.pgc 60 | *.pgd 61 | *.rsp 62 | *.sbr 63 | *.tlb 64 | *.tli 65 | *.tlh 66 | *.tmp 67 | *.tmp_proj 68 | *.log 69 | *.vspscc 70 | *.vssscc 71 | .builds 72 | *.pidb 73 | *.svclog 74 | *.scc 75 | 76 | # Chutzpah Test files 77 | _Chutzpah* 78 | 79 | # Visual C++ cache files 80 | ipch/ 81 | *.aps 82 | *.ncb 83 | *.opendb 84 | *.opensdf 85 | *.sdf 86 | *.cachefile 87 | *.VC.db 88 | *.VC.VC.opendb 89 | 90 | # Visual Studio profiler 91 | *.psess 92 | *.vsp 93 | *.vspx 94 | *.sap 95 | 96 | # TFS 2012 Local Workspace 97 | $tf/ 98 | 99 | # Guidance Automation Toolkit 100 | *.gpState 101 | 102 | # ReSharper is a .NET coding add-in 103 | _ReSharper*/ 104 | *.[Rr]e[Ss]harper 105 | *.DotSettings.user 106 | 107 | # JustCode is a .NET coding add-in 108 | .JustCode 109 | 110 | # TeamCity is a build add-in 111 | _TeamCity* 112 | 113 | # DotCover is a Code Coverage Tool 114 | *.dotCover 115 | 116 | # Visual Studio code coverage results 117 | *.coverage 118 | *.coveragexml 119 | 120 | # NCrunch 121 | _NCrunch_* 122 | .*crunch*.local.xml 123 | nCrunchTemp_* 124 | 125 | # MightyMoose 126 | *.mm.* 127 | AutoTest.Net/ 128 | 129 | # Web workbench (sass) 130 | .sass-cache/ 131 | 132 | # Installshield output folder 133 | [Ee]xpress/ 134 | 135 | # DocProject is a documentation generator add-in 136 | DocProject/buildhelp/ 137 | DocProject/Help/*.HxT 138 | DocProject/Help/*.HxC 139 | DocProject/Help/*.hhc 140 | DocProject/Help/*.hhk 141 | DocProject/Help/*.hhp 142 | DocProject/Help/Html2 143 | DocProject/Help/html 144 | 145 | # Click-Once directory 146 | publish/ 147 | 148 | # Publish Web Output 149 | *.[Pp]ublish.xml 150 | *.azurePubxml 151 | # TODO: Comment the next line if you want to checkin your web deploy settings 152 | # but database connection strings (with potential passwords) will be unencrypted 153 | *.pubxml 154 | *.publishproj 155 | 156 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 157 | # checkin your Azure Web App publish settings, but sensitive information contained 158 | # in these scripts will be unencrypted 159 | PublishScripts/ 160 | 161 | # NuGet Packages 162 | *.nupkg 163 | # The packages folder can be ignored because of Package Restore 164 | **/packages/* 165 | # except build/, which is used as an MSBuild target. 166 | !**/packages/build/ 167 | # Uncomment if necessary however generally it will be regenerated when needed 168 | #!**/packages/repositories.config 169 | # NuGet v3's project.json files produces more ignorable files 170 | *.nuget.props 171 | *.nuget.targets 172 | 173 | # Microsoft Azure Build Output 174 | csx/ 175 | *.build.csdef 176 | 177 | # Microsoft Azure Emulator 178 | ecf/ 179 | rcf/ 180 | 181 | # Windows Store app package directories and files 182 | AppPackages/ 183 | BundleArtifacts/ 184 | Package.StoreAssociation.xml 185 | _pkginfo.txt 186 | 187 | # Visual Studio cache files 188 | # files ending in .cache can be ignored 189 | *.[Cc]ache 190 | # but keep track of directories ending in .cache 191 | !*.[Cc]ache/ 192 | 193 | # Others 194 | ClientBin/ 195 | ~$* 196 | *~ 197 | *.dbmdl 198 | *.dbproj.schemaview 199 | *.jfm 200 | *.pfx 201 | *.publishsettings 202 | orleans.codegen.cs 203 | 204 | # Since there are multiple workflows, uncomment next line to ignore bower_components 205 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 206 | #bower_components/ 207 | 208 | # RIA/Silverlight projects 209 | Generated_Code/ 210 | 211 | # Backup & report files from converting an old project file 212 | # to a newer Visual Studio version. Backup files are not needed, 213 | # because we have git ;-) 214 | _UpgradeReport_Files/ 215 | Backup*/ 216 | UpgradeLog*.XML 217 | UpgradeLog*.htm 218 | 219 | # SQL Server files 220 | *.mdf 221 | *.ldf 222 | 223 | # Business Intelligence projects 224 | *.rdl.data 225 | *.bim.layout 226 | *.bim_*.settings 227 | 228 | # Microsoft Fakes 229 | FakesAssemblies/ 230 | 231 | # GhostDoc plugin setting file 232 | *.GhostDoc.xml 233 | 234 | # Node.js Tools for Visual Studio 235 | .ntvs_analysis.dat 236 | node_modules/ 237 | 238 | # Typescript v1 declaration files 239 | typings/ 240 | 241 | # Visual Studio 6 build log 242 | *.plg 243 | 244 | # Visual Studio 6 workspace options file 245 | *.opt 246 | 247 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 248 | *.vbw 249 | 250 | # Visual Studio LightSwitch build output 251 | **/*.HTMLClient/GeneratedArtifacts 252 | **/*.DesktopClient/GeneratedArtifacts 253 | **/*.DesktopClient/ModelManifest.xml 254 | **/*.Server/GeneratedArtifacts 255 | **/*.Server/ModelManifest.xml 256 | _Pvt_Extensions 257 | 258 | # Paket dependency manager 259 | .paket/paket.exe 260 | paket-files/ 261 | 262 | # FAKE - F# Make 263 | .fake/ 264 | 265 | # JetBrains Rider 266 | .idea/ 267 | *.sln.iml 268 | 269 | # CodeRush 270 | .cr/ 271 | 272 | # Python Tools for Visual Studio (PTVS) 273 | __pycache__/ 274 | *.pyc 275 | 276 | # Cake - Uncomment if you are using it 277 | # tools/** 278 | # !tools/packages.config -------------------------------------------------------------------------------- /SSCore/SSCore/Async.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | using System.Threading.Tasks; 5 | 6 | namespace SSCore 7 | { 8 | /// 9 | /// Async extension class 10 | /// 11 | public static class Async 12 | { 13 | /// 14 | /// Runs the specified task. 15 | /// 16 | /// The log provider. 17 | /// The task. 18 | /// 19 | public static Task AsyncRun(this ILoggerProvider logProvider, Action task) 20 | { 21 | return AsyncRun(logProvider, task, TaskCreationOptions.None); 22 | } 23 | 24 | /// 25 | /// Runs the specified task. 26 | /// 27 | /// The log provider. 28 | /// The task. 29 | /// The task option. 30 | /// 31 | public static Task AsyncRun(this ILoggerProvider logProvider, Action task, TaskCreationOptions taskOption) 32 | { 33 | return AsyncRun(logProvider, task, taskOption, null); 34 | } 35 | 36 | /// 37 | /// Runs the specified task. 38 | /// 39 | /// The log provider. 40 | /// The task. 41 | /// The exception handler. 42 | /// 43 | public static Task AsyncRun(this ILoggerProvider logProvider, Action task, Action exceptionHandler) 44 | { 45 | return AsyncRun(logProvider, task, TaskCreationOptions.None, exceptionHandler); 46 | } 47 | 48 | /// 49 | /// Runs the specified task. 50 | /// 51 | /// The log provider. 52 | /// The task. 53 | /// The task option. 54 | /// The exception handler. 55 | /// 56 | public static Task AsyncRun(this ILoggerProvider logProvider, Action task, TaskCreationOptions taskOption, Action exceptionHandler) 57 | { 58 | return Task.Factory.StartNew(task, taskOption).ContinueWith(t => 59 | { 60 | if (exceptionHandler != null) 61 | exceptionHandler(t.Exception); 62 | else 63 | { 64 | if (logProvider.Logger.IsErrorEnabled) 65 | { 66 | for (var i = 0; i < t.Exception.InnerExceptions.Count; i++) 67 | { 68 | logProvider.Logger.Error(t.Exception.InnerExceptions[i]); 69 | } 70 | } 71 | } 72 | }, TaskContinuationOptions.OnlyOnFaulted); 73 | } 74 | 75 | /// 76 | /// Runs the specified task. 77 | /// 78 | /// The log provider. 79 | /// The task. 80 | /// The state. 81 | /// 82 | public static Task AsyncRun(this ILoggerProvider logProvider, Action task, object state) 83 | { 84 | return AsyncRun(logProvider, task, state, TaskCreationOptions.None); 85 | } 86 | 87 | /// 88 | /// Runs the specified task. 89 | /// 90 | /// The log provider. 91 | /// The task. 92 | /// The state. 93 | /// The task option. 94 | /// 95 | public static Task AsyncRun(this ILoggerProvider logProvider, Action task, object state, TaskCreationOptions taskOption) 96 | { 97 | return AsyncRun(logProvider, task, state, taskOption, null); 98 | } 99 | 100 | /// 101 | /// Runs the specified task. 102 | /// 103 | /// The log provider. 104 | /// The task. 105 | /// The state. 106 | /// The exception handler. 107 | /// 108 | public static Task AsyncRun(this ILoggerProvider logProvider, Action task, object state, Action exceptionHandler) 109 | { 110 | return AsyncRun(logProvider, task, state, TaskCreationOptions.None, exceptionHandler); 111 | } 112 | 113 | /// 114 | /// Runs the specified task. 115 | /// 116 | /// The log provider. 117 | /// The task. 118 | /// The state. 119 | /// The task option. 120 | /// The exception handler. 121 | /// 122 | public static Task AsyncRun(this ILoggerProvider logProvider, Action task, object state, TaskCreationOptions taskOption, Action exceptionHandler) 123 | { 124 | return Task.Factory.StartNew(task, state, taskOption).ContinueWith(t => 125 | { 126 | if (exceptionHandler != null) 127 | exceptionHandler(t.Exception); 128 | else 129 | { 130 | if (logProvider.Logger.IsErrorEnabled) 131 | { 132 | for (var i = 0; i < t.Exception.InnerExceptions.Count; i++) 133 | { 134 | logProvider.Logger.Error(t.Exception.InnerExceptions[i]); 135 | } 136 | } 137 | } 138 | }, TaskContinuationOptions.OnlyOnFaulted); 139 | } 140 | } 141 | } 142 | -------------------------------------------------------------------------------- /SSCore/SSCore/IAppServer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Net.Security; 4 | using System.Security.Authentication; 5 | using System.Security.Cryptography.X509Certificates; 6 | using System.Text; 7 | 8 | namespace SSCore 9 | { 10 | /// 11 | /// The interface for AppServer 12 | /// 13 | public interface IAppServer : ILoggerProvider 14 | { 15 | /// 16 | /// Gets the started time. 17 | /// 18 | /// 19 | /// The started time. 20 | /// 21 | DateTime StartedTime { get; } 22 | 23 | 24 | /// 25 | /// Gets or sets the listeners. 26 | /// 27 | /// 28 | /// The listeners. 29 | /// 30 | //ListenerInfo[] Listeners { get; } 31 | 32 | /// 33 | /// Gets the Receive filter factory. 34 | /// 35 | object ReceiveFilterFactory { get; } 36 | 37 | /// 38 | /// Gets the certificate of current server. 39 | /// 40 | X509Certificate Certificate { get; } 41 | 42 | /// 43 | /// Gets the transfer layer security protocol. 44 | /// 45 | SslProtocols BasicSecurity { get; } 46 | 47 | /// 48 | /// Creates the app session. 49 | /// 50 | /// The socket session. 51 | /// 52 | IAppSession CreateAppSession(ISocketSession socketSession); 53 | 54 | 55 | /// 56 | /// Registers the new created app session into the appserver's session container. 57 | /// 58 | /// The session. 59 | /// 60 | bool RegisterSession(IAppSession session); 61 | 62 | /// 63 | /// Gets the app session by ID. 64 | /// 65 | /// The session ID. 66 | /// 67 | IAppSession GetSessionByID(string sessionID); 68 | 69 | /// 70 | /// Resets the session's security protocol. 71 | /// 72 | /// The session. 73 | /// The security protocol. 74 | void ResetSessionSecurity(IAppSession session, SslProtocols security); 75 | 76 | /// 77 | /// Gets the log factory. 78 | /// 79 | //ILogFactory LogFactory { get; } 80 | } 81 | 82 | /// 83 | /// The raw data processor 84 | /// 85 | /// The type of the app session. 86 | public interface IRawDataProcessor 87 | where TAppSession : IAppSession 88 | { 89 | /// 90 | /// Gets or sets the raw binary data received event handler. 91 | /// TAppSession: session 92 | /// byte[]: receive buffer 93 | /// int: receive buffer offset 94 | /// int: receive lenght 95 | /// bool: whether process the received data further 96 | /// 97 | event Func RawDataReceived; 98 | } 99 | 100 | /// 101 | /// The interface for AppServer 102 | /// 103 | /// The type of the app session. 104 | public interface IAppServer : IAppServer 105 | where TAppSession : IAppSession 106 | { 107 | /// 108 | /// Gets the matched sessions from sessions snapshot. 109 | /// 110 | /// The prediction critera. 111 | /// 112 | IEnumerable GetSessions(Func critera); 113 | 114 | /// 115 | /// Gets all sessions in sessions snapshot. 116 | /// 117 | /// 118 | IEnumerable GetAllSessions(); 119 | 120 | /// 121 | /// Gets/sets the new session connected event handler. 122 | /// 123 | event SessionHandler NewSessionConnected; 124 | 125 | /// 126 | /// Gets/sets the session closed event handler. 127 | /// 128 | event SessionHandler SessionClosed; 129 | } 130 | 131 | /// 132 | /// The interface for AppServer 133 | /// 134 | /// The type of the app session. 135 | /// The type of the request info. 136 | public interface IAppServer : IAppServer 137 | where TRequestInfo : IRequestInfo 138 | where TAppSession : IAppSession, IAppSession, new() 139 | { 140 | /// 141 | /// Occurs when [request comming]. 142 | /// 143 | event RequestHandler NewRequestReceived; 144 | } 145 | 146 | /// 147 | /// The interface for handler of session request 148 | /// 149 | /// The type of the request info. 150 | public interface IRequestHandler 151 | where TRequestInfo : IRequestInfo 152 | { 153 | /// 154 | /// Executes the command. 155 | /// 156 | /// The session. 157 | /// The request info. 158 | void ExecuteCommand(IAppSession session, TRequestInfo requestInfo); 159 | } 160 | 161 | /// 162 | /// SocketServer Accessor interface 163 | /// 164 | public interface ISocketServerAccessor 165 | { 166 | /// 167 | /// Gets the socket server. 168 | /// 169 | /// 170 | /// The socket server. 171 | /// 172 | ISocketServer SocketServer { get; } 173 | } 174 | 175 | /// 176 | /// The basic interface for RemoteCertificateValidator 177 | /// 178 | public interface IRemoteCertificateValidator 179 | { 180 | /// 181 | /// Validates the remote certificate 182 | /// 183 | /// The session. 184 | /// The sender. 185 | /// The certificate. 186 | /// The chain. 187 | /// The SSL policy errors. 188 | /// 189 | bool Validate(IAppSession session, object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors); 190 | } 191 | } 192 | -------------------------------------------------------------------------------- /SSCore/SSCore/AsyncSocketSession.cs: -------------------------------------------------------------------------------- 1 | using SSCore.Common; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Net.Sockets; 6 | using System.Text; 7 | using System.Threading; 8 | 9 | namespace SSCore 10 | { 11 | public class AsyncSocketSession : SocketSession, IAsyncSocketSession 12 | { 13 | private bool m_IsReset; 14 | 15 | private SocketAsyncEventArgs m_SocketEventArgSend; 16 | 17 | private Action _receiveCallback; 18 | 19 | public void SetReceiveHandler(Action handler) 20 | { 21 | _receiveCallback = handler; 22 | } 23 | 24 | public AsyncSocketSession(Socket client, SocketAsyncEventArgsProxy socketAsyncProxy) 25 | : this(client, socketAsyncProxy, false) 26 | { 27 | 28 | } 29 | 30 | public AsyncSocketSession(Socket client, SocketAsyncEventArgsProxy socketAsyncProxy, bool isReset) 31 | : base(client) 32 | { 33 | SocketAsyncProxy = socketAsyncProxy; 34 | m_IsReset = isReset; 35 | } 36 | 37 | ILog ILoggerProvider.Logger 38 | { 39 | get { return AppSession.Logger; } 40 | } 41 | 42 | public override void Initialize(IAppSession appSession) 43 | { 44 | base.Initialize(appSession); 45 | 46 | //Initialize SocketAsyncProxy for receiving 47 | SocketAsyncProxy.Initialize(this); 48 | 49 | if (!SyncSend) 50 | { 51 | //Initialize SocketAsyncEventArgs for sending 52 | m_SocketEventArgSend = new SocketAsyncEventArgs(); 53 | m_SocketEventArgSend.Completed += new EventHandler(OnSendingCompleted); 54 | } 55 | } 56 | 57 | public override void Start() 58 | { 59 | StartReceive(SocketAsyncProxy.SocketEventArgs); 60 | 61 | //if (!m_IsReset) 62 | // StartSession(); 63 | } 64 | 65 | bool ProcessCompleted(SocketAsyncEventArgs e) 66 | { 67 | if (e.SocketError == SocketError.Success) 68 | { 69 | if (e.BytesTransferred > 0) 70 | { 71 | return true; 72 | } 73 | } 74 | else 75 | { 76 | LogError((int)e.SocketError); 77 | } 78 | 79 | return false; 80 | } 81 | 82 | void OnSendingCompleted(object sender, SocketAsyncEventArgs e) 83 | { 84 | var queue = e.UserToken as SendingQueue; 85 | 86 | if (!ProcessCompleted(e)) 87 | { 88 | ClearPrevSendState(e); 89 | OnSendError(queue, CloseReason.SocketError); 90 | return; 91 | } 92 | 93 | var count = queue.Sum(q => q.Count); 94 | 95 | if (count != e.BytesTransferred) 96 | { 97 | queue.InternalTrim(e.BytesTransferred); 98 | //AppSession.Logger.InfoFormat("{0} of {1} were transferred, send the rest {2} bytes right now.", e.BytesTransferred, count, queue.Sum(q => q.Count)); 99 | ClearPrevSendState(e); 100 | SendAsync(queue); 101 | return; 102 | } 103 | 104 | ClearPrevSendState(e); 105 | base.OnSendingCompleted(queue); 106 | } 107 | 108 | private void ClearPrevSendState(SocketAsyncEventArgs e) 109 | { 110 | e.UserToken = null; 111 | 112 | //Clear previous sending buffer of sae to avoid memory leak 113 | if (e.Buffer != null) 114 | { 115 | e.SetBuffer(null, 0, 0); 116 | } 117 | else if (e.BufferList != null) 118 | { 119 | e.BufferList = null; 120 | } 121 | } 122 | 123 | private void StartReceive(SocketAsyncEventArgs e) 124 | { 125 | StartReceive(e, 0); 126 | } 127 | 128 | private void StartReceive(SocketAsyncEventArgs e, int offsetDelta) 129 | { 130 | bool willRaiseEvent = false; 131 | 132 | try 133 | { 134 | if (offsetDelta < 0 || offsetDelta >= 4096) //Config.ReceiveBufferSize) 135 | throw new ArgumentException(string.Format("Illigal offsetDelta: {0}", offsetDelta), "offsetDelta"); 136 | 137 | var predictOffset = SocketAsyncProxy.OrigOffset + offsetDelta; 138 | 139 | if (e.Offset != predictOffset) 140 | { 141 | e.SetBuffer(predictOffset, 4096 - offsetDelta); // Config.ReceiveBufferSize - offsetDelta); 142 | } 143 | 144 | // the connection is closing or closed 145 | if (!OnReceiveStarted()) 146 | return; 147 | 148 | willRaiseEvent = Client.ReceiveAsync(e); 149 | } 150 | catch (Exception exc) 151 | { 152 | LogError(exc); 153 | OnReceiveTerminated(CloseReason.SocketError); 154 | return; 155 | } 156 | 157 | if (!willRaiseEvent) 158 | { 159 | ProcessReceive(e); 160 | } 161 | } 162 | 163 | protected override void SendSync(SendingQueue queue) 164 | { 165 | try 166 | { 167 | for (var i = 0; i < queue.Count; i++) 168 | { 169 | var item = queue[i]; 170 | 171 | var client = Client; 172 | 173 | if (client == null) 174 | return; 175 | 176 | client.Send(item.Array, item.Offset, item.Count, SocketFlags.None); 177 | } 178 | 179 | OnSendingCompleted(queue); 180 | } 181 | catch (Exception e) 182 | { 183 | LogError(e); 184 | 185 | OnSendError(queue, CloseReason.SocketError); 186 | return; 187 | } 188 | } 189 | 190 | /// 191 | /// Sends the message to client. 192 | /// 193 | /// The message which will be sent. 194 | public virtual void Send(string message) 195 | { 196 | var data = System.Text.Encoding.UTF8.GetBytes(message); 197 | Send(data, 0, data.Length); 198 | } 199 | 200 | /// 201 | /// Sends the data to client. 202 | /// 203 | /// The data which will be sent. 204 | /// The offset. 205 | /// The length. 206 | public virtual void Send(byte[] data, int offset, int length) 207 | { 208 | InternalTrySend(new ArraySegment(data, offset, length)); 209 | } 210 | 211 | private bool InternalTrySend(ArraySegment segment) 212 | { 213 | if (!TrySend(segment)) 214 | return false; 215 | 216 | return true; 217 | } 218 | 219 | protected override void SendAsync(SendingQueue queue) 220 | { 221 | try 222 | { 223 | m_SocketEventArgSend.UserToken = queue; 224 | 225 | if (queue.Count > 1) 226 | m_SocketEventArgSend.BufferList = queue; 227 | else 228 | { 229 | var item = queue[0]; 230 | m_SocketEventArgSend.SetBuffer(item.Array, item.Offset, item.Count); 231 | } 232 | 233 | var client = Client; 234 | 235 | if (client == null) 236 | { 237 | OnSendError(queue, CloseReason.SocketError); 238 | return; 239 | } 240 | 241 | if (!client.SendAsync(m_SocketEventArgSend)) 242 | OnSendingCompleted(client, m_SocketEventArgSend); 243 | } 244 | catch (Exception e) 245 | { 246 | LogError(e); 247 | 248 | ClearPrevSendState(m_SocketEventArgSend); 249 | OnSendError(queue, CloseReason.SocketError); 250 | } 251 | } 252 | 253 | public SocketAsyncEventArgsProxy SocketAsyncProxy { get; private set; } 254 | 255 | public void ProcessReceive(SocketAsyncEventArgs e) 256 | { 257 | if (!ProcessCompleted(e)) 258 | { 259 | OnReceiveTerminated(e.SocketError == SocketError.Success ? CloseReason.ClientClosing : CloseReason.SocketError); 260 | return; 261 | } 262 | 263 | OnReceiveEnded(); 264 | 265 | int offsetDelta = 0; 266 | 267 | _receiveCallback(e); 268 | 269 | //try 270 | //{ 271 | // offsetDelta = this.AppSession.ProcessRequest(e.Buffer, e.Offset, e.BytesTransferred, true); 272 | //} 273 | //catch (Exception exc) 274 | //{ 275 | // LogError("Protocol error", exc); 276 | // this.Close(CloseReason.ProtocolError); 277 | // return; 278 | //} 279 | 280 | //read the next block of data sent from the client 281 | StartReceive(e, offsetDelta); 282 | } 283 | 284 | protected override void OnClosed(CloseReason reason) 285 | { 286 | var sae = m_SocketEventArgSend; 287 | 288 | if (sae == null) 289 | { 290 | base.OnClosed(reason); 291 | return; 292 | } 293 | 294 | if (Interlocked.CompareExchange(ref m_SocketEventArgSend, null, sae) == sae) 295 | { 296 | sae.Dispose(); 297 | base.OnClosed(reason); 298 | } 299 | } 300 | 301 | public override void ApplySecureProtocol() 302 | { 303 | //TODO: Implement async socket SSL/TLS encryption 304 | } 305 | 306 | public override int OrigReceiveOffset 307 | { 308 | get { return SocketAsyncProxy.OrigOffset; } 309 | } 310 | } 311 | } 312 | -------------------------------------------------------------------------------- /SSCore/SSCore.Common/SmartPool.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Concurrent; 3 | using System.Collections.Generic; 4 | using System.Text; 5 | using System.Threading; 6 | 7 | namespace SSCore.Common 8 | { 9 | /// 10 | /// The pool information class 11 | /// 12 | public interface IPoolInfo 13 | { 14 | /// 15 | /// Gets the min size of the pool. 16 | /// 17 | /// 18 | /// The min size of the pool. 19 | /// 20 | int MinPoolSize { get; } 21 | 22 | /// 23 | /// Gets the max size of the pool. 24 | /// 25 | /// 26 | /// The max size of the pool. 27 | /// 28 | int MaxPoolSize { get; } 29 | 30 | 31 | /// 32 | /// Gets the avialable items count. 33 | /// 34 | /// 35 | /// The avialable items count. 36 | /// 37 | int AvialableItemsCount { get; } 38 | 39 | 40 | /// 41 | /// Gets the total items count, include items in the pool and outside the pool. 42 | /// 43 | /// 44 | /// The total items count. 45 | /// 46 | int TotalItemsCount { get; } 47 | } 48 | 49 | /// 50 | /// The basic interface of smart pool 51 | /// 52 | /// 53 | public interface ISmartPool : IPoolInfo 54 | { 55 | /// 56 | /// Initializes the specified min pool size. 57 | /// 58 | /// The min size of the pool. 59 | /// The max size of the pool. 60 | /// The source creator. 61 | /// 62 | void Initialize(int minPoolSize, int maxPoolSize, ISmartPoolSourceCreator sourceCreator); 63 | 64 | /// 65 | /// Pushes the specified item into the pool. 66 | /// 67 | /// The item. 68 | void Push(T item); 69 | 70 | /// 71 | /// Tries to get one item from the pool. 72 | /// 73 | /// The item. 74 | /// 75 | bool TryGet(out T item); 76 | } 77 | 78 | /// 79 | /// ISmartPoolSource 80 | /// 81 | public interface ISmartPoolSource 82 | { 83 | /// 84 | /// Gets the count. 85 | /// 86 | /// 87 | /// The count. 88 | /// 89 | int Count { get; } 90 | } 91 | 92 | /// 93 | /// SmartPoolSource 94 | /// 95 | public class SmartPoolSource : ISmartPoolSource 96 | { 97 | /// 98 | /// Initializes a new instance of the class. 99 | /// 100 | /// The source. 101 | /// The items count. 102 | public SmartPoolSource(object source, int itemsCount) 103 | { 104 | Source = source; 105 | Count = itemsCount; 106 | } 107 | 108 | /// 109 | /// Gets the source. 110 | /// 111 | /// 112 | /// The source. 113 | /// 114 | public object Source { get; private set; } 115 | 116 | /// 117 | /// Gets the count. 118 | /// 119 | /// 120 | /// The count. 121 | /// 122 | public int Count { get; private set; } 123 | } 124 | 125 | 126 | 127 | /// 128 | /// ISmartPoolSourceCreator 129 | /// 130 | /// 131 | public interface ISmartPoolSourceCreator 132 | { 133 | /// 134 | /// Creates the specified size. 135 | /// 136 | /// The size. 137 | /// The pool items. 138 | /// 139 | ISmartPoolSource Create(int size, out T[] poolItems); 140 | } 141 | 142 | /// 143 | /// The smart pool 144 | /// 145 | /// 146 | public class SmartPool : ISmartPool 147 | { 148 | private ConcurrentStack m_GlobalStack; 149 | 150 | private ISmartPoolSource[] m_ItemsSource; 151 | 152 | private int m_CurrentSourceCount; 153 | 154 | private ISmartPoolSourceCreator m_SourceCreator; 155 | 156 | private int m_MinPoolSize; 157 | 158 | /// 159 | /// Gets the size of the min pool. 160 | /// 161 | /// 162 | /// The size of the min pool. 163 | /// 164 | public int MinPoolSize 165 | { 166 | get 167 | { 168 | return m_MinPoolSize; 169 | } 170 | } 171 | 172 | private int m_MaxPoolSize; 173 | 174 | /// 175 | /// Gets the size of the max pool. 176 | /// 177 | /// 178 | /// The size of the max pool. 179 | /// 180 | public int MaxPoolSize 181 | { 182 | get 183 | { 184 | return m_MaxPoolSize; 185 | } 186 | } 187 | 188 | /// 189 | /// Gets the avialable items count. 190 | /// 191 | /// 192 | /// The avialable items count. 193 | /// 194 | public int AvialableItemsCount 195 | { 196 | get 197 | { 198 | return m_GlobalStack.Count; 199 | } 200 | } 201 | 202 | private int m_TotalItemsCount; 203 | 204 | /// 205 | /// Gets the total items count, include items in the pool and outside the pool. 206 | /// 207 | /// 208 | /// The total items count. 209 | /// 210 | public int TotalItemsCount 211 | { 212 | get { return m_TotalItemsCount; } 213 | } 214 | 215 | /// 216 | /// Initializes the specified min and max pool size. 217 | /// 218 | /// The min size of the pool. 219 | /// The max size of the pool. 220 | /// The source creator. 221 | public void Initialize(int minPoolSize, int maxPoolSize, ISmartPoolSourceCreator sourceCreator) 222 | { 223 | m_MinPoolSize = minPoolSize; 224 | m_MaxPoolSize = maxPoolSize; 225 | m_SourceCreator = sourceCreator; 226 | m_GlobalStack = new ConcurrentStack(); 227 | 228 | var n = 0; 229 | 230 | if (minPoolSize != maxPoolSize) 231 | { 232 | var currentValue = minPoolSize; 233 | 234 | while (true) 235 | { 236 | n++; 237 | 238 | var thisValue = currentValue * 2; 239 | 240 | if (thisValue >= maxPoolSize) 241 | break; 242 | 243 | currentValue = thisValue; 244 | } 245 | } 246 | 247 | m_ItemsSource = new ISmartPoolSource[n + 1]; 248 | 249 | T[] items; 250 | m_ItemsSource[0] = sourceCreator.Create(minPoolSize, out items); 251 | m_CurrentSourceCount = 1; 252 | 253 | for (var i = 0; i < items.Length; i++) 254 | { 255 | m_GlobalStack.Push(items[i]); 256 | } 257 | 258 | m_TotalItemsCount = m_MinPoolSize; 259 | } 260 | 261 | private int m_IsIncreasing = 0; 262 | 263 | /// 264 | /// Pushes the specified item into the pool. 265 | /// 266 | /// The item. 267 | public void Push(T item) 268 | { 269 | m_GlobalStack.Push(item); 270 | } 271 | 272 | bool TryPopWithWait(out T item, int waitTicks) 273 | { 274 | var spinWait = new SpinWait(); 275 | 276 | while (true) 277 | { 278 | spinWait.SpinOnce(); 279 | 280 | if (m_GlobalStack.TryPop(out item)) 281 | return true; 282 | 283 | if (spinWait.Count >= waitTicks) 284 | { 285 | return false; 286 | } 287 | } 288 | } 289 | 290 | /// 291 | /// Tries to get one item from the pool. 292 | /// 293 | /// The item. 294 | /// 295 | /// 296 | public bool TryGet(out T item) 297 | { 298 | if (m_GlobalStack.TryPop(out item)) 299 | return true; 300 | 301 | var currentSourceCount = m_CurrentSourceCount; 302 | 303 | if (currentSourceCount >= m_ItemsSource.Length) 304 | { 305 | return TryPopWithWait(out item, 100); 306 | } 307 | 308 | var isIncreasing = m_IsIncreasing; 309 | 310 | if (isIncreasing == 1) 311 | return TryPopWithWait(out item, 100); 312 | 313 | if (Interlocked.CompareExchange(ref m_IsIncreasing, 1, isIncreasing) != isIncreasing) 314 | return TryPopWithWait(out item, 100); 315 | 316 | IncreaseCapacity(); 317 | 318 | m_IsIncreasing = 0; 319 | 320 | if (!m_GlobalStack.TryPop(out item)) 321 | { 322 | return false; 323 | } 324 | 325 | return true; 326 | } 327 | 328 | private void IncreaseCapacity() 329 | { 330 | var newItemsCount = Math.Min(m_TotalItemsCount, m_MaxPoolSize - m_TotalItemsCount); 331 | 332 | T[] items; 333 | m_ItemsSource[m_CurrentSourceCount++] = m_SourceCreator.Create(newItemsCount, out items); 334 | 335 | m_TotalItemsCount += newItemsCount; 336 | 337 | for (var i = 0; i < items.Length; i++) 338 | { 339 | m_GlobalStack.Push(items[i]); 340 | } 341 | } 342 | } 343 | } 344 | -------------------------------------------------------------------------------- /SSCore/SSCore/SocketServerBase.cs: -------------------------------------------------------------------------------- 1 | using SSCore.Common; 2 | using System; 3 | using System.Collections.Concurrent; 4 | using System.Collections.Generic; 5 | using System.Net; 6 | using System.Net.Sockets; 7 | using System.Security.Authentication; 8 | 9 | namespace SSCore 10 | { 11 | public delegate void NewSessionHandler(Socket client, ISocketSession session); 12 | 13 | /// 14 | /// The interface for socket session which requires negotiation before communication 15 | /// 16 | interface INegotiateSocketSession 17 | { 18 | /// 19 | /// Start negotiates 20 | /// 21 | void Negotiate(); 22 | 23 | /// 24 | /// Gets a value indicating whether this is result. 25 | /// 26 | /// 27 | /// true if result; otherwise, false. 28 | /// 29 | bool Result { get; } 30 | 31 | 32 | /// 33 | /// Gets the app session. 34 | /// 35 | /// 36 | /// The app session. 37 | /// 38 | IAppSession AppSession { get; } 39 | 40 | /// 41 | /// Occurs when [negotiate completed]. 42 | /// 43 | event EventHandler NegotiateCompleted; 44 | } 45 | 46 | public class SocketServerBase 47 | { 48 | protected object SyncRoot = new object(); 49 | 50 | public IAppServer AppServer { get; private set; } 51 | 52 | public bool IsRunning { get; protected set; } 53 | 54 | //protected ListenerInfo[] ListenerInfos { get; private set; } 55 | 56 | //protected List Listeners { get; private set; } 57 | 58 | /// 59 | /// Gets the sending queue manager. 60 | /// 61 | /// 62 | /// The sending queue manager. 63 | /// 64 | internal ISmartPool SendingQueuePool { get; private set; } 65 | 66 | public event NewSessionHandler NewClientAccepted; 67 | 68 | protected bool IsStopped { get; set; } 69 | 70 | private BufferManager _bufferManager; 71 | 72 | private ConcurrentStack _readWritePool; 73 | 74 | private Socket _socket 75 | { 76 | get; 77 | set; 78 | } 79 | 80 | public SocketServerBase() 81 | { 82 | Dictionary config = JsonConfigManager.Instance.Configures; 83 | if (config == null) 84 | throw new ArgumentNullException(); 85 | if (_socket != null) 86 | throw new Exception("socket server exists."); 87 | 88 | try 89 | { 90 | _socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); 91 | _socket.Bind(new IPEndPoint(IPAddress.TryParse(config["ip"], out IPAddress address) ? address : IPAddress.Any, int.Parse(config["port"]))); 92 | } 93 | catch (Exception ex) 94 | { 95 | throw ex; 96 | } 97 | } 98 | 99 | public bool Start() 100 | { 101 | int bufferSize = 0;//AppServer.Config.ReceiveBufferSize; 102 | 103 | int maxConnection = int.Parse(JsonConfigManager.Instance.Configures["max_connect"]); 104 | 105 | if (bufferSize <= 0) 106 | bufferSize = 1024 * 4; 107 | 108 | _bufferManager = new BufferManager(bufferSize * maxConnection, bufferSize); 109 | 110 | try 111 | { 112 | _bufferManager.InitBuffer(); 113 | } 114 | catch (Exception e) 115 | { 116 | throw e; 117 | //AppServer.Logger.Error("Failed to allocate buffer for async socket communication, may because there is no enough memory, please decrease maxConnectionNumber in configuration!", e); 118 | //return false; 119 | } 120 | 121 | var sendingQueuePool = new SmartPool(); 122 | sendingQueuePool.Initialize(Math.Max(maxConnection / 6, 256), 123 | Math.Max(maxConnection * 2, 256), 124 | new SendingQueueSourceCreator(5)); 125 | 126 | SendingQueuePool = sendingQueuePool; 127 | 128 | // preallocate pool of SocketAsyncEventArgs objects 129 | SocketAsyncEventArgs socketEventArg; 130 | 131 | var socketArgsProxyList = new List(maxConnection); 132 | 133 | for (int i = 0; i < maxConnection; i++) 134 | { 135 | //Pre-allocate a set of reusable SocketAsyncEventArgs 136 | socketEventArg = new SocketAsyncEventArgs(); 137 | _bufferManager.SetBuffer(socketEventArg); 138 | 139 | socketArgsProxyList.Add(new SocketAsyncEventArgsProxy(socketEventArg)); 140 | } 141 | 142 | _readWritePool = new ConcurrentStack(socketArgsProxyList); 143 | 144 | 145 | 146 | _socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.KeepAlive, true); 147 | _socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.DontLinger, true); 148 | 149 | SocketAsyncEventArgs acceptEventArg = new SocketAsyncEventArgs(); 150 | //m_AcceptSAE = acceptEventArg; 151 | acceptEventArg.Completed += AcceptEventArg_Completed; 152 | _socket.Listen(100); 153 | 154 | if (!_socket.AcceptAsync(acceptEventArg)) 155 | ProcessAccept(acceptEventArg); 156 | 157 | IsRunning = true; 158 | return true; 159 | } 160 | 161 | private void AcceptEventArg_Completed(object sender, SocketAsyncEventArgs e) 162 | { 163 | ProcessAccept(e); 164 | } 165 | 166 | void ProcessAccept(SocketAsyncEventArgs e) 167 | { 168 | Socket socket = null; 169 | 170 | if (e.SocketError != SocketError.Success) 171 | { 172 | var errorCode = (int)e.SocketError; 173 | 174 | //The listen socket was closed 175 | if (errorCode == 995 || errorCode == 10004 || errorCode == 10038) 176 | return; 177 | 178 | //OnError(new SocketException(errorCode)); 179 | } 180 | else 181 | { 182 | socket = e.AcceptSocket; 183 | } 184 | 185 | e.AcceptSocket = null; 186 | 187 | bool willRaiseEvent = false; 188 | 189 | try 190 | { 191 | willRaiseEvent = _socket.AcceptAsync(e); 192 | } 193 | catch (ObjectDisposedException) 194 | { 195 | //The listener was stopped 196 | //Do nothing 197 | //make sure ProcessAccept won't be executed in this thread 198 | willRaiseEvent = true; 199 | } 200 | catch (NullReferenceException) 201 | { 202 | //The listener was stopped 203 | //Do nothing 204 | //make sure ProcessAccept won't be executed in this thread 205 | willRaiseEvent = true; 206 | } 207 | catch (Exception exc) 208 | { 209 | //OnError(exc); 210 | //make sure ProcessAccept won't be executed in this thread 211 | willRaiseEvent = true; 212 | } 213 | 214 | if (socket != null) 215 | OnNewClientAccepted(socket, null); 216 | 217 | if (!willRaiseEvent) 218 | ProcessAccept(e); 219 | } 220 | 221 | protected void OnNewClientAccepted(Socket client, object state) 222 | { 223 | if (IsStopped) 224 | return; 225 | 226 | ProcessNewClient(client, SslProtocols.None); 227 | } 228 | 229 | private IAppSession ProcessNewClient(Socket socket, SslProtocols security) 230 | { 231 | if (!_readWritePool.TryPop(out SocketAsyncEventArgsProxy result)) 232 | { 233 | AppServer.AsyncRun(() => socket.Shutdown(SocketShutdown.Both)); 234 | return null; 235 | } 236 | 237 | ISocketSession socketSession = new AsyncSocketSession(socket, result); 238 | 239 | socketSession.InitializeSendingQueue(this.SendingQueuePool); 240 | 241 | socketSession.Initialize(null); 242 | 243 | socketSession.Start(); 244 | 245 | 246 | //var session = CreateSession(socket, socketSession); 247 | 248 | 249 | //if (session == null) 250 | //{ 251 | // result.Reset(); 252 | // this._readWritePool.Push(result); 253 | // AppServer.AsyncRun(() => socket.Shutdown(SocketShutdown.Both)); 254 | // return null; 255 | //} 256 | 257 | socketSession.Closed += SessionClosed; 258 | 259 | //var negotiateSession = socketSession as INegotiateSocketSession; 260 | 261 | //if (negotiateSession == null) 262 | //{ 263 | // if (RegisterSession(session)) 264 | // { 265 | // AppServer.AsyncRun(() => socketSession.Start()); 266 | // } 267 | 268 | // return session; 269 | //} 270 | 271 | //negotiateSession.NegotiateCompleted += OnSocketSessionNegotiateCompleted; 272 | //negotiateSession.Negotiate(); 273 | 274 | NewClientAccepted(socket, socketSession); 275 | return null; 276 | } 277 | 278 | private void OnSocketSessionNegotiateCompleted(object sender, EventArgs e) 279 | { 280 | var socketSession = sender as ISocketSession; 281 | var negotiateSession = socketSession as INegotiateSocketSession; 282 | 283 | if (!negotiateSession.Result) 284 | { 285 | socketSession.Close(CloseReason.SocketError); 286 | return; 287 | } 288 | 289 | if (RegisterSession(negotiateSession.AppSession)) 290 | { 291 | AppServer.AsyncRun(() => socketSession.Start()); 292 | } 293 | } 294 | 295 | void SessionClosed(ISocketSession session, CloseReason reason) 296 | { 297 | var socketSession = session as IAsyncSocketSessionBase; 298 | if (socketSession == null) 299 | return; 300 | 301 | var proxy = socketSession.SocketAsyncProxy; 302 | proxy.Reset(); 303 | var args = proxy.SocketEventArgs; 304 | 305 | //var serverState = AppServer.State; 306 | var pool = this._readWritePool; 307 | 308 | if (pool == null) // || serverState == ServerState.Stopping || serverState == ServerState.NotStarted) 309 | { 310 | if (!Environment.HasShutdownStarted) 311 | args.Dispose(); 312 | return; 313 | } 314 | 315 | if (proxy.OrigOffset != args.Offset) 316 | { 317 | args.SetBuffer(proxy.OrigOffset, 4096); 318 | } 319 | 320 | if (!proxy.IsRecyclable) 321 | { 322 | //cannot be recycled, so release the resource and don't return it to the pool 323 | args.Dispose(); 324 | return; 325 | } 326 | 327 | pool.Push(proxy); 328 | } 329 | 330 | protected IAppSession CreateSession(Socket client, ISocketSession session) 331 | { 332 | //if (m_SendTimeOut > 0) 333 | client.SendTimeout = 1000;// m_SendTimeOut; 334 | 335 | //if (m_ReceiveBufferSize > 0) 336 | client.ReceiveBufferSize = 4096; // m_ReceiveBufferSize; 337 | 338 | //if (m_SendBufferSize > 0) 339 | client.SendBufferSize = 4096;// m_SendBufferSize; 340 | 341 | //if (!Platform.SupportSocketIOControlByCodeEnum) 342 | // client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.KeepAlive, m_KeepAliveOptionValues); 343 | //else 344 | // client.IOControl(IOControlCode.KeepAliveValues, m_KeepAliveOptionValues, m_KeepAliveOptionOutValues); 345 | 346 | client.NoDelay = true; 347 | client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.DontLinger, true); 348 | 349 | return this.AppServer.CreateAppSession(session); 350 | } 351 | 352 | private bool RegisterSession(IAppSession appSession) 353 | { 354 | if (AppServer.RegisterSession(appSession)) 355 | return true; 356 | 357 | appSession.SocketSession.Close(CloseReason.InternalError); 358 | return false; 359 | } 360 | 361 | 362 | } 363 | } 364 | -------------------------------------------------------------------------------- /SSCore/SSCore/IAsyncSocketSession.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Net.Sockets; 3 | 4 | namespace SSCore 5 | { 6 | /// 7 | /// The interface for who provides logger 8 | /// 9 | public interface ILoggerProvider 10 | { 11 | /// 12 | /// Gets the logger assosiated with this object. 13 | /// 14 | ILog Logger { get; } 15 | } 16 | 17 | public interface IAsyncSocketSessionBase : ILoggerProvider 18 | { 19 | SocketAsyncEventArgsProxy SocketAsyncProxy { get; } 20 | 21 | Socket Client { get; } 22 | } 23 | 24 | public interface IAsyncSocketSession : IAsyncSocketSessionBase 25 | { 26 | void ProcessReceive(SocketAsyncEventArgs e); 27 | } 28 | 29 | /// 30 | /// Log interface 31 | /// 32 | public interface ILog 33 | { 34 | /// 35 | /// Gets a value indicating whether this instance is debug enabled. 36 | /// 37 | /// 38 | /// true if this instance is debug enabled; otherwise, false. 39 | /// 40 | bool IsDebugEnabled { get; } 41 | 42 | /// 43 | /// Gets a value indicating whether this instance is error enabled. 44 | /// 45 | /// 46 | /// true if this instance is error enabled; otherwise, false. 47 | /// 48 | bool IsErrorEnabled { get; } 49 | 50 | /// 51 | /// Gets a value indicating whether this instance is fatal enabled. 52 | /// 53 | /// 54 | /// true if this instance is fatal enabled; otherwise, false. 55 | /// 56 | bool IsFatalEnabled { get; } 57 | 58 | /// 59 | /// Gets a value indicating whether this instance is info enabled. 60 | /// 61 | /// 62 | /// true if this instance is info enabled; otherwise, false. 63 | /// 64 | bool IsInfoEnabled { get; } 65 | 66 | /// 67 | /// Gets a value indicating whether this instance is warn enabled. 68 | /// 69 | /// 70 | /// true if this instance is warn enabled; otherwise, false. 71 | /// 72 | bool IsWarnEnabled { get; } 73 | 74 | /// 75 | /// Logs the debug message. 76 | /// 77 | /// The message. 78 | void Debug(object message); 79 | 80 | /// 81 | /// Logs the debug message. 82 | /// 83 | /// The message. 84 | /// The exception. 85 | void Debug(object message, Exception exception); 86 | 87 | /// 88 | /// Logs the debug message. 89 | /// 90 | /// The format. 91 | /// The arg0. 92 | void DebugFormat(string format, object arg0); 93 | 94 | /// 95 | /// Logs the debug message. 96 | /// 97 | /// The format. 98 | /// The args. 99 | void DebugFormat(string format, params object[] args); 100 | 101 | /// 102 | /// Logs the debug message. 103 | /// 104 | /// The provider. 105 | /// The format. 106 | /// The args. 107 | void DebugFormat(IFormatProvider provider, string format, params object[] args); 108 | 109 | /// 110 | /// Logs the debug message. 111 | /// 112 | /// The format. 113 | /// The arg0. 114 | /// The arg1. 115 | void DebugFormat(string format, object arg0, object arg1); 116 | 117 | /// 118 | /// Logs the debug message. 119 | /// 120 | /// The format. 121 | /// The arg0. 122 | /// The arg1. 123 | /// The arg2. 124 | void DebugFormat(string format, object arg0, object arg1, object arg2); 125 | 126 | /// 127 | /// Logs the error message. 128 | /// 129 | /// The message. 130 | void Error(object message); 131 | 132 | /// 133 | /// Logs the error message. 134 | /// 135 | /// The message. 136 | /// The exception. 137 | void Error(object message, Exception exception); 138 | 139 | /// 140 | /// Logs the error message. 141 | /// 142 | /// The format. 143 | /// The arg0. 144 | void ErrorFormat(string format, object arg0); 145 | 146 | /// 147 | /// Logs the error message. 148 | /// 149 | /// The format. 150 | /// The args. 151 | void ErrorFormat(string format, params object[] args); 152 | 153 | /// 154 | /// Logs the error message. 155 | /// 156 | /// The provider. 157 | /// The format. 158 | /// The args. 159 | void ErrorFormat(IFormatProvider provider, string format, params object[] args); 160 | 161 | /// 162 | /// Logs the error message. 163 | /// 164 | /// The format. 165 | /// The arg0. 166 | /// The arg1. 167 | void ErrorFormat(string format, object arg0, object arg1); 168 | 169 | /// 170 | /// Logs the error message. 171 | /// 172 | /// The format. 173 | /// The arg0. 174 | /// The arg1. 175 | /// The arg2. 176 | void ErrorFormat(string format, object arg0, object arg1, object arg2); 177 | 178 | /// 179 | /// Logs the fatal error message. 180 | /// 181 | /// The message. 182 | void Fatal(object message); 183 | 184 | /// 185 | /// Logs the fatal error message. 186 | /// 187 | /// The message. 188 | /// The exception. 189 | void Fatal(object message, Exception exception); 190 | 191 | /// 192 | /// Logs the fatal error message. 193 | /// 194 | /// The format. 195 | /// The arg0. 196 | void FatalFormat(string format, object arg0); 197 | 198 | /// 199 | /// Logs the fatal error message. 200 | /// 201 | /// The format. 202 | /// The args. 203 | void FatalFormat(string format, params object[] args); 204 | 205 | /// 206 | /// Logs the fatal error message. 207 | /// 208 | /// The provider. 209 | /// The format. 210 | /// The args. 211 | void FatalFormat(IFormatProvider provider, string format, params object[] args); 212 | 213 | /// 214 | /// Logs the fatal error message. 215 | /// 216 | /// The format. 217 | /// The arg0. 218 | /// The arg1. 219 | void FatalFormat(string format, object arg0, object arg1); 220 | 221 | /// 222 | /// Logs the fatal error message. 223 | /// 224 | /// The format. 225 | /// The arg0. 226 | /// The arg1. 227 | /// The arg2. 228 | void FatalFormat(string format, object arg0, object arg1, object arg2); 229 | 230 | /// 231 | /// Logs the info message. 232 | /// 233 | /// The message. 234 | void Info(object message); 235 | 236 | /// 237 | /// Logs the info message. 238 | /// 239 | /// The message. 240 | /// The exception. 241 | void Info(object message, Exception exception); 242 | 243 | /// 244 | /// Logs the info message. 245 | /// 246 | /// The format. 247 | /// The arg0. 248 | void InfoFormat(string format, object arg0); 249 | 250 | /// 251 | /// Logs the info message. 252 | /// 253 | /// The format. 254 | /// The args. 255 | void InfoFormat(string format, params object[] args); 256 | 257 | /// 258 | /// Logs the info message. 259 | /// 260 | /// The provider. 261 | /// The format. 262 | /// The args. 263 | void InfoFormat(IFormatProvider provider, string format, params object[] args); 264 | 265 | /// 266 | /// Logs the info message. 267 | /// 268 | /// The format. 269 | /// The arg0. 270 | /// The arg1. 271 | void InfoFormat(string format, object arg0, object arg1); 272 | 273 | /// 274 | /// Logs the info message. 275 | /// 276 | /// The format. 277 | /// The arg0. 278 | /// The arg1. 279 | /// The arg2. 280 | void InfoFormat(string format, object arg0, object arg1, object arg2); 281 | 282 | /// 283 | /// Logs the warning message. 284 | /// 285 | /// The message. 286 | void Warn(object message); 287 | 288 | /// 289 | /// Logs the warning message. 290 | /// 291 | /// The message. 292 | /// The exception. 293 | void Warn(object message, Exception exception); 294 | 295 | /// 296 | /// Logs the warning message. 297 | /// 298 | /// The format. 299 | /// The arg0. 300 | void WarnFormat(string format, object arg0); 301 | 302 | /// 303 | /// Logs the warning message. 304 | /// 305 | /// The format. 306 | /// The args. 307 | void WarnFormat(string format, params object[] args); 308 | 309 | /// 310 | /// Logs the warning message. 311 | /// 312 | /// The provider. 313 | /// The format. 314 | /// The args. 315 | void WarnFormat(IFormatProvider provider, string format, params object[] args); 316 | 317 | /// 318 | /// Logs the warning message. 319 | /// 320 | /// The format. 321 | /// The arg0. 322 | /// The arg1. 323 | void WarnFormat(string format, object arg0, object arg1); 324 | 325 | /// 326 | /// Logs the warning message. 327 | /// 328 | /// The format. 329 | /// The arg0. 330 | /// The arg1. 331 | /// The arg2. 332 | void WarnFormat(string format, object arg0, object arg1, object arg2); 333 | } 334 | } -------------------------------------------------------------------------------- /SSCore/SSCore.Common/SendingQueue.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | using System.Threading; 5 | 6 | namespace SSCore.Common 7 | { 8 | /// 9 | /// SendingQueue 10 | /// 11 | public sealed class SendingQueue : IList> 12 | { 13 | private readonly int m_Offset; 14 | 15 | private readonly int m_Capacity; 16 | 17 | private int m_CurrentCount = 0; 18 | 19 | private ArraySegment[] m_GlobalQueue; 20 | 21 | private static ArraySegment m_Null = default(ArraySegment); 22 | 23 | private int m_UpdatingCount; 24 | 25 | private bool m_ReadOnly = false; 26 | 27 | private ushort m_TrackID = 1; 28 | 29 | private int m_InnerOffset = 0; 30 | 31 | /// 32 | /// Gets the track ID. 33 | /// 34 | /// 35 | /// The track ID. 36 | /// 37 | public ushort TrackID 38 | { 39 | get { return m_TrackID; } 40 | } 41 | 42 | /// 43 | /// Initializes a new instance of the class. 44 | /// 45 | /// The global queue. 46 | /// The offset. 47 | /// The capacity. 48 | public SendingQueue(ArraySegment[] globalQueue, int offset, int capacity) 49 | { 50 | m_GlobalQueue = globalQueue; 51 | m_Offset = offset; 52 | m_Capacity = capacity; 53 | } 54 | 55 | private bool TryEnqueue(ArraySegment item, out bool conflict, ushort trackID) 56 | { 57 | conflict = false; 58 | 59 | var oldCount = m_CurrentCount; 60 | 61 | if (oldCount >= Capacity) 62 | return false; 63 | 64 | if (m_ReadOnly) 65 | return false; 66 | 67 | if (trackID != m_TrackID) 68 | return false; 69 | 70 | int compareCount = Interlocked.CompareExchange(ref m_CurrentCount, oldCount + 1, oldCount); 71 | 72 | //conflicts 73 | if (compareCount != oldCount) 74 | { 75 | conflict = true; 76 | return false; 77 | } 78 | 79 | m_GlobalQueue[m_Offset + oldCount] = item; 80 | 81 | return true; 82 | } 83 | 84 | /// 85 | /// Enqueues the specified item. 86 | /// 87 | /// The item. 88 | /// The track ID. 89 | /// 90 | public bool Enqueue(ArraySegment item, ushort trackID) 91 | { 92 | if (m_ReadOnly) 93 | return false; 94 | 95 | Interlocked.Increment(ref m_UpdatingCount); 96 | 97 | while (!m_ReadOnly) 98 | { 99 | bool conflict = false; 100 | 101 | if (TryEnqueue(item, out conflict, trackID)) 102 | { 103 | Interlocked.Decrement(ref m_UpdatingCount); 104 | return true; 105 | } 106 | 107 | //Needn't retry 108 | if (!conflict) 109 | break; 110 | } 111 | 112 | Interlocked.Decrement(ref m_UpdatingCount); 113 | return false; 114 | } 115 | 116 | /// 117 | /// Enqueues the specified items. 118 | /// 119 | /// The items. 120 | /// The track ID. 121 | /// 122 | public bool Enqueue(IList> items, ushort trackID) 123 | { 124 | if (m_ReadOnly) 125 | return false; 126 | 127 | Interlocked.Increment(ref m_UpdatingCount); 128 | 129 | bool conflict; 130 | 131 | while (!m_ReadOnly) 132 | { 133 | if (TryEnqueue(items, out conflict, trackID)) 134 | { 135 | Interlocked.Decrement(ref m_UpdatingCount); 136 | return true; 137 | } 138 | 139 | if (!conflict) 140 | break; 141 | } 142 | 143 | Interlocked.Decrement(ref m_UpdatingCount); 144 | return false; 145 | } 146 | 147 | private bool TryEnqueue(IList> items, out bool conflict, ushort trackID) 148 | { 149 | conflict = false; 150 | 151 | var oldCount = m_CurrentCount; 152 | 153 | int newItemCount = items.Count; 154 | int expectedCount = oldCount + newItemCount; 155 | 156 | if (expectedCount > Capacity) 157 | return false; 158 | 159 | if (m_ReadOnly) 160 | return false; 161 | 162 | if (m_TrackID != trackID) 163 | return false; 164 | 165 | int compareCount = Interlocked.CompareExchange(ref m_CurrentCount, expectedCount, oldCount); 166 | 167 | if (compareCount != oldCount) 168 | { 169 | conflict = true; 170 | return false; 171 | } 172 | 173 | var queue = m_GlobalQueue; 174 | 175 | for (var i = 0; i < items.Count; i++) 176 | { 177 | queue[m_Offset + oldCount + i] = items[i]; 178 | } 179 | 180 | return true; 181 | } 182 | 183 | /// 184 | /// Stops the enqueue, and then wait all current excueting enqueu threads exit. 185 | /// 186 | public void StopEnqueue() 187 | { 188 | if (m_ReadOnly) 189 | return; 190 | 191 | m_ReadOnly = true; 192 | 193 | if (m_UpdatingCount <= 0) 194 | return; 195 | 196 | var spinWait = new SpinWait(); 197 | 198 | spinWait.SpinOnce(); 199 | 200 | //Wait until all insertings are finished 201 | while (m_UpdatingCount > 0) 202 | { 203 | spinWait.SpinOnce(); 204 | } 205 | } 206 | 207 | /// 208 | /// Starts to allow enqueue. 209 | /// 210 | public void StartEnqueue() 211 | { 212 | m_ReadOnly = false; 213 | } 214 | 215 | /// 216 | /// Gets the global queue. 217 | /// 218 | /// 219 | /// The global queue. 220 | /// 221 | public ArraySegment[] GlobalQueue 222 | { 223 | get { return m_GlobalQueue; } 224 | } 225 | 226 | /// 227 | /// Gets the offset. 228 | /// 229 | /// 230 | /// The offset. 231 | /// 232 | public int Offset 233 | { 234 | get { return m_Offset; } 235 | } 236 | 237 | /// 238 | /// Gets the capacity. 239 | /// 240 | /// 241 | /// The capacity. 242 | /// 243 | public int Capacity 244 | { 245 | get { return m_Capacity; } 246 | } 247 | 248 | /// 249 | /// Gets the number of elements contained in the . 250 | /// 251 | /// The number of elements contained in the . 252 | public int Count 253 | { 254 | get { return m_CurrentCount - m_InnerOffset; } 255 | } 256 | 257 | /// 258 | /// Gets or sets the position. 259 | /// 260 | /// 261 | /// The position. 262 | /// 263 | public int Position { get; set; } 264 | 265 | /// 266 | /// Determines the index of a specific item in the . 267 | /// 268 | /// The object to locate in the . 269 | /// 270 | /// The index of if found in the list; otherwise, -1. 271 | /// 272 | /// 273 | public int IndexOf(ArraySegment item) 274 | { 275 | throw new NotSupportedException(); 276 | } 277 | 278 | /// 279 | /// Inserts an item to the at the specified index. 280 | /// 281 | /// The zero-based index at which should be inserted. 282 | /// The object to insert into the . 283 | /// 284 | public void Insert(int index, ArraySegment item) 285 | { 286 | throw new NotSupportedException(); 287 | } 288 | 289 | /// 290 | /// Removes the item at the specified index. 291 | /// 292 | /// The zero-based index of the item to remove. 293 | /// 294 | public void RemoveAt(int index) 295 | { 296 | throw new NotSupportedException(); 297 | } 298 | 299 | /// 300 | /// Gets or sets the element at the specified index. 301 | /// 302 | /// The index. 303 | /// 304 | /// 305 | public ArraySegment this[int index] 306 | { 307 | get 308 | { 309 | var targetIndex = m_Offset + m_InnerOffset + index; 310 | var value = m_GlobalQueue[targetIndex]; 311 | 312 | if (value.Array != null) 313 | return value; 314 | 315 | var spinWait = new SpinWait(); 316 | 317 | while (true) 318 | { 319 | spinWait.SpinOnce(); 320 | value = m_GlobalQueue[targetIndex]; 321 | 322 | if (value.Array != null) 323 | return value; 324 | 325 | if (spinWait.Count > 50) 326 | return value; 327 | } 328 | } 329 | set 330 | { 331 | throw new NotSupportedException(); 332 | } 333 | } 334 | 335 | /// 336 | /// Adds an item to the . 337 | /// 338 | /// The object to add to the . 339 | /// 340 | public void Add(ArraySegment item) 341 | { 342 | throw new NotSupportedException(); 343 | } 344 | 345 | /// 346 | /// Removes all items from the . 347 | /// 348 | /// 349 | public void Clear() 350 | { 351 | if (m_TrackID >= ushort.MaxValue) 352 | m_TrackID = 1; 353 | else 354 | m_TrackID++; 355 | 356 | for (var i = 0; i < m_CurrentCount; i++) 357 | { 358 | m_GlobalQueue[m_Offset + i] = m_Null; 359 | } 360 | 361 | m_CurrentCount = 0; 362 | m_InnerOffset = 0; 363 | Position = 0; 364 | } 365 | 366 | /// 367 | /// Determines whether the contains a specific value. 368 | /// 369 | /// The object to locate in the . 370 | /// 371 | /// true if is found in the ; otherwise, false. 372 | /// 373 | /// 374 | public bool Contains(ArraySegment item) 375 | { 376 | throw new NotSupportedException(); 377 | } 378 | 379 | /// 380 | /// Copies to. 381 | /// 382 | /// The array. 383 | /// Index of the array. 384 | public void CopyTo(ArraySegment[] array, int arrayIndex) 385 | { 386 | for (var i = 0; i < Count; i++) 387 | { 388 | array[arrayIndex + i] = this[i]; 389 | } 390 | } 391 | 392 | /// 393 | /// Gets a value indicating whether the is read-only. 394 | /// 395 | /// true if the is read-only; otherwise, false. 396 | public bool IsReadOnly 397 | { 398 | get { return m_ReadOnly; } 399 | } 400 | 401 | /// 402 | /// Removes the first occurrence of a specific object from the . 403 | /// 404 | /// The object to remove from the . 405 | /// 406 | /// true if was successfully removed from the ; otherwise, false. This method also returns false if is not found in the original . 407 | /// 408 | /// 409 | public bool Remove(ArraySegment item) 410 | { 411 | throw new NotSupportedException(); 412 | } 413 | 414 | /// 415 | /// Returns an enumerator that iterates through the collection. 416 | /// 417 | /// 418 | /// A that can be used to iterate through the collection. 419 | /// 420 | /// 421 | public IEnumerator> GetEnumerator() 422 | { 423 | for (var i = 0; i < (m_CurrentCount - m_InnerOffset); i++) 424 | { 425 | yield return m_GlobalQueue[m_Offset + m_InnerOffset + i]; 426 | } 427 | } 428 | 429 | /// 430 | /// Returns an enumerator that iterates through a collection. 431 | /// 432 | /// 433 | /// An object that can be used to iterate through the collection. 434 | /// 435 | /// 436 | System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() 437 | { 438 | return GetEnumerator(); 439 | } 440 | 441 | /// 442 | /// Trim the internal segments at the begining by the binary data size. 443 | /// 444 | /// The binary data size should be trimed at the begining. 445 | public void InternalTrim(int offset) 446 | { 447 | var innerCount = m_CurrentCount - m_InnerOffset; 448 | var subTotal = 0; 449 | 450 | for (var i = m_InnerOffset; i < innerCount; i++) 451 | { 452 | var segment = m_GlobalQueue[m_Offset + i]; 453 | subTotal += segment.Count; 454 | 455 | if (subTotal <= offset) 456 | continue; 457 | 458 | m_InnerOffset = i; 459 | 460 | var rest = subTotal - offset; 461 | m_GlobalQueue[m_Offset + i] = new ArraySegment(segment.Array, segment.Offset + segment.Count - rest, rest); 462 | 463 | break; 464 | } 465 | } 466 | } 467 | 468 | /// 469 | /// SendingQueueSourceCreator 470 | /// 471 | public class SendingQueueSourceCreator : ISmartPoolSourceCreator 472 | { 473 | private int m_SendingQueueSize; 474 | 475 | /// 476 | /// Initializes a new instance of the class. 477 | /// 478 | /// Size of the sending queue. 479 | public SendingQueueSourceCreator(int sendingQueueSize) 480 | { 481 | m_SendingQueueSize = sendingQueueSize; 482 | } 483 | 484 | /// 485 | /// Creates the specified size. 486 | /// 487 | /// The size. 488 | /// The pool items. 489 | /// 490 | public ISmartPoolSource Create(int size, out SendingQueue[] poolItems) 491 | { 492 | var source = new ArraySegment[size * m_SendingQueueSize]; 493 | 494 | poolItems = new SendingQueue[size]; 495 | 496 | for (var i = 0; i < size; i++) 497 | { 498 | poolItems[i] = new SendingQueue(source, i * m_SendingQueueSize, m_SendingQueueSize); 499 | } 500 | 501 | return new SmartPoolSource(source, size); 502 | } 503 | } 504 | } 505 | -------------------------------------------------------------------------------- /SSCore/SSCore/SocketSession.cs: -------------------------------------------------------------------------------- 1 | using SSCore.Common; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.IO; 5 | using System.Net; 6 | using System.Net.Sockets; 7 | using System.Security.Authentication; 8 | using System.Threading; 9 | 10 | namespace SSCore 11 | { 12 | static class SocketState 13 | { 14 | public const int Normal = 0;//0000 0000 15 | public const int InClosing = 16;//0001 0000 >= 16 16 | public const int Closed = 16777216;//256 * 256 * 256; 0x01 0x00 0x00 0x00 17 | public const int InSending = 1;//0000 0001 > 1 18 | public const int InReceiving = 2;//0000 0010 > 2 19 | public const int InSendingReceivingMask = -4;// ~(InSending | InReceiving); 0xf0 0xff 0xff 0xff 20 | } 21 | 22 | /// 23 | /// Socket Session, all application session should base on this class 24 | /// 25 | public abstract partial class SocketSession : ISocketSession 26 | { 27 | public IAppSession AppSession { get; private set; } 28 | 29 | protected readonly object SyncRoot = new object(); 30 | 31 | //0x00 0x00 0x00 0x00 32 | //1st byte: Closed(Y/N) - 0x01 33 | //2nd byte: N/A 34 | //3th byte: CloseReason 35 | //Last byte: 0000 0000 - normal state 36 | //0000 0001: in sending 37 | //0000 0010: in receiving 38 | //0001 0000: in closing 39 | private int m_State = 0; 40 | 41 | private void AddStateFlag(int stateValue) 42 | { 43 | AddStateFlag(stateValue, false); 44 | } 45 | 46 | private bool AddStateFlag(int stateValue, bool notClosing) 47 | { 48 | while (true) 49 | { 50 | var oldState = m_State; 51 | 52 | if (notClosing) 53 | { 54 | // don't update the state if the connection has entered the closing procedure 55 | if (oldState >= SocketState.InClosing) 56 | { 57 | return false; 58 | } 59 | } 60 | 61 | var newState = m_State | stateValue; 62 | 63 | if (Interlocked.CompareExchange(ref m_State, newState, oldState) == oldState) 64 | return true; 65 | } 66 | } 67 | 68 | private bool TryAddStateFlag(int stateValue) 69 | { 70 | while (true) 71 | { 72 | var oldState = m_State; 73 | var newState = m_State | stateValue; 74 | 75 | //Already marked 76 | if (oldState == newState) 77 | { 78 | return false; 79 | } 80 | 81 | var compareState = Interlocked.CompareExchange(ref m_State, newState, oldState); 82 | 83 | if (compareState == oldState) 84 | return true; 85 | } 86 | } 87 | 88 | private void RemoveStateFlag(int stateValue) 89 | { 90 | while (true) 91 | { 92 | var oldState = m_State; 93 | var newState = m_State & (~stateValue); 94 | 95 | if (Interlocked.CompareExchange(ref m_State, newState, oldState) == oldState) 96 | return; 97 | } 98 | } 99 | 100 | private bool CheckState(int stateValue) 101 | { 102 | return (m_State & stateValue) == stateValue; 103 | } 104 | 105 | protected bool SyncSend { get; private set; } 106 | 107 | private ISmartPool m_SendingQueuePool; 108 | 109 | public SocketSession(Socket client) 110 | : this(Guid.NewGuid().ToString()) 111 | { 112 | m_Client = client ?? throw new ArgumentNullException("client"); 113 | 114 | LocalEndPoint = (IPEndPoint)client.LocalEndPoint; 115 | RemoteEndPoint = (IPEndPoint)client.RemoteEndPoint; 116 | } 117 | 118 | public SocketSession(string sessionID) 119 | { 120 | SessionID = sessionID; 121 | } 122 | 123 | public virtual void Initialize(IAppSession appSession) 124 | { 125 | AppSession = appSession; 126 | 127 | SendingQueue queue; 128 | if (m_SendingQueuePool.TryGet(out queue)) 129 | { 130 | m_SendingQueue = queue; 131 | queue.StartEnqueue(); 132 | } 133 | //Config = appSession.Config; 134 | //SyncSend = Config.SyncSend; 135 | 136 | //if (m_SendingQueuePool == null) 137 | // m_SendingQueuePool = ((SocketServerBase)((ISocketServerAccessor)appSession.AppServer).SocketServer).SendingQueuePool; 138 | 139 | //SendingQueue queue; 140 | //if (m_SendingQueuePool.TryGet(out queue)) 141 | //{ 142 | // m_SendingQueue = queue; 143 | // queue.StartEnqueue(); 144 | //} 145 | } 146 | 147 | public void InitializeSendingQueue(ISmartPool pool) 148 | { 149 | m_SendingQueuePool = pool; 150 | } 151 | 152 | /// 153 | /// Gets or sets the session ID. 154 | /// 155 | /// The session ID. 156 | public string SessionID { get; private set; } 157 | 158 | 159 | /// 160 | /// Gets or sets the config. 161 | /// 162 | /// 163 | /// The config. 164 | /// 165 | //public IServerConfig Config { get; set; } 166 | 167 | /// 168 | /// Starts this session. 169 | /// 170 | public abstract void Start(); 171 | 172 | /// 173 | /// Says the welcome information when a client connectted. 174 | /// 175 | protected virtual void StartSession() 176 | { 177 | AppSession.StartSession(); 178 | } 179 | 180 | /// 181 | /// Called when [close]. 182 | /// 183 | protected virtual void OnClosed(CloseReason reason) 184 | { 185 | //Already closed 186 | if (!TryAddStateFlag(SocketState.Closed)) 187 | return; 188 | 189 | //Before changing m_SendingQueue, must check m_IsClosed 190 | while (true) 191 | { 192 | var sendingQueue = m_SendingQueue; 193 | 194 | if (sendingQueue == null) 195 | break; 196 | 197 | //There is no sending was started after the m_Closed ws set to 'true' 198 | if (Interlocked.CompareExchange(ref m_SendingQueue, null, sendingQueue) == sendingQueue) 199 | { 200 | sendingQueue.Clear(); 201 | m_SendingQueuePool.Push(sendingQueue); 202 | break; 203 | } 204 | } 205 | 206 | var closedHandler = Closed; 207 | if (closedHandler != null) 208 | { 209 | closedHandler(this, reason); 210 | } 211 | } 212 | 213 | /// 214 | /// Occurs when [closed]. 215 | /// 216 | public Action Closed { get; set; } 217 | 218 | private SendingQueue m_SendingQueue; 219 | 220 | /// 221 | /// Tries to send array segment. 222 | /// 223 | /// The segments. 224 | /// 225 | public bool TrySend(IList> segments) 226 | { 227 | if (IsClosed) 228 | return false; 229 | 230 | var queue = m_SendingQueue; 231 | 232 | if (queue == null) 233 | return false; 234 | 235 | var trackID = queue.TrackID; 236 | 237 | if (!queue.Enqueue(segments, trackID)) 238 | return false; 239 | 240 | StartSend(queue, trackID, true); 241 | return true; 242 | } 243 | 244 | /// 245 | /// Tries to send array segment. 246 | /// 247 | /// The segment. 248 | /// 249 | public bool TrySend(ArraySegment segment) 250 | { 251 | if (IsClosed) 252 | return false; 253 | 254 | var queue = m_SendingQueue; 255 | 256 | if (queue == null) 257 | return false; 258 | 259 | var trackID = queue.TrackID; 260 | 261 | if (!queue.Enqueue(segment, trackID)) 262 | return false; 263 | 264 | StartSend(queue, trackID, true); 265 | return true; 266 | } 267 | 268 | /// 269 | /// Sends in async mode. 270 | /// 271 | /// The queue. 272 | protected abstract void SendAsync(SendingQueue queue); 273 | 274 | /// 275 | /// Sends in sync mode. 276 | /// 277 | /// The queue. 278 | protected abstract void SendSync(SendingQueue queue); 279 | 280 | private void Send(SendingQueue queue) 281 | { 282 | if (SyncSend) 283 | { 284 | SendSync(queue); 285 | } 286 | else 287 | { 288 | SendAsync(queue); 289 | } 290 | } 291 | 292 | private void StartSend(SendingQueue queue, int sendingTrackID, bool initial) 293 | { 294 | if (initial) 295 | { 296 | if (!TryAddStateFlag(SocketState.InSending)) 297 | { 298 | return; 299 | } 300 | 301 | var currentQueue = m_SendingQueue; 302 | 303 | if (currentQueue != queue || sendingTrackID != currentQueue.TrackID) 304 | { 305 | //Has been sent 306 | OnSendEnd(); 307 | return; 308 | } 309 | } 310 | 311 | Socket client; 312 | 313 | if (IsInClosingOrClosed && TryValidateClosedBySocket(out client)) 314 | { 315 | OnSendEnd(); 316 | return; 317 | } 318 | 319 | SendingQueue newQueue; 320 | 321 | if (!m_SendingQueuePool.TryGet(out newQueue)) 322 | { 323 | OnSendEnd(CloseReason.InternalError, true); 324 | AppSession.Logger.Error("There is no enougth sending queue can be used."); 325 | return; 326 | } 327 | 328 | var oldQueue = Interlocked.CompareExchange(ref m_SendingQueue, newQueue, queue); 329 | 330 | if (!ReferenceEquals(oldQueue, queue)) 331 | { 332 | if (newQueue != null) 333 | m_SendingQueuePool.Push(newQueue); 334 | 335 | if (IsInClosingOrClosed) 336 | { 337 | OnSendEnd(); 338 | } 339 | else 340 | { 341 | OnSendEnd(CloseReason.InternalError, true); 342 | AppSession.Logger.Error("Failed to switch the sending queue."); 343 | } 344 | 345 | return; 346 | } 347 | 348 | //Start to allow enqueue 349 | newQueue.StartEnqueue(); 350 | queue.StopEnqueue(); 351 | 352 | if (queue.Count == 0) 353 | { 354 | 355 | m_SendingQueuePool.Push(queue); 356 | OnSendEnd(CloseReason.InternalError, true); 357 | AppSession.Logger.Error("There is no data to be sent in the queue."); 358 | return; 359 | } 360 | 361 | Send(queue); 362 | } 363 | 364 | private void OnSendEnd() 365 | { 366 | OnSendEnd(CloseReason.Unknown, false); 367 | } 368 | 369 | private void OnSendEnd(CloseReason closeReason, bool forceClose) 370 | { 371 | RemoveStateFlag(SocketState.InSending); 372 | ValidateClosed(closeReason, forceClose, true); 373 | } 374 | 375 | protected virtual void OnSendingCompleted(SendingQueue queue) 376 | { 377 | queue.Clear(); 378 | m_SendingQueuePool.Push(queue); 379 | 380 | var newQueue = m_SendingQueue; 381 | 382 | if (IsInClosingOrClosed) 383 | { 384 | Socket client; 385 | 386 | //has data is being sent and the socket isn't closed 387 | if (newQueue.Count > 0 && !TryValidateClosedBySocket(out client)) 388 | { 389 | StartSend(newQueue, newQueue.TrackID, false); 390 | return; 391 | } 392 | 393 | OnSendEnd(); 394 | return; 395 | } 396 | 397 | if (newQueue.Count == 0) 398 | { 399 | OnSendEnd(); 400 | 401 | if (newQueue.Count > 0) 402 | { 403 | StartSend(newQueue, newQueue.TrackID, true); 404 | } 405 | } 406 | else 407 | { 408 | StartSend(newQueue, newQueue.TrackID, false); 409 | } 410 | } 411 | 412 | public abstract void ApplySecureProtocol(); 413 | 414 | public Stream GetUnderlyStream() 415 | { 416 | return new NetworkStream(Client); 417 | } 418 | 419 | private Socket m_Client; 420 | /// 421 | /// Gets or sets the client. 422 | /// 423 | /// The client. 424 | public Socket Client 425 | { 426 | get { return m_Client; } 427 | } 428 | 429 | protected bool IsInClosingOrClosed 430 | { 431 | get { return m_State >= SocketState.InClosing; } 432 | } 433 | 434 | protected bool IsClosed 435 | { 436 | get { return m_State >= SocketState.Closed; } 437 | } 438 | 439 | /// 440 | /// Gets the local end point. 441 | /// 442 | /// The local end point. 443 | public virtual IPEndPoint LocalEndPoint { get; protected set; } 444 | 445 | /// 446 | /// Gets the remote end point. 447 | /// 448 | /// The remote end point. 449 | public virtual IPEndPoint RemoteEndPoint { get; protected set; } 450 | 451 | /// 452 | /// Gets or sets the secure protocol. 453 | /// 454 | /// The secure protocol. 455 | public SslProtocols SecureProtocol { get; set; } 456 | 457 | protected virtual bool TryValidateClosedBySocket(out Socket socket) 458 | { 459 | socket = m_Client; 460 | //Already closed/closing 461 | return socket == null; 462 | } 463 | 464 | public virtual void Close(CloseReason reason) 465 | { 466 | //Already in closing procedure 467 | if (!TryAddStateFlag(SocketState.InClosing)) 468 | return; 469 | 470 | Socket client; 471 | 472 | //No need to clean the socket instance 473 | if (TryValidateClosedBySocket(out client)) 474 | return; 475 | 476 | //Some data is in sending 477 | if (CheckState(SocketState.InSending)) 478 | { 479 | //Set closing reason only, don't close the socket directly 480 | AddStateFlag(GetCloseReasonValue(reason)); 481 | return; 482 | } 483 | 484 | // In the udp mode, we needn't close the socket instance 485 | if (client != null) 486 | InternalClose(client, reason, true); 487 | else //In Udp mode, and the socket is not in the sending state, then fire the closed event directly 488 | OnClosed(reason); 489 | } 490 | 491 | private void InternalClose(Socket client, CloseReason reason, bool setCloseReason) 492 | { 493 | if (Interlocked.CompareExchange(ref m_Client, null, client) == client) 494 | { 495 | if (setCloseReason) 496 | AddStateFlag(GetCloseReasonValue(reason)); 497 | 498 | client.Shutdown(SocketShutdown.Both); 499 | 500 | if (ValidateNotInSendingReceiving()) 501 | { 502 | OnClosed(reason); 503 | } 504 | } 505 | } 506 | 507 | protected void OnSendError(SendingQueue queue, CloseReason closeReason) 508 | { 509 | queue.Clear(); 510 | m_SendingQueuePool.Push(queue); 511 | OnSendEnd(closeReason, true); 512 | } 513 | 514 | // the receive action won't be started for this connection any more 515 | protected void OnReceiveTerminated(CloseReason closeReason) 516 | { 517 | OnReceiveEnded(); 518 | ValidateClosed(closeReason, true); 519 | } 520 | 521 | 522 | // return false if the connection has entered the closing procedure or has closed already 523 | protected bool OnReceiveStarted() 524 | { 525 | if (AddStateFlag(SocketState.InReceiving, true)) 526 | return true; 527 | 528 | // the connection is in closing 529 | ValidateClosed(CloseReason.Unknown, false); 530 | return false; 531 | } 532 | 533 | protected void OnReceiveEnded() 534 | { 535 | RemoveStateFlag(SocketState.InReceiving); 536 | } 537 | 538 | /// 539 | /// Validates the socket is not in the sending or receiving operation. 540 | /// 541 | /// 542 | private bool ValidateNotInSendingReceiving() 543 | { 544 | var oldState = m_State; 545 | 546 | if ((oldState & SocketState.InSendingReceivingMask) == oldState) 547 | { 548 | return true; 549 | } 550 | 551 | return false; 552 | } 553 | 554 | private const int m_CloseReasonMagic = 256; 555 | 556 | private int GetCloseReasonValue(CloseReason reason) 557 | { 558 | return ((int)reason + 1) * m_CloseReasonMagic; 559 | } 560 | 561 | private CloseReason GetCloseReasonFromState() 562 | { 563 | return (CloseReason)(m_State / m_CloseReasonMagic - 1); 564 | } 565 | 566 | private void FireCloseEvent() 567 | { 568 | OnClosed(GetCloseReasonFromState()); 569 | } 570 | 571 | private void ValidateClosed() 572 | { 573 | // CloseReason.Unknown won't be used 574 | ValidateClosed(CloseReason.Unknown, false); 575 | } 576 | 577 | private void ValidateClosed(CloseReason closeReason, bool forceClose) 578 | { 579 | ValidateClosed(closeReason, forceClose, false); 580 | } 581 | 582 | private void ValidateClosed(CloseReason closeReason, bool forceClose, bool forSend) 583 | { 584 | lock (this) 585 | { 586 | if (IsClosed) 587 | return; 588 | 589 | if (CheckState(SocketState.InClosing)) 590 | { 591 | // we only keep socket instance after InClosing state when the it is sending 592 | // so we check if the socket instance is alive now 593 | if (forSend) 594 | { 595 | Socket client; 596 | 597 | if (!TryValidateClosedBySocket(out client)) 598 | { 599 | var sendingQueue = m_SendingQueue; 600 | // No data to be sent 601 | if (forceClose || (sendingQueue != null && sendingQueue.Count == 0)) 602 | { 603 | if (client != null)// the socket instance is not closed yet, do it now 604 | InternalClose(client, GetCloseReasonFromState(), false); 605 | else// The UDP mode, the socket instance always is null, fire the closed event directly 606 | FireCloseEvent(); 607 | 608 | return; 609 | } 610 | 611 | return; 612 | } 613 | } 614 | 615 | if (ValidateNotInSendingReceiving()) 616 | { 617 | FireCloseEvent(); 618 | } 619 | } 620 | else if (forceClose) 621 | { 622 | Close(closeReason); 623 | } 624 | } 625 | } 626 | 627 | public abstract int OrigReceiveOffset { get; } 628 | 629 | protected virtual bool IsIgnorableSocketError(int socketErrorCode) 630 | { 631 | if (socketErrorCode == 10004 //Interrupted 632 | || socketErrorCode == 10053 //ConnectionAborted 633 | || socketErrorCode == 10054 //ConnectionReset 634 | || socketErrorCode == 10058 //Shutdown 635 | || socketErrorCode == 10060 //TimedOut 636 | || socketErrorCode == 995 //OperationAborted 637 | || socketErrorCode == -1073741299) 638 | { 639 | return true; 640 | } 641 | 642 | return false; 643 | } 644 | 645 | protected virtual bool IsIgnorableException(Exception e, out int socketErrorCode) 646 | { 647 | socketErrorCode = 0; 648 | 649 | if (e is ObjectDisposedException || e is NullReferenceException) 650 | return true; 651 | 652 | SocketException socketException = null; 653 | 654 | if (e is IOException) 655 | { 656 | if (e.InnerException is ObjectDisposedException || e.InnerException is NullReferenceException) 657 | return true; 658 | 659 | socketException = e.InnerException as SocketException; 660 | } 661 | else 662 | { 663 | socketException = e as SocketException; 664 | } 665 | 666 | if (socketException == null) 667 | return false; 668 | 669 | //socketErrorCode = socketException.ErrorCode; 670 | 671 | //if (Config.LogAllSocketException) 672 | // return false; 673 | 674 | return IsIgnorableSocketError(socketErrorCode); 675 | } 676 | } 677 | } -------------------------------------------------------------------------------- /SSCore/SSCore/AppSession.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Net; 4 | using System.Security.Authentication; 5 | using System.Text; 6 | using System.Threading; 7 | 8 | namespace SSCore 9 | { 10 | /// 11 | /// AppSession base class 12 | /// 13 | /// The type of the app session. 14 | /// The type of the request info. 15 | public abstract class AppSession : IAppSession, IAppSession 16 | where TAppSession : AppSession, IAppSession, new() 17 | where TRequestInfo : class, IRequestInfo 18 | { 19 | #region Properties 20 | 21 | /// 22 | /// Gets the app server instance assosiated with the session. 23 | /// 24 | public virtual AppServerBase AppServer { get; private set; } 25 | 26 | /// 27 | /// Gets the app server instance assosiated with the session. 28 | /// 29 | IAppServer IAppSession.AppServer 30 | { 31 | get { return this.AppServer; } 32 | } 33 | 34 | /// 35 | /// Gets or sets the charset which is used for transfering text message. 36 | /// 37 | /// 38 | /// The charset. 39 | /// 40 | public Encoding Charset { get; set; } 41 | 42 | private IDictionary m_Items; 43 | 44 | /// 45 | /// Gets the items dictionary, only support 10 items maximum 46 | /// 47 | public IDictionary Items 48 | { 49 | get 50 | { 51 | if (m_Items == null) 52 | m_Items = new Dictionary(10); 53 | 54 | return m_Items; 55 | } 56 | } 57 | 58 | 59 | private bool m_Connected = false; 60 | 61 | /// 62 | /// Gets a value indicating whether this is connected. 63 | /// 64 | /// 65 | /// true if connected; otherwise, false. 66 | /// 67 | public bool Connected 68 | { 69 | get { return m_Connected; } 70 | internal set { m_Connected = value; } 71 | } 72 | 73 | /// 74 | /// Gets or sets the previous command. 75 | /// 76 | /// 77 | /// The prev command. 78 | /// 79 | public string PrevCommand { get; set; } 80 | 81 | /// 82 | /// Gets or sets the current executing command. 83 | /// 84 | /// 85 | /// The current command. 86 | /// 87 | public string CurrentCommand { get; set; } 88 | 89 | 90 | /// 91 | /// Gets or sets the secure protocol of transportation layer. 92 | /// 93 | /// 94 | /// The secure protocol. 95 | /// 96 | public SslProtocols SecureProtocol 97 | { 98 | get { return SocketSession.SecureProtocol; } 99 | set { SocketSession.SecureProtocol = value; } 100 | } 101 | 102 | /// 103 | /// Gets the local listening endpoint. 104 | /// 105 | public IPEndPoint LocalEndPoint 106 | { 107 | get { return SocketSession.LocalEndPoint; } 108 | } 109 | 110 | /// 111 | /// Gets the remote endpoint of client. 112 | /// 113 | public IPEndPoint RemoteEndPoint 114 | { 115 | get { return SocketSession.RemoteEndPoint; } 116 | } 117 | 118 | /// 119 | /// Gets the logger. 120 | /// 121 | public ILog Logger 122 | { 123 | get { return AppServer.Logger; } 124 | } 125 | 126 | /// 127 | /// Gets or sets the last active time of the session. 128 | /// 129 | /// 130 | /// The last active time. 131 | /// 132 | public DateTime LastActiveTime { get; set; } 133 | 134 | /// 135 | /// Gets the start time of the session. 136 | /// 137 | public DateTime StartTime { get; private set; } 138 | 139 | /// 140 | /// Gets the session ID. 141 | /// 142 | public string SessionID { get; private set; } 143 | 144 | /// 145 | /// Gets the socket session of the AppSession. 146 | /// 147 | public ISocketSession SocketSession { get; private set; } 148 | 149 | /// 150 | /// Gets the config of the server. 151 | /// 152 | //public IServerConfig Config 153 | //{ 154 | // get { return AppServer.Config; } 155 | //} 156 | 157 | //IReceiveFilter m_ReceiveFilter; 158 | 159 | #endregion 160 | 161 | /// 162 | /// Initializes a new instance of the class. 163 | /// 164 | public AppSession() 165 | { 166 | this.StartTime = DateTime.Now; 167 | this.LastActiveTime = this.StartTime; 168 | } 169 | 170 | 171 | /// 172 | /// Initializes the specified app session by AppServer and SocketSession. 173 | /// 174 | /// The app server. 175 | /// The socket session. 176 | public virtual void Initialize(IAppServer appServer, ISocketSession socketSession) 177 | { 178 | var castedAppServer = (AppServerBase)appServer; 179 | AppServer = castedAppServer; 180 | Charset = castedAppServer.TextEncoding; 181 | SocketSession = socketSession; 182 | SessionID = socketSession.SessionID; 183 | m_Connected = true; 184 | //m_ReceiveFilter = castedAppServer.ReceiveFilterFactory.CreateFilter(appServer, this, socketSession.RemoteEndPoint); 185 | 186 | //var filterInitializer = m_ReceiveFilter as IReceiveFilterInitializer; 187 | //if (filterInitializer != null) 188 | // filterInitializer.Initialize(castedAppServer, this); 189 | 190 | socketSession.Initialize(this); 191 | 192 | OnInit(); 193 | } 194 | 195 | /// 196 | /// Starts the session. 197 | /// 198 | void IAppSession.StartSession() 199 | { 200 | OnSessionStarted(); 201 | } 202 | 203 | /// 204 | /// Called when [init]. 205 | /// 206 | protected virtual void OnInit() 207 | { 208 | 209 | } 210 | 211 | /// 212 | /// Called when [session started]. 213 | /// 214 | protected virtual void OnSessionStarted() 215 | { 216 | 217 | } 218 | 219 | /// 220 | /// Called when [session closed]. 221 | /// 222 | /// The reason. 223 | internal protected virtual void OnSessionClosed(CloseReason reason) 224 | { 225 | 226 | } 227 | 228 | 229 | /// 230 | /// Handles the exceptional error, it only handles application error. 231 | /// 232 | /// The exception. 233 | protected virtual void HandleException(Exception e) 234 | { 235 | Logger.Error(this, e); 236 | this.Close(CloseReason.ApplicationError); 237 | } 238 | 239 | /// 240 | /// Handles the unknown request. 241 | /// 242 | /// The request info. 243 | protected virtual void HandleUnknownRequest(TRequestInfo requestInfo) 244 | { 245 | 246 | } 247 | 248 | internal void InternalHandleUnknownRequest(TRequestInfo requestInfo) 249 | { 250 | HandleUnknownRequest(requestInfo); 251 | } 252 | 253 | internal void InternalHandleExcetion(Exception e) 254 | { 255 | HandleException(e); 256 | } 257 | 258 | /// 259 | /// Closes the session by the specified reason. 260 | /// 261 | /// The close reason. 262 | public virtual void Close(CloseReason reason) 263 | { 264 | this.SocketSession.Close(reason); 265 | } 266 | 267 | /// 268 | /// Closes this session. 269 | /// 270 | public virtual void Close() 271 | { 272 | Close(CloseReason.ServerClosing); 273 | } 274 | 275 | #region Sending processing 276 | 277 | /// 278 | /// Try to send the message to client. 279 | /// 280 | /// The message which will be sent. 281 | /// Indicate whether the message was pushed into the sending queue 282 | public virtual bool TrySend(string message) 283 | { 284 | var data = this.Charset.GetBytes(message); 285 | return InternalTrySend(new ArraySegment(data, 0, data.Length)); 286 | } 287 | 288 | /// 289 | /// Sends the message to client. 290 | /// 291 | /// The message which will be sent. 292 | public virtual void Send(string message) 293 | { 294 | var data = this.Charset.GetBytes(message); 295 | Send(data, 0, data.Length); 296 | } 297 | 298 | /// 299 | /// Try to send the data to client. 300 | /// 301 | /// The data which will be sent. 302 | /// The offset. 303 | /// The length. 304 | /// Indicate whether the message was pushed into the sending queue 305 | public virtual bool TrySend(byte[] data, int offset, int length) 306 | { 307 | return InternalTrySend(new ArraySegment(data, offset, length)); 308 | } 309 | 310 | /// 311 | /// Sends the data to client. 312 | /// 313 | /// The data which will be sent. 314 | /// The offset. 315 | /// The length. 316 | public virtual void Send(byte[] data, int offset, int length) 317 | { 318 | InternalSend(new ArraySegment(data, offset, length)); 319 | } 320 | 321 | private bool InternalTrySend(ArraySegment segment) 322 | { 323 | if (!SocketSession.TrySend(segment)) 324 | return false; 325 | 326 | LastActiveTime = DateTime.Now; 327 | return true; 328 | } 329 | 330 | /// 331 | /// Try to send the data segment to client. 332 | /// 333 | /// The segment which will be sent. 334 | /// Indicate whether the message was pushed into the sending queue 335 | public virtual bool TrySend(ArraySegment segment) 336 | { 337 | if (!m_Connected) 338 | return false; 339 | 340 | return InternalTrySend(segment); 341 | } 342 | 343 | 344 | private void InternalSend(ArraySegment segment) 345 | { 346 | if (!m_Connected) 347 | return; 348 | 349 | if (InternalTrySend(segment)) 350 | return; 351 | 352 | var sendTimeOut = 1000;// Config.SendTimeOut; 353 | 354 | //Don't retry, timeout directly 355 | if (sendTimeOut < 0) 356 | { 357 | throw new TimeoutException("The sending attempt timed out"); 358 | } 359 | 360 | var timeOutTime = sendTimeOut > 0 ? DateTime.Now.AddMilliseconds(sendTimeOut) : DateTime.Now; 361 | 362 | var spinWait = new SpinWait(); 363 | 364 | while (m_Connected) 365 | { 366 | spinWait.SpinOnce(); 367 | 368 | if (InternalTrySend(segment)) 369 | return; 370 | 371 | //If sendTimeOut = 0, don't have timeout check 372 | if (sendTimeOut > 0 && DateTime.Now >= timeOutTime) 373 | { 374 | throw new TimeoutException("The sending attempt timed out"); 375 | } 376 | } 377 | } 378 | 379 | /// 380 | /// Sends the data segment to client. 381 | /// 382 | /// The segment which will be sent. 383 | public virtual void Send(ArraySegment segment) 384 | { 385 | InternalSend(segment); 386 | } 387 | 388 | private bool InternalTrySend(IList> segments) 389 | { 390 | if (!SocketSession.TrySend(segments)) 391 | return false; 392 | 393 | LastActiveTime = DateTime.Now; 394 | return true; 395 | } 396 | 397 | /// 398 | /// Try to send the data segments to client. 399 | /// 400 | /// The segments. 401 | /// 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 402 | public virtual bool TrySend(IList> segments) 403 | { 404 | if (!m_Connected) 405 | return false; 406 | 407 | return InternalTrySend(segments); 408 | } 409 | 410 | private void InternalSend(IList> segments) 411 | { 412 | if (!m_Connected) 413 | return; 414 | 415 | if (InternalTrySend(segments)) 416 | return; 417 | 418 | var sendTimeOut = 1000;// Config.SendTimeOut; 419 | 420 | //Don't retry, timeout directly 421 | if (sendTimeOut < 0) 422 | { 423 | throw new TimeoutException("The sending attempt timed out"); 424 | } 425 | 426 | var timeOutTime = sendTimeOut > 0 ? DateTime.Now.AddMilliseconds(sendTimeOut) : DateTime.Now; 427 | 428 | var spinWait = new SpinWait(); 429 | 430 | while (m_Connected) 431 | { 432 | spinWait.SpinOnce(); 433 | 434 | if (InternalTrySend(segments)) 435 | return; 436 | 437 | //If sendTimeOut = 0, don't have timeout check 438 | if (sendTimeOut > 0 && DateTime.Now >= timeOutTime) 439 | { 440 | throw new TimeoutException("The sending attempt timed out"); 441 | } 442 | } 443 | } 444 | 445 | /// 446 | /// Sends the data segments to client. 447 | /// 448 | /// The segments. 449 | public virtual void Send(IList> segments) 450 | { 451 | InternalSend(segments); 452 | } 453 | 454 | /// 455 | /// Sends the response. 456 | /// 457 | /// The message which will be sent. 458 | /// The parameter values. 459 | public virtual void Send(string message, params object[] paramValues) 460 | { 461 | var data = this.Charset.GetBytes(string.Format(message, paramValues)); 462 | InternalSend(new ArraySegment(data, 0, data.Length)); 463 | } 464 | 465 | #endregion 466 | 467 | #region Receiving processing 468 | 469 | /// 470 | /// Sets the next Receive filter which will be used when next data block received 471 | /// 472 | /// The next receive filter. 473 | //protected void SetNextReceiveFilter(IReceiveFilter nextReceiveFilter) 474 | //{ 475 | // m_ReceiveFilter = nextReceiveFilter; 476 | //} 477 | 478 | /// 479 | /// Gets the maximum allowed length of the request. 480 | /// 481 | /// 482 | protected virtual int GetMaxRequestLength() 483 | { 484 | return AppServer.Config.MaxRequestLength; 485 | } 486 | 487 | /// 488 | /// Filters the request. 489 | /// 490 | /// The read buffer. 491 | /// The offset. 492 | /// The length. 493 | /// if set to true [to be copied]. 494 | /// The rest, the size of the data which has not been processed 495 | /// return offset delta of next receiving buffer. 496 | /// 497 | TRequestInfo FilterRequest(byte[] readBuffer, int offset, int length, bool toBeCopied, out int rest, out int offsetDelta) 498 | { 499 | return null; 500 | 501 | if (!AppServer.OnRawDataReceived(this, readBuffer, offset, length)) 502 | { 503 | rest = 0; 504 | offsetDelta = 0; 505 | return null; 506 | } 507 | 508 | var currentRequestLength = m_ReceiveFilter.LeftBufferSize; 509 | 510 | var requestInfo = m_ReceiveFilter.Filter(readBuffer, offset, length, toBeCopied, out rest); 511 | 512 | if (m_ReceiveFilter.State == FilterState.Error) 513 | { 514 | rest = 0; 515 | offsetDelta = 0; 516 | Close(CloseReason.ProtocolError); 517 | return null; 518 | } 519 | 520 | var offsetAdapter = m_ReceiveFilter as IOffsetAdapter; 521 | 522 | offsetDelta = offsetAdapter != null ? offsetAdapter.OffsetDelta : 0; 523 | 524 | if (requestInfo == null) 525 | { 526 | //current buffered length 527 | currentRequestLength = m_ReceiveFilter.LeftBufferSize; 528 | } 529 | else 530 | { 531 | //current request length 532 | currentRequestLength = currentRequestLength + length - rest; 533 | } 534 | 535 | var maxRequestLength = GetMaxRequestLength(); 536 | 537 | if (currentRequestLength >= maxRequestLength) 538 | { 539 | if (Logger.IsErrorEnabled) 540 | Logger.Error(this, string.Format("Max request length: {0}, current processed length: {1}", maxRequestLength, currentRequestLength)); 541 | 542 | Close(CloseReason.ProtocolError); 543 | return null; 544 | } 545 | 546 | //If next Receive filter wasn't set, still use current Receive filter in next round received data processing 547 | if (m_ReceiveFilter.NextReceiveFilter != null) 548 | m_ReceiveFilter = m_ReceiveFilter.NextReceiveFilter; 549 | 550 | return requestInfo; 551 | } 552 | 553 | /// 554 | /// Processes the request data. 555 | /// 556 | /// The read buffer. 557 | /// The offset. 558 | /// The length. 559 | /// if set to true [to be copied]. 560 | /// 561 | /// return offset delta of next receiving buffer 562 | /// 563 | int IAppSession.ProcessRequest(byte[] readBuffer, int offset, int length, bool toBeCopied) 564 | { 565 | int rest, offsetDelta; 566 | 567 | while (true) 568 | { 569 | //var requestInfo = FilterRequest(readBuffer, offset, length, toBeCopied, out rest, out offsetDelta); 570 | 571 | if (requestInfo != null) 572 | { 573 | try 574 | { 575 | AppServer.ExecuteCommand(this, requestInfo); 576 | } 577 | catch (Exception e) 578 | { 579 | HandleException(e); 580 | } 581 | } 582 | 583 | if (rest <= 0) 584 | { 585 | return offsetDelta; 586 | } 587 | 588 | //Still have data has not been processed 589 | offset = offset + length - rest; 590 | length = rest; 591 | } 592 | } 593 | 594 | #endregion 595 | } 596 | 597 | /// 598 | /// AppServer basic class for whose request infoe type is StringRequestInfo 599 | /// 600 | /// The type of the app session. 601 | public abstract class AppSession : AppSession 602 | where TAppSession : AppSession, IAppSession, new() 603 | { 604 | 605 | private bool m_AppendNewLineForResponse = false; 606 | 607 | private static string m_NewLine = "\r\n"; 608 | 609 | /// 610 | /// Initializes a new instance of the class. 611 | /// 612 | public AppSession() 613 | : this(true) 614 | { 615 | 616 | } 617 | 618 | /// 619 | /// Initializes a new instance of the class. 620 | /// 621 | /// if set to true [append new line for response]. 622 | public AppSession(bool appendNewLineForResponse) 623 | { 624 | m_AppendNewLineForResponse = appendNewLineForResponse; 625 | } 626 | 627 | /// 628 | /// Handles the unknown request. 629 | /// 630 | /// The request info. 631 | protected override void HandleUnknownRequest(StringRequestInfo requestInfo) 632 | { 633 | Send("Unknown request: " + requestInfo.Key); 634 | } 635 | 636 | /// 637 | /// Processes the sending message. 638 | /// 639 | /// The raw message. 640 | /// 641 | protected virtual string ProcessSendingMessage(string rawMessage) 642 | { 643 | if (!m_AppendNewLineForResponse) 644 | return rawMessage; 645 | 646 | //if (AppServer.Config.Mode == SocketMode.Udp) 647 | // return rawMessage; 648 | 649 | if (string.IsNullOrEmpty(rawMessage) || !rawMessage.EndsWith(m_NewLine)) 650 | return rawMessage + m_NewLine; 651 | else 652 | return rawMessage; 653 | } 654 | 655 | /// 656 | /// Sends the specified message. 657 | /// 658 | /// The message. 659 | /// 660 | public override void Send(string message) 661 | { 662 | base.Send(ProcessSendingMessage(message)); 663 | } 664 | 665 | /// 666 | /// Sends the response. 667 | /// 668 | /// The message. 669 | /// The param values. 670 | /// Indicate whether the message was pushed into the sending queue 671 | public override void Send(string message, params object[] paramValues) 672 | { 673 | base.Send(ProcessSendingMessage(message), paramValues); 674 | } 675 | } 676 | 677 | /// 678 | /// AppServer basic class for whose request infoe type is StringRequestInfo 679 | /// 680 | public class AppSession : AppSession 681 | { 682 | 683 | } 684 | } 685 | --------------------------------------------------------------------------------