using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Collections.ObjectModel; using System.IO; using System.Net; using System.Net.Sockets; using System.Security.Authentication; using System.Text; using System.Threading; using SuperSocket.Common; using SuperSocket.SocketBase; using SuperSocket.SocketBase.Command; using SuperSocket.SocketBase.Config; using SuperSocket.SocketBase.Protocol; namespace SuperSocket.SocketEngine { static class SocketState { public const int Normal = 0;//0000 0000 public const int InClosing = 16;//0001 0000 >= 16 public const int Closed = 16777216;//256 * 256 * 256; 0x01 0x00 0x00 0x00 public const int InSending = 1;//0000 0001 > 1 public const int InReceiving = 2;//0000 0010 > 2 public const int InSendingReceivingMask = -4;// ~(InSending | InReceiving); 0xf0 0xff 0xff 0xff } /// /// Socket Session, all application session should base on this class /// abstract partial class SocketSession : ISocketSession { public IAppSession AppSession { get; private set; } protected readonly object SyncRoot = new object(); //0x00 0x00 0x00 0x00 //1st byte: Closed(Y/N) - 0x01 //2nd byte: N/A //3th byte: CloseReason //Last byte: 0000 0000 - normal state //0000 0001: in sending //0000 0010: in receiving //0001 0000: in closing private int m_State = 0; private void AddStateFlag(int stateValue) { AddStateFlag(stateValue, false); } private bool AddStateFlag(int stateValue, bool notClosing) { while(true) { var oldState = m_State; if (notClosing) { // don't update the state if the connection has entered the closing procedure if (oldState >= SocketState.InClosing) { return false; } } var newState = m_State | stateValue; if(Interlocked.CompareExchange(ref m_State, newState, oldState) == oldState) return true; } } private bool TryAddStateFlag(int stateValue) { while (true) { var oldState = m_State; var newState = m_State | stateValue; //Already marked if (oldState == newState) { return false; } var compareState = Interlocked.CompareExchange(ref m_State, newState, oldState); if (compareState == oldState) return true; } } private void RemoveStateFlag(int stateValue) { while(true) { var oldState = m_State; var newState = m_State & (~stateValue); if(Interlocked.CompareExchange(ref m_State, newState, oldState) == oldState) return; } } private bool CheckState(int stateValue) { return (m_State & stateValue) == stateValue; } protected bool SyncSend { get; private set; } private ISmartPool m_SendingQueuePool; public SocketSession(Socket client) : this(Guid.NewGuid().ToString()) { if (client == null) throw new ArgumentNullException("client"); m_Client = client; LocalEndPoint = (IPEndPoint)client.LocalEndPoint; RemoteEndPoint = (IPEndPoint)client.RemoteEndPoint; } public SocketSession(string sessionID) { SessionID = sessionID; } public virtual void Initialize(IAppSession appSession) { AppSession = appSession; Config = appSession.Config; SyncSend = Config.SyncSend; if (m_SendingQueuePool == null) m_SendingQueuePool = ((SocketServerBase)((ISocketServerAccessor)appSession.AppServer).SocketServer).SendingQueuePool; SendingQueue queue; if (m_SendingQueuePool.TryGet(out queue)) { m_SendingQueue = queue; queue.StartEnqueue(); } } /// /// Gets or sets the session ID. /// /// The session ID. public string SessionID { get; private set; } /// /// Gets or sets the config. /// /// /// The config. /// public IServerConfig Config { get; set; } /// /// Starts this session. /// public abstract void Start(); /// /// Says the welcome information when a client connectted. /// protected virtual void StartSession() { AppSession.StartSession(); } /// /// Called when [close]. /// protected virtual void OnClosed(CloseReason reason) { //Already closed if (!TryAddStateFlag(SocketState.Closed)) return; //Before changing m_SendingQueue, must check m_IsClosed while (true) { var sendingQueue = m_SendingQueue; if (sendingQueue == null) break; //There is no sending was started after the m_Closed ws set to 'true' if (Interlocked.CompareExchange(ref m_SendingQueue, null, sendingQueue) == sendingQueue) { sendingQueue.Clear(); m_SendingQueuePool.Push(sendingQueue); break; } } var closedHandler = Closed; if (closedHandler != null) { closedHandler(this, reason); } } /// /// Occurs when [closed]. /// public Action Closed { get; set; } private SendingQueue m_SendingQueue; /// /// Tries to send array segment. /// /// The segments. /// public bool TrySend(IList> segments) { if (IsClosed) return false; var queue = m_SendingQueue; if (queue == null) return false; var trackID = queue.TrackID; if (!queue.Enqueue(segments, trackID)) return false; StartSend(queue, trackID, true); return true; } /// /// Tries to send array segment. /// /// The segment. /// public bool TrySend(ArraySegment segment) { if (IsClosed) return false; var queue = m_SendingQueue; if (queue == null) return false; var trackID = queue.TrackID; if (!queue.Enqueue(segment, trackID)) return false; StartSend(queue, trackID, true); return true; } /// /// Sends in async mode. /// /// The queue. protected abstract void SendAsync(SendingQueue queue); /// /// Sends in sync mode. /// /// The queue. protected abstract void SendSync(SendingQueue queue); private void Send(SendingQueue queue) { if (SyncSend) { SendSync(queue); } else { SendAsync(queue); } } private void StartSend(SendingQueue queue, int sendingTrackID, bool initial) { if (initial) { if (!TryAddStateFlag(SocketState.InSending)) { return; } var currentQueue = m_SendingQueue; if (currentQueue != queue || sendingTrackID != currentQueue.TrackID) { //Has been sent OnSendEnd(); return; } } Socket client; if (IsInClosingOrClosed && TryValidateClosedBySocket(out client)) { OnSendEnd(true); return; } SendingQueue newQueue; if (!m_SendingQueuePool.TryGet(out newQueue)) { AppSession.Logger.Error("There is no enougth sending queue can be used."); OnSendEnd(false); this.Close(CloseReason.InternalError); return; } var oldQueue = Interlocked.CompareExchange(ref m_SendingQueue, newQueue, queue); if (!ReferenceEquals(oldQueue, queue)) { if (newQueue != null) m_SendingQueuePool.Push(newQueue); if (IsInClosingOrClosed) { OnSendEnd(true); } else { OnSendEnd(false); AppSession.Logger.Error("Failed to switch the sending queue."); this.Close(CloseReason.InternalError); } return; } //Start to allow enqueue newQueue.StartEnqueue(); queue.StopEnqueue(); if (queue.Count == 0) { AppSession.Logger.Error("There is no data to be sent in the queue."); m_SendingQueuePool.Push(queue); OnSendEnd(false); this.Close(CloseReason.InternalError); return; } Send(queue); } private void OnSendEnd() { OnSendEnd(IsInClosingOrClosed); } private void OnSendEnd(bool isInClosingOrClosed) { RemoveStateFlag(SocketState.InSending); if (isInClosingOrClosed) { Socket client; if (!TryValidateClosedBySocket(out client)) { var sendingQueue = m_SendingQueue; //No data to be sent if (sendingQueue != null && sendingQueue.Count == 0) { if (client != null)// the socket instance is not closed yet, do it now InternalClose(client, GetCloseReasonFromState(), false); else// The UDP mode, the socket instance always is null, fire the closed event directly OnClosed(GetCloseReasonFromState()); return; } return; } if (ValidateNotInSendingReceiving()) { FireCloseEvent(); } } } protected virtual void OnSendingCompleted(SendingQueue queue) { queue.Clear(); m_SendingQueuePool.Push(queue); var newQueue = m_SendingQueue; if (IsInClosingOrClosed) { Socket client; //has data is being sent and the socket isn't closed if (newQueue.Count > 0 && !TryValidateClosedBySocket(out client)) { StartSend(newQueue, newQueue.TrackID, false); return; } OnSendEnd(true); return; } if (newQueue.Count == 0) { OnSendEnd(); if (newQueue.Count > 0) { StartSend(newQueue, newQueue.TrackID, true); } } else { StartSend(newQueue, newQueue.TrackID, false); } } public abstract void ApplySecureProtocol(); public Stream GetUnderlyStream() { return new NetworkStream(Client); } private Socket m_Client; /// /// Gets or sets the client. /// /// The client. public Socket Client { get { return m_Client; } } protected bool IsInClosingOrClosed { get { return m_State >= SocketState.InClosing; } } protected bool IsClosed { get { return m_State >= SocketState.Closed; } } /// /// Gets the local end point. /// /// The local end point. public virtual IPEndPoint LocalEndPoint { get; protected set; } /// /// Gets the remote end point. /// /// The remote end point. public virtual IPEndPoint RemoteEndPoint { get; protected set; } /// /// Gets or sets the secure protocol. /// /// The secure protocol. public SslProtocols SecureProtocol { get; set; } protected virtual bool TryValidateClosedBySocket(out Socket socket) { socket = m_Client; //Already closed/closing return socket == null; } public virtual void Close(CloseReason reason) { //Already in closing procedure if (!TryAddStateFlag(SocketState.InClosing)) return; Socket client; //No need to clean the socket instance if (TryValidateClosedBySocket(out client)) return; //Some data is in sending if (CheckState(SocketState.InSending)) { //Set closing reason only, don't close the socket directly AddStateFlag(GetCloseReasonValue(reason)); return; } // In the udp mode, we needn't close the socket instance if (client != null) InternalClose(client, reason, true); else //In Udp mode, and the socket is not in the sending state, then fire the closed event directly OnClosed(reason); } private void InternalClose(Socket client, CloseReason reason, bool setCloseReason) { if (Interlocked.CompareExchange(ref m_Client, null, client) == client) { if (setCloseReason) AddStateFlag(GetCloseReasonValue(reason)); client.SafeClose(); if (ValidateNotInSendingReceiving()) { OnClosed(reason); } } } protected void OnSendError(SendingQueue queue, CloseReason closeReason) { queue.Clear(); m_SendingQueuePool.Push(queue); OnSendEnd(); ValidateClosed(closeReason); } // the receive action won't be started for this connection any more protected void OnReceiveTerminated(CloseReason closeReason) { OnReceiveEnded(); ValidateClosed(closeReason); } // return false if the connection has entered the closing procedure or has closed already protected bool OnReceiveStarted() { return AddStateFlag(SocketState.InReceiving, true); } protected void OnReceiveEnded() { RemoveStateFlag(SocketState.InReceiving); } /// /// Validates the socket is not in the sending or receiving operation. /// /// private bool ValidateNotInSendingReceiving() { var oldState = m_State; if ((oldState & SocketState.InSendingReceivingMask) == oldState) { return true; } return false; } private const int m_CloseReasonMagic = 256; private int GetCloseReasonValue(CloseReason reason) { return ((int)reason + 1) * m_CloseReasonMagic; } private CloseReason GetCloseReasonFromState() { return (CloseReason)(m_State / m_CloseReasonMagic - 1); } private void FireCloseEvent() { OnClosed(GetCloseReasonFromState()); } private void ValidateClosed(CloseReason closeReason) { if (IsClosed) return; if (CheckState(SocketState.InClosing)) { if (ValidateNotInSendingReceiving()) { FireCloseEvent(); } } else { Close(closeReason); } } public abstract int OrigReceiveOffset { get; } protected virtual bool IsIgnorableSocketError(int socketErrorCode) { if (socketErrorCode == 10004 //Interrupted || socketErrorCode == 10053 //ConnectionAborted || socketErrorCode == 10054 //ConnectionReset || socketErrorCode == 10058 //Shutdown || socketErrorCode == 10060 //TimedOut || socketErrorCode == 995 //OperationAborted || socketErrorCode == -1073741299) { return true; } return false; } protected virtual bool IsIgnorableException(Exception e, out int socketErrorCode) { socketErrorCode = 0; if (e is ObjectDisposedException || e is NullReferenceException) return true; SocketException socketException = null; if (e is IOException) { if (e.InnerException is ObjectDisposedException || e.InnerException is NullReferenceException) return true; socketException = e.InnerException as SocketException; } else { socketException = e as SocketException; } if (socketException == null) return false; socketErrorCode = socketException.ErrorCode; if (Config.LogAllSocketException) return false; return IsIgnorableSocketError(socketErrorCode); } } }