From adcb5ec09c547970bfb45eb32f99d19aad87c282 Mon Sep 17 00:00:00 2001 From: sta Date: Wed, 25 Sep 2013 20:35:06 +0900 Subject: [PATCH] Fix for issue #20 --- Example3/Example3.csproj | 2 +- websocket-sharp/Ext.cs | 15 ++ websocket-sharp/Server/HttpServer.cs | 151 ++++++++++---- websocket-sharp/Server/ServerState.cs | 40 ++++ websocket-sharp/Server/WebSocketServer.cs | 122 +++++++++-- websocket-sharp/Server/WebSocketServerBase.cs | 37 +++- .../Server/WebSocketServiceHost.cs | 124 ++++++++++-- .../Server/WebSocketServiceHostManager.cs | 95 ++++++--- .../Server/WebSocketSessionManager.cs | 191 ++++++++---------- websocket-sharp/websocket-sharp.csproj | 1 + 10 files changed, 561 insertions(+), 217 deletions(-) create mode 100644 websocket-sharp/Server/ServerState.cs diff --git a/Example3/Example3.csproj b/Example3/Example3.csproj index cfb81b7b..ce4fe265 100644 --- a/Example3/Example3.csproj +++ b/Example3/Example3.csproj @@ -8,7 +8,7 @@ {C648BA25-77E5-4A40-A97F-D0AA37B9FB26} Exe Example3 - Example3 + example3 v3.5 diff --git a/websocket-sharp/Ext.cs b/websocket-sharp/Ext.cs index 34a87f29..7f6aa368 100644 --- a/websocket-sharp/Ext.cs +++ b/websocket-sharp/Ext.cs @@ -49,6 +49,7 @@ using System.Security.Cryptography.X509Certificates; using System.Text; using WebSocketSharp.Net; using WebSocketSharp.Net.WebSockets; +using WebSocketSharp.Server; namespace WebSocketSharp { @@ -164,6 +165,20 @@ namespace WebSocketSharp } } + internal static string CheckIfStarted (this ServerState state) + { + return state != ServerState.START + ? "Not started, on shutdown or stopped." + : null; + } + + internal static string CheckIfStopped (this ServerState state) + { + return state == ServerState.START || state == ServerState.SHUTDOWN + ? "Already started or on shutdown." + : null; + } + internal static string CheckIfValidCloseData (this byte [] data) { return data.Length > 125 diff --git a/websocket-sharp/Server/HttpServer.cs b/websocket-sharp/Server/HttpServer.cs index 7269f7fb..69d9fbc2 100644 --- a/websocket-sharp/Server/HttpServer.cs +++ b/websocket-sharp/Server/HttpServer.cs @@ -47,13 +47,15 @@ namespace WebSocketSharp.Server #region Private Fields private HttpListener _listener; - private bool _listening; + private volatile bool _listening; private Logger _logger; private int _port; private Thread _receiveRequestThread; private string _rootPath; private bool _secure; private WebSocketServiceHostManager _serviceHosts; + private volatile ServerState _state; + private object _sync; private bool _windows; #endregion @@ -308,12 +310,40 @@ namespace WebSocketSharp.Server #region Private Methods + private void abort () + { + lock (_sync) + { + if (_state != ServerState.START) + return; + + _state = ServerState.SHUTDOWN; + } + + _listening = false; + _serviceHosts.Stop (((ushort) CloseStatusCode.SERVER_ERROR).ToByteArray (ByteOrder.BIG)); + _listener.Abort (); + + _state = ServerState.STOP; + } + + private string checkIfCertExists () + { + return _secure && + !EndPointListener.CertificateExists (_port, _listener.CertificateFolderPath) && + Certificate == null + ? "The secure connection requires a server certificate." + : null; + } + private void init () { _listener = new HttpListener (); _listening = false; _logger = new Logger (); _serviceHosts = new WebSocketServiceHostManager (_logger); + _state = ServerState.READY; + _sync = new object (); _windows = false; var os = Environment.OSVersion; @@ -420,7 +450,8 @@ namespace WebSocketSharp.Server context.Response.Close (); } catch (Exception ex) { - _logger.Fatal (ex.Message); + _logger.Fatal (ex.ToString ()); + context.Connection.Close (true); } }; @@ -434,15 +465,18 @@ namespace WebSocketSharp.Server try { processRequestAsync (_listener.GetContext ()); } - catch (HttpListenerException) { - _logger.Info ("HttpListener has been stopped."); + catch (HttpListenerException ex) { + _logger.Warn (String.Format ("Receiving has been stopped.\nreason: {0}.", ex.Message)); break; } catch (Exception ex) { - _logger.Fatal (ex.Message); + _logger.Fatal (ex.ToString ()); break; } } + + if (_listening) + abort (); } private void startReceiveRequestThread () @@ -452,20 +486,14 @@ namespace WebSocketSharp.Server _receiveRequestThread.Start (); } - private void stop (ushort code, string reason) + private void stopListener () { - var data = code.Append (reason); - var msg = data.CheckIfValidCloseData (); - if (msg != null) - { - _logger.Error (String.Format ("{0}\ncode: {1}\nreason: {2}", msg, code, reason)); + if (!_listening) return; - } - _serviceHosts.Stop (data); + _listening = false; _listener.Close (); _receiveRequestThread.Join (5 * 1000); - _listening = false; } #endregion @@ -554,21 +582,22 @@ namespace WebSocketSharp.Server /// public void Start () { - if (_listening) - return; - - if (_secure && - !EndPointListener.CertificateExists (_port, _listener.CertificateFolderPath) && - Certificate == null - ) + lock (_sync) { - _logger.Error ("Secure connection requires a server certificate."); - return; - } + var msg = _state.CheckIfStopped () ?? checkIfCertExists (); + if (msg != null) + { + _logger.Error (String.Format ("{0}\nstate: {1}\nsecure: {2}", msg, _state, _secure)); + return; + } - _listener.Start (); - startReceiveRequestThread (); - _listening = true; + _serviceHosts.Start (); + _listener.Start (); + startReceiveRequestThread (); + _listening = true; + + _state = ServerState.START; + } } /// @@ -576,13 +605,22 @@ namespace WebSocketSharp.Server /// public void Stop () { - if (!_listening) - return; + lock (_sync) + { + var msg = _state.CheckIfStarted (); + if (msg != null) + { + _logger.Error (String.Format ("{0}\nstate: {1}", msg, _state)); + return; + } + + _state = ServerState.SHUTDOWN; + } _serviceHosts.Stop (); - _listener.Close (); - _receiveRequestThread.Join (5 * 1000); - _listening = false; + stopListener (); + + _state = ServerState.STOP; } /// @@ -597,17 +635,26 @@ namespace WebSocketSharp.Server /// public void Stop (ushort code, string reason) { - if (!_listening) - return; - - var msg = code.CheckIfValidCloseStatusCode (); - if (msg != null) + byte [] data = null; + lock (_sync) { - _logger.Error (String.Format ("{0}\ncode: {1}", msg, code)); - return; + var msg = _state.CheckIfStarted () ?? + code.CheckIfValidCloseStatusCode () ?? + (data = code.Append (reason)).CheckIfValidCloseData (); + + if (msg != null) + { + _logger.Error (String.Format ("{0}\nstate: {1}\ncode: {2}\nreason: {3}", msg, _state, code, reason)); + return; + } + + _state = ServerState.SHUTDOWN; } - stop (code, reason); + _serviceHosts.Stop (data); + stopListener (); + + _state = ServerState.STOP; } /// @@ -615,17 +662,33 @@ namespace WebSocketSharp.Server /// and used to stop the WebSocket services. /// /// - /// A that contains a status code indicating the reason for stop. + /// One of the values that represent the status codes indicating + /// the reasons for stop. /// /// /// A that contains the reason for stop. /// public void Stop (CloseStatusCode code, string reason) { - if (!_listening) - return; + byte [] data = null; + lock (_sync) + { + var msg = _state.CheckIfStarted () ?? + (data = ((ushort) code).Append (reason)).CheckIfValidCloseData (); - stop ((ushort) code, reason); + if (msg != null) + { + _logger.Error (String.Format ("{0}\nstate: {1}\nreason: {2}", msg, _state, reason)); + return; + } + + _state = ServerState.SHUTDOWN; + } + + _serviceHosts.Stop (data); + stopListener (); + + _state = ServerState.STOP; } #endregion diff --git a/websocket-sharp/Server/ServerState.cs b/websocket-sharp/Server/ServerState.cs new file mode 100644 index 00000000..1a4b9254 --- /dev/null +++ b/websocket-sharp/Server/ServerState.cs @@ -0,0 +1,40 @@ +#region License +/* + * ServerState.cs + * + * The MIT License + * + * Copyright (c) 2013 sta.blockhead + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#endregion + +using System; + +namespace WebSocketSharp.Server +{ + internal enum ServerState + { + READY, + START, + SHUTDOWN, + STOP + } +} diff --git a/websocket-sharp/Server/WebSocketServer.cs b/websocket-sharp/Server/WebSocketServer.cs index 0c44bde5..bd86ebe4 100644 --- a/websocket-sharp/Server/WebSocketServer.cs +++ b/websocket-sharp/Server/WebSocketServer.cs @@ -48,6 +48,8 @@ namespace WebSocketSharp.Server #region Private Fields private WebSocketServiceHostManager _serviceHosts; + private volatile ServerState _state; + private object _sync; #endregion @@ -87,6 +89,8 @@ namespace WebSocketSharp.Server throw new ArgumentException ("Must not contain the path component: " + url, "url"); _serviceHosts = new WebSocketServiceHostManager (Log); + _state = ServerState.READY; + _sync = new object (); } /// @@ -139,6 +143,8 @@ namespace WebSocketSharp.Server : base (address, port, "/", secure) { _serviceHosts = new WebSocketServiceHostManager (Log); + _state = ServerState.READY; + _sync = new object (); } #endregion @@ -193,26 +199,30 @@ namespace WebSocketSharp.Server #endregion - #region Private Methods + #region Protected Methods - private void stop (ushort code, string reason) + /// + /// Aborts receiving the WebSocket connection requests. + /// + /// + /// This method is called when an exception occurs while receiving the WebSocket connection requests. + /// + protected override void Abort () { - var data = code.Append (reason); - var msg = data.CheckIfValidCloseData (); - if (msg != null) + lock (_sync) { - Log.Error (String.Format ("{0}\ncode: {1}\nreason: {2}", msg, code, reason)); - return; + if (_state != ServerState.START) + return; + + _state = ServerState.SHUTDOWN; } - base.Stop (); - _serviceHosts.Stop (data); + StopListener (); + _serviceHosts.Stop (((ushort) CloseStatusCode.SERVER_ERROR).ToByteArray (ByteOrder.BIG)); + + _state = ServerState.STOP; } - #endregion - - #region Protected Methods - /// /// Accepts a WebSocket connection request. /// @@ -301,13 +311,54 @@ namespace WebSocketSharp.Server return _serviceHosts.Remove (servicePath); } + /// + /// Starts to receive the WebSocket connection requests. + /// + public override void Start () + { + lock (_sync) + { + var msg = _state.CheckIfStopped (); + if (msg != null) + { + Log.Error (String.Format ("{0}\nstate: {1}", msg, _state)); + return; + } + + _serviceHosts.Start (); + + base.Start (); + if (!IsListening) + { + _serviceHosts.Stop (); + return; + } + + _state = ServerState.START; + } + } + /// /// Stops receiving the WebSocket connection requests. /// public override void Stop () { + lock (_sync) + { + var msg = _state.CheckIfStarted (); + if (msg != null) + { + Log.Error (String.Format ("{0}\nstate: {1}", msg, _state)); + return; + } + + _state = ServerState.SHUTDOWN; + } + base.Stop (); _serviceHosts.Stop (); + + _state = ServerState.STOP; } /// @@ -322,14 +373,26 @@ namespace WebSocketSharp.Server /// public void Stop (ushort code, string reason) { - var msg = code.CheckIfValidCloseStatusCode (); - if (msg != null) + byte [] data = null; + lock (_sync) { - Log.Error (String.Format ("{0}\ncode: {1}", msg, code)); - return; + var msg = _state.CheckIfStarted () ?? + code.CheckIfValidCloseStatusCode () ?? + (data = code.Append (reason)).CheckIfValidCloseData (); + + if (msg != null) + { + Log.Error (String.Format ("{0}\nstate: {1}\ncode: {2}\nreason: {3}", msg, _state, code, reason)); + return; + } + + _state = ServerState.SHUTDOWN; } - stop (code, reason); + base.Stop (); + _serviceHosts.Stop (data); + + _state = ServerState.STOP; } /// @@ -337,14 +400,33 @@ namespace WebSocketSharp.Server /// and . /// /// - /// A that contains a status code indicating the reason for stop. + /// One of the values that represent the status codes indicating + /// the reasons for stop. /// /// /// A that contains the reason for stop. /// public void Stop (CloseStatusCode code, string reason) { - stop ((ushort) code, reason); + byte [] data = null; + lock (_sync) + { + var msg = _state.CheckIfStarted () ?? + (data = ((ushort) code).Append (reason)).CheckIfValidCloseData (); + + if (msg != null) + { + Log.Error (String.Format ("{0}\nstate: {1}\nreason: {2}", msg, _state, reason)); + return; + } + + _state = ServerState.SHUTDOWN; + } + + base.Stop (); + _serviceHosts.Stop (data); + + _state = ServerState.STOP; } #endregion diff --git a/websocket-sharp/Server/WebSocketServerBase.cs b/websocket-sharp/Server/WebSocketServerBase.cs index 711347f7..1901ba54 100644 --- a/websocket-sharp/Server/WebSocketServerBase.cs +++ b/websocket-sharp/Server/WebSocketServerBase.cs @@ -48,7 +48,7 @@ namespace WebSocketSharp.Server private IPAddress _address; private X509Certificate2 _cert; - private bool _listening; + private volatile bool _listening; private Logger _logger; private int _port; private Thread _receiveRequestThread; @@ -339,8 +339,8 @@ namespace WebSocketSharp.Server } catch (Exception ex) { + _logger.Fatal (ex.ToString ()); client.Close (); - _logger.Fatal (ex.Message); } }; @@ -354,15 +354,18 @@ namespace WebSocketSharp.Server try { processRequestAsync (_listener.AcceptTcpClient ()); } - catch (SocketException) { - _logger.Info ("TcpListener has been stopped."); + catch (SocketException ex) { + _logger.Warn (String.Format ("Receiving has been stopped.\nreason: {0}.", ex.Message)); break; } catch (Exception ex) { - _logger.Fatal (ex.Message); + _logger.Fatal (ex.ToString ()); break; } } + + if (_listening) + Abort (); } private void startReceiveRequestThread () @@ -392,6 +395,14 @@ namespace WebSocketSharp.Server #region Protected Methods + /// + /// Aborts receiving the WebSocket connection requests. + /// + /// + /// This method is called when an exception occurs while receiving the WebSocket connection requests. + /// + protected abstract void Abort (); + /// /// Accepts a WebSocket connection request. /// @@ -400,6 +411,18 @@ namespace WebSocketSharp.Server /// protected abstract void AcceptWebSocket (TcpListenerWebSocketContext context); + /// + /// Stops the inner used to receive the WebSocket connection requests. + /// + protected void StopListener () + { + if (!_listening) + return; + + _listening = false; + _listener.Stop (); + } + #endregion #region Public Methods @@ -414,7 +437,7 @@ namespace WebSocketSharp.Server if (_secure && _cert == null) { - _logger.Error ("Secure connection requires a server certificate."); + _logger.Error ("The secure connection requires a server certificate."); return; } @@ -431,9 +454,9 @@ namespace WebSocketSharp.Server if (!_selfHost || !_listening) return; + _listening = false; _listener.Stop (); _receiveRequestThread.Join (5 * 1000); - _listening = false; } #endregion diff --git a/websocket-sharp/Server/WebSocketServiceHost.cs b/websocket-sharp/Server/WebSocketServiceHost.cs index c0680da7..1a6fb525 100644 --- a/websocket-sharp/Server/WebSocketServiceHost.cs +++ b/websocket-sharp/Server/WebSocketServiceHost.cs @@ -54,6 +54,8 @@ namespace WebSocketSharp.Server private string _servicePath; private WebSocketSessionManager _sessions; + private volatile ServerState _state; + private object _sync; #endregion @@ -63,6 +65,8 @@ namespace WebSocketSharp.Server : base (logger) { _sessions = new WebSocketSessionManager (logger); + _state = ServerState.READY; + _sync = new object (); } #endregion @@ -92,6 +96,8 @@ namespace WebSocketSharp.Server : base (url) { _sessions = new WebSocketSessionManager (Log); + _state = ServerState.READY; + _sync = new object (); } /// @@ -186,6 +192,8 @@ namespace WebSocketSharp.Server : base (address, port, servicePath, secure) { _sessions = new WebSocketSessionManager (Log); + _state = ServerState.READY; + _sync = new object (); } #endregion @@ -267,26 +275,30 @@ namespace WebSocketSharp.Server #endregion - #region Private Methods + #region Protected Methods - private void stop (ushort code, string reason) + /// + /// Aborts receiving the WebSocket connection requests. + /// + /// + /// This method is called when an exception occurs while receiving the WebSocket connection requests. + /// + protected override void Abort () { - var data = code.Append (reason); - var msg = data.CheckIfValidCloseData (); - if (msg != null) + lock (_sync) { - Log.Error (String.Format ("{0}\ncode: {1}\nreason: {2}", msg, code, reason)); - return; + if (_state != ServerState.START) + return; + + _state = ServerState.SHUTDOWN; } - base.Stop (); - _sessions.Stop (data); + StopListener (); + _sessions.Stop (((ushort) CloseStatusCode.SERVER_ERROR).ToByteArray (ByteOrder.BIG)); + + _state = ServerState.STOP; } - #endregion - - #region Protected Methods - /// /// Accepts a WebSocket connection request. /// @@ -315,13 +327,54 @@ namespace WebSocketSharp.Server #region Public Methods + /// + /// Starts to receive the WebSocket connection requests. + /// + public override void Start () + { + lock (_sync) + { + var msg = _state.CheckIfStopped (); + if (msg != null) + { + Log.Error (String.Format ("{0}\nstate: {1}", msg, _state)); + return; + } + + _sessions.Start (); + + base.Start (); + if (!IsListening) + { + _sessions.Stop (); + return; + } + + _state = ServerState.START; + } + } + /// /// Stops receiving the WebSocket connection requests. /// public override void Stop () { + lock (_sync) + { + var msg = _state.CheckIfStarted (); + if (msg != null) + { + Log.Error (String.Format ("{0}\nstate: {1}", msg, _state)); + return; + } + + _state = ServerState.SHUTDOWN; + } + base.Stop (); _sessions.Stop (); + + _state = ServerState.STOP; } /// @@ -336,14 +389,26 @@ namespace WebSocketSharp.Server /// public void Stop (ushort code, string reason) { - var msg = code.CheckIfValidCloseStatusCode (); - if (msg != null) + byte [] data = null; + lock (_sync) { - Log.Error (String.Format ("{0}\ncode: {1}", msg, code)); - return; + var msg = _state.CheckIfStarted () ?? + code.CheckIfValidCloseStatusCode () ?? + (data = code.Append (reason)).CheckIfValidCloseData (); + + if (msg != null) + { + Log.Error (String.Format ("{0}\nstate: {1}\ncode: {2}\nreason: {3}", msg, _state, code, reason)); + return; + } + + _state = ServerState.SHUTDOWN; } - stop (code, reason); + base.Stop (); + _sessions.Stop (data); + + _state = ServerState.STOP; } /// @@ -351,14 +416,33 @@ namespace WebSocketSharp.Server /// and . /// /// - /// A that contains a status code indicating the reason for stop. + /// One of the values that represent the status codes indicating + /// the reasons for stop. /// /// /// A that contains the reason for stop. /// public void Stop (CloseStatusCode code, string reason) { - stop ((ushort) code, reason); + byte [] data = null; + lock (_sync) + { + var msg = _state.CheckIfStarted () ?? + (data = ((ushort) code).Append (reason)).CheckIfValidCloseData (); + + if (msg != null) + { + Log.Error (String.Format ("{0}\nstate: {1}\nreason: {2}", msg, _state, reason)); + return; + } + + _state = ServerState.SHUTDOWN; + } + + base.Stop (); + _sessions.Stop (data); + + _state = ServerState.STOP; } #endregion diff --git a/websocket-sharp/Server/WebSocketServiceHostManager.cs b/websocket-sharp/Server/WebSocketServiceHostManager.cs index 95ab6e96..56b4be4f 100644 --- a/websocket-sharp/Server/WebSocketServiceHostManager.cs +++ b/websocket-sharp/Server/WebSocketServiceHostManager.cs @@ -45,6 +45,7 @@ namespace WebSocketSharp.Server private volatile bool _keepClean; private Logger _logger; private Dictionary _serviceHosts; + private volatile ServerState _state; private object _sync; #endregion @@ -61,6 +62,7 @@ namespace WebSocketSharp.Server _logger = logger; _keepClean = true; _serviceHosts = new Dictionary (); + _state = ServerState.READY; _sync = new object (); } @@ -78,7 +80,12 @@ namespace WebSocketSharp.Server get { var count = 0; foreach (var host in ServiceHosts) + { + if (_state != ServerState.START) + break; + count += host.ConnectionCount; + } return count; } @@ -143,12 +150,12 @@ namespace WebSocketSharp.Server internal set { lock (_sync) { - if (_keepClean ^ value) - { - _keepClean = value; - foreach (var host in _serviceHosts.Values) - host.KeepClean = value; - } + if (!(value ^ _keepClean)) + return; + + _keepClean = value; + foreach (var host in _serviceHosts.Values) + host.KeepClean = value; } } } @@ -192,7 +199,12 @@ namespace WebSocketSharp.Server { var result = new Dictionary> (); foreach (var host in ServiceHosts) + { + if (_state != ServerState.START) + break; + result.Add (host.ServicePath, host.Sessions.BroadpingInternally (data)); + } return result; } @@ -214,6 +226,9 @@ namespace WebSocketSharp.Server return; } + if (_state == ServerState.START) + serviceHost.Sessions.Start (); + _serviceHosts.Add (servicePath, serviceHost); } } @@ -234,18 +249,33 @@ namespace WebSocketSharp.Server _serviceHosts.Remove (servicePath); } - host.Sessions.Stop (((ushort) CloseStatusCode.AWAY).ToByteArray (ByteOrder.BIG)); + if (host.Sessions.State == ServerState.START) + host.Sessions.Stop (((ushort) CloseStatusCode.AWAY).ToByteArray (ByteOrder.BIG)); + return true; } + internal void Start () + { + lock (_sync) + { + foreach (var host in _serviceHosts.Values) + host.Sessions.Start (); + + _state = ServerState.START; + } + } + internal void Stop () { lock (_sync) { + _state = ServerState.SHUTDOWN; foreach (var host in _serviceHosts.Values) host.Sessions.Stop (); _serviceHosts.Clear (); + _state = ServerState.STOP; } } @@ -253,10 +283,12 @@ namespace WebSocketSharp.Server { lock (_sync) { + _state = ServerState.SHUTDOWN; foreach (var host in _serviceHosts.Values) host.Sessions.Stop (data); _serviceHosts.Clear (); + _state = ServerState.STOP; } } @@ -282,7 +314,7 @@ namespace WebSocketSharp.Server /// public void Broadcast (byte [] data) { - var msg = data.CheckIfValidSendData (); + var msg = _state.CheckIfStarted () ?? data.CheckIfValidSendData (); if (msg != null) { _logger.Error (msg); @@ -290,7 +322,12 @@ namespace WebSocketSharp.Server } foreach (var host in ServiceHosts) + { + if (_state != ServerState.START) + break; + host.Sessions.BroadcastInternally (data); + } } /// @@ -302,7 +339,7 @@ namespace WebSocketSharp.Server /// public void Broadcast (string data) { - var msg = data.CheckIfValidSendData (); + var msg = _state.CheckIfStarted () ?? data.CheckIfValidSendData (); if (msg != null) { _logger.Error (msg); @@ -310,7 +347,12 @@ namespace WebSocketSharp.Server } foreach (var host in ServiceHosts) + { + if (_state != ServerState.START) + break; + host.Sessions.BroadcastInternally (data); + } } /// @@ -328,7 +370,7 @@ namespace WebSocketSharp.Server /// public bool BroadcastTo (byte [] data, string servicePath) { - var msg = data.CheckIfValidSendData () ?? servicePath.CheckIfValidServicePath (); + var msg = _state.CheckIfStarted () ?? data.CheckIfValidSendData () ?? servicePath.CheckIfValidServicePath (); if (msg != null) { _logger.Error (msg); @@ -361,7 +403,7 @@ namespace WebSocketSharp.Server /// public bool BroadcastTo (string data, string servicePath) { - var msg = data.CheckIfValidSendData () ?? servicePath.CheckIfValidServicePath (); + var msg = _state.CheckIfStarted () ?? data.CheckIfValidSendData () ?? servicePath.CheckIfValidServicePath (); if (msg != null) { _logger.Error (msg); @@ -389,6 +431,13 @@ namespace WebSocketSharp.Server /// public Dictionary> Broadping () { + var msg = _state.CheckIfStarted (); + if (msg != null) + { + _logger.Error (msg); + return null; + } + return broadping (new byte [] {}); } @@ -408,10 +457,10 @@ namespace WebSocketSharp.Server public Dictionary> Broadping (string message) { if (message == null || message.Length == 0) - return broadping (new byte [] {}); + return Broadping (); var data = Encoding.UTF8.GetBytes (message); - var msg = data.CheckIfValidPingData (); + var msg = _state.CheckIfStarted () ?? data.CheckIfValidPingData (); if (msg != null) { _logger.Error (msg); @@ -434,7 +483,7 @@ namespace WebSocketSharp.Server /// public Dictionary BroadpingTo (string servicePath) { - var msg = servicePath.CheckIfValidServicePath (); + var msg = _state.CheckIfStarted () ?? servicePath.CheckIfValidServicePath (); if (msg != null) { _logger.Error (msg); @@ -472,7 +521,7 @@ namespace WebSocketSharp.Server return BroadpingTo (servicePath); var data = Encoding.UTF8.GetBytes (message); - var msg = data.CheckIfValidPingData () ?? servicePath.CheckIfValidServicePath (); + var msg = _state.CheckIfStarted () ?? data.CheckIfValidPingData () ?? servicePath.CheckIfValidServicePath (); if (msg != null) { _logger.Error (msg); @@ -501,7 +550,7 @@ namespace WebSocketSharp.Server /// public void CloseSession (string id, string servicePath) { - var msg = servicePath.CheckIfValidServicePath (); + var msg = _state.CheckIfStarted () ?? servicePath.CheckIfValidServicePath (); if (msg != null) { _logger.Error (msg); @@ -536,7 +585,7 @@ namespace WebSocketSharp.Server /// public void CloseSession (ushort code, string reason, string id, string servicePath) { - var msg = servicePath.CheckIfValidServicePath (); + var msg = _state.CheckIfStarted () ?? servicePath.CheckIfValidServicePath (); if (msg != null) { _logger.Error (msg); @@ -571,7 +620,7 @@ namespace WebSocketSharp.Server /// public void CloseSession (CloseStatusCode code, string reason, string id, string servicePath) { - var msg = servicePath.CheckIfValidServicePath (); + var msg = _state.CheckIfStarted () ?? servicePath.CheckIfValidServicePath (); if (msg != null) { _logger.Error (msg); @@ -604,7 +653,7 @@ namespace WebSocketSharp.Server /// public bool PingTo (string id, string servicePath) { - var msg = servicePath.CheckIfValidServicePath (); + var msg = _state.CheckIfStarted () ?? servicePath.CheckIfValidServicePath (); if (msg != null) { _logger.Error (msg); @@ -640,7 +689,7 @@ namespace WebSocketSharp.Server /// public bool PingTo (string message, string id, string servicePath) { - var msg = servicePath.CheckIfValidServicePath (); + var msg = _state.CheckIfStarted () ?? servicePath.CheckIfValidServicePath (); if (msg != null) { _logger.Error (msg); @@ -675,7 +724,7 @@ namespace WebSocketSharp.Server /// public bool SendTo (byte [] data, string id, string servicePath) { - var msg = servicePath.CheckIfValidServicePath (); + var msg = _state.CheckIfStarted () ?? servicePath.CheckIfValidServicePath (); if (msg != null) { _logger.Error (msg); @@ -710,7 +759,7 @@ namespace WebSocketSharp.Server /// public bool SendTo (string data, string id, string servicePath) { - var msg = servicePath.CheckIfValidServicePath (); + var msg = _state.CheckIfStarted () ?? servicePath.CheckIfValidServicePath (); if (msg != null) { _logger.Error (msg); @@ -744,7 +793,7 @@ namespace WebSocketSharp.Server /// public bool TryGetServiceHost (string servicePath, out IWebSocketServiceHost serviceHost) { - var msg = servicePath.CheckIfValidServicePath (); + var msg = _state.CheckIfStarted () ?? servicePath.CheckIfValidServicePath (); if (msg != null) { _logger.Error (msg); diff --git a/websocket-sharp/Server/WebSocketSessionManager.cs b/websocket-sharp/Server/WebSocketSessionManager.cs index 30135d53..713c2f82 100644 --- a/websocket-sharp/Server/WebSocketSessionManager.cs +++ b/websocket-sharp/Server/WebSocketSessionManager.cs @@ -42,9 +42,10 @@ namespace WebSocketSharp.Server #region Private Fields private object _forSweep; + private volatile bool _keepClean; private Logger _logger; private Dictionary _sessions; - private volatile bool _stopped; + private volatile ServerState _state; private volatile bool _sweeping; private Timer _sweepTimer; private object _sync; @@ -62,21 +63,30 @@ namespace WebSocketSharp.Server { _logger = logger; _forSweep = new object (); + _keepClean = true; _sessions = new Dictionary (); - _stopped = false; + _state = ServerState.READY; _sweeping = false; _sync = new object (); - setSweepTimer (); - startSweepTimer (); + setSweepTimer (60 * 1000); } #endregion #region Internal Properties + internal ServerState State { + get { + return _state; + } + } + internal IEnumerable ServiceInstances { get { + if (_state != ServerState.START) + return new List (); + lock (_sync) { return _sessions.Values.ToList (); @@ -158,7 +168,7 @@ namespace WebSocketSharp.Server /// public IWebSocketSession this [string id] { get { - var msg = id.CheckIfValidSessionID (); + var msg = _state.CheckIfStarted () ?? id.CheckIfValidSessionID (); if (msg != null) { _logger.Error (msg); @@ -182,17 +192,16 @@ namespace WebSocketSharp.Server /// public bool KeepClean { get { - return _sweepTimer.Enabled; + return _keepClean; } internal set { - if (value) - { - if (!_stopped) - startSweepTimer (); - } - else - stopSweepTimer (); + if (!(value ^ _keepClean)) + return; + + _keepClean = value; + if (_state == ServerState.START) + _sweepTimer.Enabled = value; } } @@ -213,72 +222,20 @@ namespace WebSocketSharp.Server #region Private Methods - private void broadcast (byte [] data) - { - foreach (var service in ServiceInstances) - service.Send (data); - } - - private void broadcast (string data) - { - foreach (var service in ServiceInstances) - service.Send (data); - } - - private void broadcastAsync (byte [] data) - { - var services = ServiceInstances.GetEnumerator (); - Action completed = null; - completed = () => - { - if (services.MoveNext ()) - services.Current.SendAsync (data, completed); - }; - - if (services.MoveNext ()) - services.Current.SendAsync (data, completed); - } - - private void broadcastAsync (string data) - { - var services = ServiceInstances.GetEnumerator (); - Action completed = null; - completed = () => - { - if (services.MoveNext ()) - services.Current.SendAsync (data, completed); - }; - - if (services.MoveNext ()) - services.Current.SendAsync (data, completed); - } - private static string createID () { return Guid.NewGuid ().ToString ("N"); } - private void setSweepTimer () + private void setSweepTimer (double interval) { - _sweepTimer = new Timer (60 * 1000); + _sweepTimer = new Timer (interval); _sweepTimer.Elapsed += (sender, e) => { Sweep (); }; } - private void startSweepTimer () - { - if (!_sweepTimer.Enabled) - _sweepTimer.Start (); - } - - private void stopSweepTimer () - { - if (_sweepTimer.Enabled) - _sweepTimer.Stop (); - } - #endregion #region Internal Methods @@ -287,7 +244,7 @@ namespace WebSocketSharp.Server { lock (_sync) { - if (_stopped) + if (_state != ServerState.START) return null; var id = createID (); @@ -299,25 +256,42 @@ namespace WebSocketSharp.Server internal void BroadcastInternally (byte [] data) { - if (_stopped) - broadcast (data); - else - broadcastAsync (data); + var services = ServiceInstances.GetEnumerator (); + Action completed = null; + completed = () => + { + if (_state == ServerState.START && services.MoveNext ()) + services.Current.SendAsync (data, completed); + }; + + if (_state == ServerState.START && services.MoveNext ()) + services.Current.SendAsync (data, completed); } internal void BroadcastInternally (string data) { - if (_stopped) - broadcast (data); - else - broadcastAsync (data); + var services = ServiceInstances.GetEnumerator (); + Action completed = null; + completed = () => + { + if (_state == ServerState.START && services.MoveNext ()) + services.Current.SendAsync (data, completed); + }; + + if (_state == ServerState.START && services.MoveNext ()) + services.Current.SendAsync (data, completed); } internal Dictionary BroadpingInternally (byte [] data) { var result = new Dictionary (); foreach (var session in ServiceInstances) + { + if (_state != ServerState.START) + break; + result.Add (session.ID, session.Context.WebSocket.Ping (data)); + } return result; } @@ -330,31 +304,37 @@ namespace WebSocketSharp.Server } } + internal void Start () + { + _sweepTimer.Enabled = _keepClean; + _state = ServerState.START; + } + internal void Stop () { - stopSweepTimer (); lock (_sync) { - if (_stopped) - return; + _state = ServerState.SHUTDOWN; - _stopped = true; - foreach (var session in ServiceInstances) + _sweepTimer.Enabled = false; + foreach (var session in _sessions.Values.ToList ()) session.Context.WebSocket.Close (); + + _state = ServerState.STOP; } } internal void Stop (byte [] data) { - stopSweepTimer (); lock (_sync) { - if (_stopped) - return; + _state = ServerState.SHUTDOWN; - _stopped = true; - foreach (var session in ServiceInstances) + _sweepTimer.Enabled = false; + foreach (var session in _sessions.Values.ToList ()) session.Context.WebSocket.Close (data); + + _state = ServerState.STOP; } } @@ -378,7 +358,7 @@ namespace WebSocketSharp.Server /// public void Broadcast (byte [] data) { - var msg = data.CheckIfValidSendData (); + var msg = _state.CheckIfStarted () ?? data.CheckIfValidSendData (); if (msg != null) { _logger.Error (msg); @@ -396,7 +376,7 @@ namespace WebSocketSharp.Server /// public void Broadcast (string data) { - var msg = data.CheckIfValidSendData (); + var msg = _state.CheckIfStarted () ?? data.CheckIfValidSendData (); if (msg != null) { _logger.Error (msg); @@ -415,6 +395,13 @@ namespace WebSocketSharp.Server /// public Dictionary Broadping () { + var msg = _state.CheckIfStarted (); + if (msg != null) + { + _logger.Error (msg); + return null; + } + return BroadpingInternally (new byte [] {}); } @@ -431,10 +418,10 @@ namespace WebSocketSharp.Server public Dictionary Broadping (string message) { if (message == null || message.Length == 0) - return BroadpingInternally (new byte [] {}); + return Broadping (); var data = Encoding.UTF8.GetBytes (message); - var msg = data.CheckIfValidPingData (); + var msg = _state.CheckIfStarted () ?? data.CheckIfValidPingData (); if (msg != null) { _logger.Error (msg); @@ -452,7 +439,7 @@ namespace WebSocketSharp.Server /// public void CloseSession (string id) { - var msg = id.CheckIfValidSessionID (); + var msg = _state.CheckIfStarted () ?? id.CheckIfValidSessionID (); if (msg != null) { _logger.Error (msg); @@ -484,7 +471,7 @@ namespace WebSocketSharp.Server /// public void CloseSession (ushort code, string reason, string id) { - var msg = id.CheckIfValidSessionID (); + var msg = _state.CheckIfStarted () ?? id.CheckIfValidSessionID (); if (msg != null) { _logger.Error (msg); @@ -516,7 +503,7 @@ namespace WebSocketSharp.Server /// public void CloseSession (CloseStatusCode code, string reason, string id) { - var msg = id.CheckIfValidSessionID (); + var msg = _state.CheckIfStarted () ?? id.CheckIfValidSessionID (); if (msg != null) { _logger.Error (msg); @@ -545,7 +532,7 @@ namespace WebSocketSharp.Server /// public bool PingTo (string id) { - var msg = id.CheckIfValidSessionID (); + var msg = _state.CheckIfStarted () ?? id.CheckIfValidSessionID (); if (msg != null) { _logger.Error (msg); @@ -578,7 +565,7 @@ namespace WebSocketSharp.Server /// public bool PingTo (string message, string id) { - var msg = id.CheckIfValidSessionID (); + var msg = _state.CheckIfStarted () ?? id.CheckIfValidSessionID (); if (msg != null) { _logger.Error (msg); @@ -610,7 +597,7 @@ namespace WebSocketSharp.Server /// public bool SendTo (byte [] data, string id) { - var msg = id.CheckIfValidSessionID (); + var msg = _state.CheckIfStarted () ?? id.CheckIfValidSessionID (); if (msg != null) { _logger.Error (msg); @@ -643,7 +630,7 @@ namespace WebSocketSharp.Server /// public bool SendTo (string data, string id) { - var msg = id.CheckIfValidSessionID (); + var msg = _state.CheckIfStarted () ?? id.CheckIfValidSessionID (); if (msg != null) { _logger.Error (msg); @@ -666,7 +653,7 @@ namespace WebSocketSharp.Server /// public void Sweep () { - if (_stopped || _sweeping || Count == 0) + if (_state != ServerState.START || _sweeping || Count == 0) return; lock (_forSweep) @@ -674,11 +661,11 @@ namespace WebSocketSharp.Server _sweeping = true; foreach (var id in InactiveIDs) { + if (_state != ServerState.START) + break; + lock (_sync) { - if (_stopped) - break; - WebSocketService session; if (_sessions.TryGetValue (id, out session)) { @@ -714,7 +701,7 @@ namespace WebSocketSharp.Server /// public bool TryGetSession (string id, out IWebSocketSession session) { - var msg = id.CheckIfValidSessionID (); + var msg = _state.CheckIfStarted () ?? id.CheckIfValidSessionID (); if (msg != null) { _logger.Error (msg); diff --git a/websocket-sharp/websocket-sharp.csproj b/websocket-sharp/websocket-sharp.csproj index 0774bf83..13553bcf 100644 --- a/websocket-sharp/websocket-sharp.csproj +++ b/websocket-sharp/websocket-sharp.csproj @@ -129,6 +129,7 @@ +