diff --git a/websocket-sharp/Server/IServiceHost.cs b/websocket-sharp/Server/IServiceHost.cs index 43988ff6..5fec48b9 100644 --- a/websocket-sharp/Server/IServiceHost.cs +++ b/websocket-sharp/Server/IServiceHost.cs @@ -27,16 +27,25 @@ #endregion using System; +using System.Collections.Generic; using WebSocketSharp.Net.WebSockets; -namespace WebSocketSharp.Server { - +namespace WebSocketSharp.Server +{ /// /// Exposes the methods and properties for the WebSocket service host. /// /// /// - public interface IServiceHost { + public interface IServiceHost + { + /// + /// Gets the connection count to the WebSocket service host. + /// + /// + /// An that contains the connection count. + /// + int ConnectionCount { get; } /// /// Gets or sets a value indicating whether the WebSocket service host cleans up the inactive service @@ -54,24 +63,101 @@ namespace WebSocketSharp.Server { /// /// A that contains the WebSocket connection request objects to bind. /// - void BindWebSocket(WebSocketContext context); + void BindWebSocket (WebSocketContext context); /// - /// Broadcasts the specified to all service clients. + /// Broadcasts the specified array of to all clients of the WebSocket service host. + /// + /// + /// An array of to broadcast. + /// + void Broadcast (byte [] data); + + /// + /// Broadcasts the specified to all clients of the WebSocket service host. /// /// /// A to broadcast. /// - void Broadcast(string data); + void Broadcast (string data); + + /// + /// Sends Pings with the specified to all clients of the WebSocket service host. + /// + /// + /// A Dictionary<string, bool> that contains the collection of session IDs and values + /// indicating whether the WebSocket service host received the Pongs from each clients in a time. + /// + /// + /// A that contains a message to send. + /// + Dictionary Broadping (string message); + + /// + /// Sends a Ping with the specified to the client associated with + /// the specified ID. + /// + /// + /// true if the WebSocket service host receives a Pong from the client in a time; + /// otherwise, false. + /// + /// + /// A that contains an ID that represents the destination for the Ping. + /// + /// + /// A that contains a message to send. + /// + bool PingTo (string id, string message); + + /// + /// Sends a binary data to the client associated with the specified ID. + /// + /// + /// true if the client associated with is successfully found; + /// otherwise, false. + /// + /// + /// A that contains an ID that represents the destination for the data. + /// + /// + /// An array of that contains a binary data to send. + /// + bool SendTo (string id, byte [] data); + + /// + /// Sends a text data to the client associated with the specified ID. + /// + /// + /// true if the client associated with is successfully found; + /// otherwise, false. + /// + /// + /// A that contains an ID that represents the destination for the data. + /// + /// + /// A that contains a text data to send. + /// + bool SendTo (string id, string data); /// /// Starts the WebSocket service host. /// - void Start(); + void Start (); /// /// Stops the WebSocket service host. /// - void Stop(); + void Stop (); + + /// + /// Stops the WebSocket service host with the specified and . + /// + /// + /// A that contains a status code indicating the reason for stop. + /// + /// + /// A that contains the reason for stop. + /// + void Stop (ushort code, string reason); } } diff --git a/websocket-sharp/Server/ServiceHostManager.cs b/websocket-sharp/Server/ServiceHostManager.cs index 76a1f6f8..5d4b760d 100644 --- a/websocket-sharp/Server/ServiceHostManager.cs +++ b/websocket-sharp/Server/ServiceHostManager.cs @@ -29,34 +29,56 @@ using System; using System.Collections.Generic; -namespace WebSocketSharp.Server { - - internal class ServiceHostManager { - +namespace WebSocketSharp.Server +{ + internal class ServiceHostManager + { #region Private Fields - private bool _keepClean; - private Dictionary _svcHosts; + private volatile bool _keepClean; + private Logger _logger; + private Dictionary _serviceHosts; + private object _sync; #endregion #region Public Constructors - public ServiceHostManager() + public ServiceHostManager () + : this (new Logger ()) { + } + + public ServiceHostManager (Logger logger) + { + _logger = logger; _keepClean = true; - _svcHosts = new Dictionary(); + _serviceHosts = new Dictionary (); + _sync = new object (); } #endregion #region Public Properties + public int ConnectionCount { + get { + var count = 0; + foreach (var host in ServiceHosts) + count += host.ConnectionCount; + + return count; + } + } + public int Count { get { - return _svcHosts.Count; + lock (_sync) + { + return _serviceHosts.Count; + } } - } + } public bool KeepClean { get { @@ -64,24 +86,45 @@ namespace WebSocketSharp.Server { } set { - if (_keepClean ^ value) + lock (_sync) { - _keepClean = value; - foreach (var svcHost in _svcHosts.Values) - svcHost.KeepClean = value; + if (_keepClean ^ value) + { + _keepClean = value; + foreach (var host in _serviceHosts.Values) + host.KeepClean = value; + } } } } public IEnumerable Paths { get { - return _svcHosts.Keys; + lock (_sync) + { + return _serviceHosts.Keys; + } } } public IEnumerable ServiceHosts { get { - return _svcHosts.Values; + lock (_sync) + { + return _serviceHosts.Values; + } + } + } + + #endregion + + #region Private Methods + + private Dictionary copy () + { + lock (_sync) + { + return new Dictionary (_serviceHosts); } } @@ -89,28 +132,173 @@ namespace WebSocketSharp.Server { #region Public Methods - public void Add(string absPath, IServiceHost svcHost) + public void Add (string servicePath, IServiceHost serviceHost) { - _svcHosts.Add(absPath.UrlDecode(), svcHost); + lock (_sync) + { + IServiceHost host; + if (_serviceHosts.TryGetValue (servicePath, out host)) + { + _logger.Error ( + "The WebSocket service host with the specified path found.\npath: " + servicePath); + return; + } + + _serviceHosts.Add (servicePath.UrlDecode (), serviceHost); + } } - public void Broadcast(string data) + public void Broadcast (byte [] data) { - foreach (var svcHost in _svcHosts.Values) - svcHost.Broadcast(data); + foreach (var host in ServiceHosts) + host.Broadcast (data); } - public void Stop() + public void Broadcast (string data) { - foreach (var svcHost in _svcHosts.Values) - svcHost.Stop(); - - _svcHosts.Clear(); + foreach (var host in ServiceHosts) + host.Broadcast (data); } - public bool TryGetServiceHost(string absPath, out IServiceHost svcHost) + public bool BroadcastTo (string servicePath, byte [] data) { - return _svcHosts.TryGetValue(absPath, out svcHost); + IServiceHost host; + if (TryGetServiceHost (servicePath, out host)) + { + host.Broadcast (data); + return true; + } + + _logger.Error ( + "The WebSocket service host with the specified path not found.\npath: " + servicePath); + return false; + } + + public bool BroadcastTo (string servicePath, string data) + { + IServiceHost host; + if (TryGetServiceHost (servicePath, out host)) + { + host.Broadcast (data); + return true; + } + + _logger.Error ( + "The WebSocket service host with the specified path not found.\npath: " + servicePath); + return false; + } + + public Dictionary> Broadping (string message) + { + var result = new Dictionary> (); + foreach (var service in copy ()) + result.Add (service.Key, service.Value.Broadping (message)); + + return result; + } + + public Dictionary BroadpingTo (string servicePath, string message) + { + IServiceHost host; + if (TryGetServiceHost (servicePath, out host)) + return host.Broadping (message); + + _logger.Error ( + "The WebSocket service host with the specified path not found.\npath: " + servicePath); + return null; + } + + public int GetConnectionCount (string servicePath) + { + IServiceHost host; + if (TryGetServiceHost (servicePath, out host)) + return host.ConnectionCount; + + _logger.Error ( + "The WebSocket service host with the specified path not found.\npath: " + servicePath); + return -1; + } + + public bool PingTo (string servicePath, string id, string message) + { + IServiceHost host; + if (TryGetServiceHost (servicePath, out host)) + return host.PingTo (id, message); + + _logger.Error ( + "The WebSocket service host with the specified path not found.\npath: " + servicePath); + return false; + } + + public bool Remove (string servicePath) + { + IServiceHost host; + lock (_sync) + { + if (!_serviceHosts.TryGetValue (servicePath, out host)) + { + _logger.Error ( + "The WebSocket service host with the specified path not found.\npath: " + servicePath); + return false; + } + + _serviceHosts.Remove (servicePath); + } + + host.Stop ((ushort) CloseStatusCode.AWAY, String.Empty); + return true; + } + + public bool SendTo (string servicePath, string id, byte [] data) + { + IServiceHost host; + if (TryGetServiceHost (servicePath, out host)) + return host.SendTo (id, data); + + _logger.Error ( + "The WebSocket service host with the specified path not found.\npath: " + servicePath); + return false; + } + + public bool SendTo (string servicePath, string id, string data) + { + IServiceHost host; + if (TryGetServiceHost (servicePath, out host)) + return host.SendTo (id, data); + + _logger.Error ( + "The WebSocket service host with the specified path not found.\npath: " + servicePath); + return false; + } + + public void Stop () + { + lock (_sync) + { + foreach (var host in _serviceHosts.Values) + host.Stop (); + + _serviceHosts.Clear (); + } + } + + public void Stop (ushort code, string reason) + { + lock (_sync) + { + foreach (var host in _serviceHosts.Values) + host.Stop (code, reason); + + _serviceHosts.Clear (); + } + } + + public bool TryGetServiceHost (string servicePath, out IServiceHost serviceHost) + { + lock (_sync) + { + return _serviceHosts.TryGetValue (servicePath, out serviceHost); + } } #endregion diff --git a/websocket-sharp/Server/WebSocketServer.cs b/websocket-sharp/Server/WebSocketServer.cs index 4f2926db..6ffae487 100644 --- a/websocket-sharp/Server/WebSocketServer.cs +++ b/websocket-sharp/Server/WebSocketServer.cs @@ -31,11 +31,12 @@ using System; using System.Collections.Generic; using System.Net.Sockets; +using System.Text; using WebSocketSharp.Net; using WebSocketSharp.Net.WebSockets; -namespace WebSocketSharp.Server { - +namespace WebSocketSharp.Server +{ /// /// Provides the functions of the server that receives the WebSocket connection requests. /// @@ -46,7 +47,7 @@ namespace WebSocketSharp.Server { { #region Private Fields - private ServiceHostManager _svcHosts; + private ServiceHostManager _serviceHosts; #endregion @@ -55,95 +56,107 @@ namespace WebSocketSharp.Server { /// /// Initializes a new instance of the class. /// - public WebSocketServer() - : this(80) + public WebSocketServer () + : this (80) { } /// - /// Initializes a new instance of the class that listens for incoming connection attempts - /// on the specified . + /// Initializes a new instance of the class that listens for + /// incoming connection attempts on the specified . /// /// /// An that contains a port number. /// - public WebSocketServer(int port) - : this(System.Net.IPAddress.Any, port) + public WebSocketServer (int port) + : this (System.Net.IPAddress.Any, port) { } /// - /// Initializes a new instance of the class that listens for incoming connection attempts - /// on the specified WebSocket URL. + /// Initializes a new instance of the class that listens for + /// incoming connection attempts on the specified WebSocket URL. /// /// /// A that contains a WebSocket URL. /// - public WebSocketServer(string url) - : base(url) + public WebSocketServer (string url) + : base (url) { if (BaseUri.AbsolutePath != "/") - { - var msg = "Must not contain the path component: " + url; - throw new ArgumentException(msg, "url"); - } + throw new ArgumentException ("Must not contain the path component: " + url, "url"); - _svcHosts = new ServiceHostManager(); + _serviceHosts = new ServiceHostManager (Log); } /// - /// Initializes a new instance of the class that listens for incoming connection attempts - /// on the specified and . + /// Initializes a new instance of the class that listens for + /// incoming connection attempts on the specified and . /// /// - /// An that contains a port number. + /// An that contains a port number. /// /// - /// A that indicates providing a secure connection or not. (true indicates providing a secure connection.) + /// A that indicates providing a secure connection or not. + /// (true indicates providing a secure connection.) /// - public WebSocketServer(int port, bool secure) - : this(System.Net.IPAddress.Any, port, secure) + public WebSocketServer (int port, bool secure) + : this (System.Net.IPAddress.Any, port, secure) { } /// - /// Initializes a new instance of the class that listens for incoming connection attempts - /// on the specified and . + /// Initializes a new instance of the class that listens for + /// incoming connection attempts on the specified and . /// /// /// A that contains a local IP address. /// /// - /// An that contains a port number. + /// An that contains a port number. /// - public WebSocketServer(System.Net.IPAddress address, int port) - : this(address, port, port == 443 ? true : false) + public WebSocketServer (System.Net.IPAddress address, int port) + : this (address, port, port == 443 ? true : false) { } /// - /// Initializes a new instance of the class that listens for incoming connection attempts - /// on the specified , and . + /// Initializes a new instance of the class that listens for + /// incoming connection attempts on the specified , + /// and . /// /// /// A that contains a local IP address. /// /// - /// An that contains a port number. + /// An that contains a port number. /// /// - /// A that indicates providing a secure connection or not. (true indicates providing a secure connection.) + /// A that indicates providing a secure connection or not. + /// (true indicates providing a secure connection.) /// - public WebSocketServer(System.Net.IPAddress address, int port, bool secure) - : base(address, port, "/", secure) + public WebSocketServer (System.Net.IPAddress address, int port, bool secure) + : base (address, port, "/", secure) { - _svcHosts = new ServiceHostManager(); + _serviceHosts = new ServiceHostManager (Log); } #endregion #region Public Properties + /// + /// Gets the connection count to the . + /// + /// + /// An that contains the connection count. + /// + public int ConnectionCount { + get { + return _serviceHosts.ConnectionCount; + } + } + /// /// Gets or sets a value indicating whether the server cleans up the inactive WebSocket service /// instances periodically. @@ -154,11 +167,11 @@ namespace WebSocketSharp.Server { /// public bool KeepClean { get { - return _svcHosts.KeepClean; + return _serviceHosts.KeepClean; } set { - _svcHosts.KeepClean = value; + _serviceHosts.KeepClean = value; } } @@ -171,16 +184,36 @@ namespace WebSocketSharp.Server { public IEnumerable ServicePaths { get { var url = BaseUri.IsAbsoluteUri - ? BaseUri.ToString().TrimEnd('/') + ? BaseUri.ToString ().TrimEnd ('/') : String.Empty; - foreach (var path in _svcHosts.Paths) + foreach (var path in _serviceHosts.Paths) yield return url + path; } } #endregion + #region Private Methods + + private void stop (ushort code, string reason) + { + var data = code.Append (reason); + if (data.Length > 125) + { + var msg = "The payload length of a Close frame must be 125 bytes or less."; + Log.Error (String.Format ("{0}\ncode: {1}\nreason: {2}", msg, code, reason)); + Error (msg); + + return; + } + + base.Stop (); + _serviceHosts.Stop (code, reason); + } + + #endregion + #region Protected Methods /// @@ -189,23 +222,23 @@ namespace WebSocketSharp.Server { /// /// A that contains the WebSocket connection request objects. /// - protected override void AcceptWebSocket(TcpListenerWebSocketContext context) + protected override void AcceptWebSocket (TcpListenerWebSocketContext context) { var ws = context.WebSocket; - var path = context.Path.UrlDecode(); + var path = context.Path.UrlDecode (); ws.Log = Log; - IServiceHost svcHost; - if (!_svcHosts.TryGetServiceHost(path, out svcHost)) + IServiceHost host; + if (!_serviceHosts.TryGetServiceHost (path, out host)) { - ws.Close(HttpStatusCode.NotImplemented); + ws.Close (HttpStatusCode.NotImplemented); return; } if (BaseUri.IsAbsoluteUri) - ws.Url = new Uri(BaseUri, path); + ws.Url = new Uri (BaseUri, path); - svcHost.BindWebSocket(context); + host.BindWebSocket (context); } #endregion @@ -213,35 +246,55 @@ namespace WebSocketSharp.Server { #region Public Methods /// - /// Adds the specified type WebSocket service. + /// Adds the specified typed WebSocket service. /// - /// - /// A that contains an absolute path associated with the WebSocket service. + /// + /// A that contains an absolute path to the WebSocket service. /// /// /// The type of the WebSocket service. The T must inherit the class. /// - public void AddWebSocketService(string absPath) - where T : WebSocketService, new() + public void AddWebSocketService (string servicePath) + where T : WebSocketService, new () { string msg; - if (!absPath.IsValidAbsolutePath(out msg)) + if (!servicePath.IsValidAbsolutePath (out msg)) { - Log.Error(msg); - Error(msg); + Log.Error (msg); + Error (msg); return; } - var svcHost = new WebSocketServiceHost(Log); - svcHost.Uri = BaseUri.IsAbsoluteUri - ? new Uri(BaseUri, absPath) - : absPath.ToUri(); + var host = new WebSocketServiceHost (Log); + host.Uri = BaseUri.IsAbsoluteUri + ? new Uri (BaseUri, servicePath) + : servicePath.ToUri (); if (!KeepClean) - svcHost.KeepClean = false; + host.KeepClean = false; - _svcHosts.Add(absPath, svcHost); + _serviceHosts.Add (servicePath, host); + } + + /// + /// Broadcasts the specified array of to all clients. + /// + /// + /// An array of to broadcast. + /// + public void Broadcast (byte [] data) + { + if (data == null) + { + var msg = "'data' must not be null."; + Log.Error (msg); + Error (msg); + + return; + } + + _serviceHosts.Broadcast (data); } /// @@ -250,18 +303,358 @@ namespace WebSocketSharp.Server { /// /// A to broadcast. /// - public void Broadcast(string data) + public void Broadcast (string data) { - _svcHosts.Broadcast(data); + if (data == null) + { + var msg = "'data' must not be null."; + Log.Error (msg); + Error (msg); + + return; + } + + _serviceHosts.Broadcast (data); + } + + /// + /// Broadcasts the specified array of to all clients of the WebSocket service + /// with the specified . + /// + /// + /// true if the WebSocket service is found; otherwise, false. + /// + /// + /// A that contains an absolute path to the WebSocket service to find. + /// + /// + /// An array of to broadcast. + /// + public bool BroadcastTo (string servicePath, byte [] data) + { + var msg = servicePath.IsNullOrEmpty () + ? "'servicePath' must not be null or empty." + : data == null + ? "'data' must not be null." + : String.Empty; + + if (msg.Length > 0) + { + Log.Error (msg); + Error (msg); + + return false; + } + + return _serviceHosts.BroadcastTo (servicePath, data); + } + + /// + /// Broadcasts the specified to all clients of the WebSocket service + /// with the specified . + /// + /// + /// true if the WebSocket service is found; otherwise, false. + /// + /// + /// A that contains an absolute path to the WebSocket service to find. + /// + /// + /// A to broadcast. + /// + public bool BroadcastTo (string servicePath, string data) + { + var msg = servicePath.IsNullOrEmpty () + ? "'servicePath' must not be null or empty." + : data == null + ? "'data' must not be null." + : String.Empty; + + if (msg.Length > 0) + { + Log.Error (msg); + Error (msg); + + return false; + } + + return _serviceHosts.BroadcastTo (servicePath, data); + } + + /// + /// Sends Pings with the specified to all clients. + /// + /// + /// A Dictionary<string, Dictionary<string, bool>> that contains the collection of + /// service paths and pairs of ID and value indicating whether the + /// received the Pongs from each clients in a time. + /// + /// + /// A that contains a message to send. + /// + public Dictionary> Broadping (string message) + { + if (message.IsNullOrEmpty ()) + return _serviceHosts.Broadping (String.Empty); + + var len = Encoding.UTF8.GetBytes (message).Length; + if (len > 125) + { + var msg = "The payload length of a Ping frame must be 125 bytes or less."; + Log.Error (msg); + Error (msg); + + return null; + } + + return _serviceHosts.Broadping (message); + } + + /// + /// Sends Pings with the specified to all clients of the WebSocket service + /// with the specified . + /// + /// + /// A Dictionary<string, bool> that contains the collection of session IDs and values + /// indicating whether the received the Pongs from each clients + /// in a time. If the WebSocket service is not found, returns . + /// + /// + /// A that contains an absolute path to the WebSocket service to find. + /// + /// + /// A that contains a message to send. + /// + public Dictionary BroadpingTo (string servicePath, string message) + { + if (message == null) + message = String.Empty; + + var msg = servicePath.IsNullOrEmpty () + ? "'servicePath' must not be null or empty." + : Encoding.UTF8.GetBytes (message).Length > 125 + ? "The payload length of a Ping frame must be 125 bytes or less." + : String.Empty; + + if (msg.Length > 0) + { + Log.Error (msg); + Error (msg); + + return null; + } + + return _serviceHosts.BroadpingTo (servicePath, message); + } + + /// + /// Gets the connection count to the WebSocket service with the specified . + /// + /// + /// An that contains the connection count if the WebSocket service is successfully found; + /// otherwise, -1. + /// + /// + /// A that contains an absolute path to the WebSocket service to find. + /// + public int GetConnectionCount (string servicePath) + { + if (servicePath.IsNullOrEmpty ()) + { + var msg = "'servicePath' must not be null or empty."; + Log.Error (msg); + Error (msg); + + return -1; + } + + return _serviceHosts.GetConnectionCount (servicePath); + } + + /// + /// Sends a Ping with the specified to the client associated with + /// the specified and . + /// + /// + /// true if the receives a Pong from the client in a time; + /// otherwise, false. + /// + /// + /// A that contains an absolute path to the WebSocket service to find. + /// + /// + /// A that contains an ID that represents the destination for the Ping. + /// + /// + /// A that contains a message to send. + /// + public bool PingTo (string servicePath, string id, string message) + { + if (message == null) + message = String.Empty; + + var msg = servicePath.IsNullOrEmpty () + ? "'servicePath' must not be null or empty." + : id.IsNullOrEmpty () + ? "'id' must not be null or empty." + : Encoding.UTF8.GetBytes (message).Length > 125 + ? "The payload length of a Ping frame must be 125 bytes or less." + : String.Empty; + + if (msg.Length > 0) + { + Log.Error (msg); + Error (msg); + + return false; + } + + return _serviceHosts.PingTo (servicePath, id, message); + } + + /// + /// Removes the WebSocket service with the specified . + /// + /// + /// true if the WebSocket service is successfully found and removed; otherwise, false. + /// + /// + /// A that contains an absolute path to the WebSocket service to find. + /// + public bool RemoveWebSocketService (string servicePath) + { + if (servicePath.IsNullOrEmpty ()) + { + var msg = "'servicePath' must not be null or empty."; + Log.Error (msg); + Error (msg); + + return false; + } + + return _serviceHosts.Remove (servicePath); + } + + /// + /// Sends a binary data to the client associated with the specified and + /// . + /// + /// + /// true if the client is successfully found; otherwise, false. + /// + /// + /// A that contains an absolute path to the WebSocket service to find. + /// + /// + /// A that contains an ID that represents the destination for the data. + /// + /// + /// An array of that contains a binary data to send. + /// + public bool SendTo (string servicePath, string id, byte [] data) + { + var msg = servicePath.IsNullOrEmpty () + ? "'servicePath' must not be null or empty." + : id.IsNullOrEmpty () + ? "'id' must not be null or empty." + : data == null + ? "'data' must not be null." + : String.Empty; + + if (msg.Length > 0) + { + Log.Error (msg); + Error (msg); + + return false; + } + + return _serviceHosts.SendTo (servicePath, id, data); + } + + /// + /// Sends a text data to the client associated with the specified and + /// . + /// + /// + /// true if the client is successfully found; otherwise, false. + /// + /// + /// A that contains an absolute path to the WebSocket service to find. + /// + /// + /// A that contains an ID that represents the destination for the data. + /// + /// + /// A that contains a text data to send. + /// + public bool SendTo (string servicePath, string id, string data) + { + var msg = servicePath.IsNullOrEmpty () + ? "'servicePath' must not be null or empty." + : id.IsNullOrEmpty () + ? "'id' must not be null or empty." + : data == null + ? "'data' must not be null." + : String.Empty; + + if (msg.Length > 0) + { + Log.Error (msg); + Error (msg); + + return false; + } + + return _serviceHosts.SendTo (servicePath, id, data); } /// /// Stops receiving the WebSocket connection requests. /// - public override void Stop() + public override void Stop () { - base.Stop(); - _svcHosts.Stop(); + base.Stop (); + _serviceHosts.Stop (); + } + + /// + /// Stops receiving the WebSocket connection requests with the specified and + /// . + /// + /// + /// A that contains a status code indicating the reason for stop. + /// + /// + /// A that contains the reason for stop. + /// + public void Stop (ushort code, string reason) + { + if (!code.IsCloseStatusCode ()) + { + var msg = "Invalid status code for stop."; + Log.Error (String.Format ("{0}\ncode: {1}", msg, code)); + Error (msg); + + return; + } + + stop (code, reason); + } + + /// + /// Stops receiving the WebSocket connection requests with the specified + /// and . + /// + /// + /// A that contains a status code indicating the reason for stop. + /// + /// + /// A that contains the reason for stop. + /// + public void Stop (CloseStatusCode code, string reason) + { + stop ((ushort) code, reason); } #endregion diff --git a/websocket-sharp/Server/WebSocketService.cs b/websocket-sharp/Server/WebSocketService.cs index e55d2b60..730ecf27 100644 --- a/websocket-sharp/Server/WebSocketService.cs +++ b/websocket-sharp/Server/WebSocketService.cs @@ -29,6 +29,7 @@ using System; using System.Collections.Generic; using System.Collections.Specialized; +using System.Text; using System.Threading; using WebSocketSharp.Net; using WebSocketSharp.Net.WebSockets; @@ -94,10 +95,8 @@ namespace WebSocketSharp.Server } set { - if (!IsBound) - return; - - _websocket.Log = value; + if (IsBound) + _websocket.Log = value; } } @@ -217,6 +216,18 @@ namespace WebSocketSharp.Server #region Protected Methods + /// + /// Calls the method with the specified . + /// + /// + /// A that contains an error message. + /// + protected virtual void Error (string message) + { + if (!message.IsNullOrEmpty ()) + OnError (new ErrorEventArgs (message)); + } + /// /// Is called when the WebSocket connection has been closed. /// @@ -229,7 +240,8 @@ namespace WebSocketSharp.Server } /// - /// Is called when the inner gets an error. + /// Is called when the inner or current + /// gets an error. /// /// /// An that contains an event data associated with @@ -285,29 +297,51 @@ namespace WebSocketSharp.Server #region Public Methods /// - /// Broadcasts the specified array of to the clients of - /// every instances in the . + /// Broadcasts the specified array of to the clients of every + /// instances in the . /// /// /// An array of to broadcast. /// - public void Broadcast (byte [] data) + public virtual void Broadcast (byte [] data) { - if (IsBound) - _sessions.Broadcast (data); + if (!IsBound) + return; + + if (data == null) + { + var msg = "'data' must not be null."; + Log.Error (msg); + Error (msg); + + return; + } + + _sessions.Broadcast (data); } /// - /// Broadcasts the specified to the clients of - /// every instances in the . + /// Broadcasts the specified to the clients of every + /// instances in the . /// /// /// A to broadcast. /// - public void Broadcast (string data) + public virtual void Broadcast (string data) { - if (IsBound) - _sessions.Broadcast (data); + if (!IsBound) + return; + + if (data == null) + { + var msg = "'data' must not be null."; + Log.Error (msg); + Error (msg); + + return; + } + + _sessions.Broadcast (data); } /// @@ -315,17 +349,19 @@ namespace WebSocketSharp.Server /// in the . /// /// - /// A Dictionary<string, bool> that contains the collection of IDs and values - /// indicating whether the each instances received a Pong in a time. + /// A Dictionary<string, bool> that contains the collection of IDs and values indicating + /// whether the each instances received a Pong in a time. /// - public Dictionary Broadping () + public virtual Dictionary Broadping () { - return Broadping (String.Empty); + return IsBound + ? _sessions.Broadping (String.Empty) + : null; } /// - /// Sends Pings with the specified to the clients of - /// every instances in the . + /// Sends Pings with the specified to the clients of every + /// instances in the . /// /// /// A Dictionary<string, bool> that contains the collection of IDs and values @@ -334,11 +370,25 @@ namespace WebSocketSharp.Server /// /// A that contains a message to send. /// - public Dictionary Broadping (string message) + public virtual Dictionary Broadping (string message) { - return IsBound - ? _sessions.Broadping (message) - : null; + if (!IsBound) + return null; + + if (message.IsNullOrEmpty ()) + return _sessions.Broadping (String.Empty); + + var len = Encoding.UTF8.GetBytes (message).Length; + if (len > 125) + { + var msg = "The payload length of a Ping frame must be 125 bytes or less."; + Log.Error (msg); + Error (msg); + + return null; + } + + return _sessions.Broadping (message); } /// @@ -348,9 +398,11 @@ namespace WebSocketSharp.Server /// true if the current instance receives a Pong in a time; /// otherwise, false. /// - public bool Ping () + public virtual bool Ping () { - return Ping (String.Empty); + return IsBound + ? _websocket.Ping () + : false; } /// @@ -364,7 +416,7 @@ namespace WebSocketSharp.Server /// /// A that contains a message to send. /// - public bool Ping (string message) + public virtual bool Ping (string message) { return IsBound ? _websocket.Ping (message) @@ -373,7 +425,7 @@ namespace WebSocketSharp.Server /// /// Sends a Ping to the client of the instance - /// associated with the specified . + /// with the specified ID. /// /// /// true if the instance receives a Pong in a time; @@ -382,14 +434,14 @@ namespace WebSocketSharp.Server /// /// A that contains an ID that represents the destination for the Ping. /// - public bool PingTo (string id) + public virtual bool PingTo (string id) { return PingTo (id, String.Empty); } /// - /// Sends a Ping with the specified to the client of - /// the instance associated with the specified . + /// Sends a Ping with the specified to the client of the + /// instance with the specified ID. /// /// /// true if the instance receives a Pong in a time; @@ -401,15 +453,29 @@ namespace WebSocketSharp.Server /// /// A that contains a message to send. /// - public bool PingTo (string id, string message) + public virtual bool PingTo (string id, string message) { if (!IsBound) return false; - WebSocketService service; - return _sessions.TryGetWebSocketService (id, out service) - ? service.Ping (message) - : false; + if (message == null) + message = String.Empty; + + var msg = id.IsNullOrEmpty () + ? "'id' must not be null or empty." + : Encoding.UTF8.GetBytes (message).Length > 125 + ? "The payload length of a Ping frame must be 125 bytes or less." + : String.Empty; + + if (msg.Length > 0) + { + Log.Error (msg); + Error (msg); + + return false; + } + + return _sessions.PingTo (id, message); } /// @@ -418,7 +484,7 @@ namespace WebSocketSharp.Server /// /// An array of that contains a binary data to send. /// - public void Send (byte [] data) + public virtual void Send (byte [] data) { if (IsBound) _websocket.Send (data); @@ -430,7 +496,7 @@ namespace WebSocketSharp.Server /// /// A that contains a text data to send. /// - public void Send (string data) + public virtual void Send (string data) { if (IsBound) _websocket.Send (data); @@ -438,42 +504,72 @@ namespace WebSocketSharp.Server /// /// Sends a binary data to the client of the instance - /// associated with the specified . + /// with the specified ID. /// + /// + /// true if the client is successfully found; otherwise, false. + /// /// /// A that contains an ID that represents the destination for the data. /// /// /// An array of that contains a binary data to send. /// - public void SendTo (string id, byte [] data) + public virtual bool SendTo (string id, byte [] data) { if (!IsBound) - return; + return false; - WebSocketService service; - if (_sessions.TryGetWebSocketService (id, out service)) - service.Send (data); + var msg = id.IsNullOrEmpty () + ? "'id' must not be null or empty." + : data == null + ? "'data' must not be null." + : String.Empty; + + if (msg.Length > 0) + { + Log.Error (msg); + Error (msg); + + return false; + } + + return _sessions.SendTo (id, data); } /// /// Sends a text data to the client of the instance - /// associated with the specified . + /// with the specified ID. /// + /// + /// true if the client is successfully found; otherwise, false. + /// /// /// A that contains an ID that represents the destination for the data. /// /// /// A that contains a text data to send. /// - public void SendTo (string id, string data) + public virtual bool SendTo (string id, string data) { if (!IsBound) - return; + return false; - WebSocketService service; - if (_sessions.TryGetWebSocketService (id, out service)) - service.Send (data); + var msg = id.IsNullOrEmpty () + ? "'id' must not be null or empty." + : data == null + ? "'data' must not be null." + : String.Empty; + + if (msg.Length > 0) + { + Log.Error (msg); + Error (msg); + + return false; + } + + return _sessions.SendTo (id, data); } /// @@ -490,10 +586,8 @@ namespace WebSocketSharp.Server /// public void Stop () { - if (!IsBound) - return; - - _websocket.Close (); + if (IsBound) + _websocket.Close (); } /// @@ -508,10 +602,8 @@ namespace WebSocketSharp.Server /// public void Stop (ushort code, string reason) { - if (!IsBound) - return; - - _websocket.Close (code, reason); + if (IsBound) + _websocket.Close (code, reason); } /// @@ -527,7 +619,8 @@ namespace WebSocketSharp.Server /// public void Stop (CloseStatusCode code, string reason) { - Stop ((ushort) code, reason); + if (IsBound) + _websocket.Close (code, reason); } #endregion diff --git a/websocket-sharp/Server/WebSocketServiceHost.cs b/websocket-sharp/Server/WebSocketServiceHost.cs index 92b2db45..6dbefe98 100644 --- a/websocket-sharp/Server/WebSocketServiceHost.cs +++ b/websocket-sharp/Server/WebSocketServiceHost.cs @@ -31,11 +31,12 @@ using System; using System.Collections.Generic; using System.Net.Sockets; +using System.Text; using WebSocketSharp.Net; using WebSocketSharp.Net.WebSockets; -namespace WebSocketSharp.Server { - +namespace WebSocketSharp.Server +{ /// /// Provides the functions of the server that receives the WebSocket connection requests. /// @@ -43,10 +44,11 @@ namespace WebSocketSharp.Server { /// The WebSocketServiceHost<T> class provides the single WebSocket service. /// /// - /// The type of the WebSocket service that the server provides. The T must inherit the class. + /// The type of the WebSocket service that the server provides. + /// The T must inherit the class. /// public class WebSocketServiceHost : WebSocketServerBase, IServiceHost - where T : WebSocketService, new() + where T : WebSocketService, new () { #region Private Fields @@ -56,10 +58,10 @@ namespace WebSocketSharp.Server { #region Internal Constructors - internal WebSocketServiceHost(Logger logger) - : base(logger) + internal WebSocketServiceHost (Logger logger) + : base (logger) { - _sessions = new WebSocketServiceManager(); + _sessions = new WebSocketServiceManager (logger); } #endregion @@ -67,116 +69,122 @@ namespace WebSocketSharp.Server { #region Public Constructors /// - /// Initializes a new instance of the WebSocketServiceHost<T> class that listens for incoming connection attempts - /// on the specified . + /// Initializes a new instance of the WebSocketServiceHost<T> class that listens for + /// incoming connection attempts on the specified . /// - /// + /// /// An that contains a port number. /// - public WebSocketServiceHost(int port) - : this(port, "/") + public WebSocketServiceHost (int port) + : this (port, "/") { } /// - /// Initializes a new instance of the WebSocketServiceHost<T> class that listens for incoming connection attempts - /// on the specified WebSocket URL. + /// Initializes a new instance of the WebSocketServiceHost<T> class that listens for + /// incoming connection attempts on the specified WebSocket URL. /// /// /// A that contains a WebSocket URL. /// - public WebSocketServiceHost(string url) - : base(url) + public WebSocketServiceHost (string url) + : base (url) { - _sessions = new WebSocketServiceManager(); + _sessions = new WebSocketServiceManager (Log); } /// - /// Initializes a new instance of the WebSocketServiceHost<T> class that listens for incoming connection attempts - /// on the specified and . + /// Initializes a new instance of the WebSocketServiceHost<T> class that listens for + /// incoming connection attempts on the specified and . /// /// - /// An that contains a port number. + /// An that contains a port number. /// /// - /// A that indicates providing a secure connection or not. (true indicates providing a secure connection.) + /// A that indicates providing a secure connection or not. + /// (true indicates providing a secure connection.) /// - public WebSocketServiceHost(int port, bool secure) - : this(port, "/", secure) + public WebSocketServiceHost (int port, bool secure) + : this (port, "/", secure) { } /// - /// Initializes a new instance of the WebSocketServiceHost<T> class that listens for incoming connection attempts - /// on the specified and . + /// Initializes a new instance of the WebSocketServiceHost<T> class that listens for + /// incoming connection attempts on the specified and . /// /// - /// An that contains a port number. + /// An that contains a port number. /// /// /// A that contains an absolute path. /// - public WebSocketServiceHost(int port, string absPath) - : this(System.Net.IPAddress.Any, port, absPath) + public WebSocketServiceHost (int port, string absPath) + : this (System.Net.IPAddress.Any, port, absPath) { } /// - /// Initializes a new instance of the WebSocketServiceHost<T> class that listens for incoming connection attempts - /// on the specified , and . + /// Initializes a new instance of the WebSocketServiceHost<T> class that listens for + /// incoming connection attempts on the specified , + /// and . /// /// - /// An that contains a port number. + /// An that contains a port number. /// /// /// A that contains an absolute path. /// /// - /// A that indicates providing a secure connection or not. (true indicates providing a secure connection.) + /// A that indicates providing a secure connection or not. + /// (true indicates providing a secure connection.) /// - public WebSocketServiceHost(int port, string absPath, bool secure) - : this(System.Net.IPAddress.Any, port, absPath, secure) + public WebSocketServiceHost (int port, string absPath, bool secure) + : this (System.Net.IPAddress.Any, port, absPath, secure) { } /// - /// Initializes a new instance of the WebSocketServiceHost<T> class that listens for incoming connection attempts - /// on the specified , and . + /// Initializes a new instance of the WebSocketServiceHost<T> class that listens for + /// incoming connection attempts on the specified , + /// and . /// /// /// A that contains a local IP address. /// /// - /// An that contains a port number. + /// An that contains a port number. /// /// /// A that contains an absolute path. /// - public WebSocketServiceHost(System.Net.IPAddress address, int port, string absPath) - : this(address, port, absPath, port == 443 ? true : false) + public WebSocketServiceHost (System.Net.IPAddress address, int port, string absPath) + : this (address, port, absPath, port == 443 ? true : false) { } /// - /// Initializes a new instance of the WebSocketServiceHost<T> class that listens for incoming connection attempts - /// on the specified , , and . + /// Initializes a new instance of the WebSocketServiceHost<T> class that listens for + /// incoming connection attempts on the specified , , + /// and . /// /// /// A that contains a local IP address. /// /// - /// An that contains a port number. + /// An that contains a port number. /// /// /// A that contains an absolute path. /// /// - /// A that indicates providing a secure connection or not. (true indicates providing a secure connection.) + /// A that indicates providing a secure connection or not. + /// (true indicates providing a secure connection.) /// - public WebSocketServiceHost(System.Net.IPAddress address, int port, string absPath, bool secure) - : base(address, port, absPath, secure) + public WebSocketServiceHost (System.Net.IPAddress address, int port, string absPath, bool secure) + : base (address, port, absPath, secure) { - _sessions = new WebSocketServiceManager(); + _sessions = new WebSocketServiceManager (Log); } #endregion @@ -184,11 +192,23 @@ namespace WebSocketSharp.Server { #region Public Properties /// - /// Gets or sets a value indicating whether the server cleans up the inactive WebSocket service - /// instances periodically. + /// Gets the connection count to the WebSocket service host. /// /// - /// true if the server cleans up the inactive WebSocket service instances every 60 seconds; + /// An that contains the connection count. + /// + public int ConnectionCount { + get { + return _sessions.Count; + } + } + + /// + /// Gets or sets a value indicating whether the WebSocket service host cleans up + /// the inactive instances periodically. + /// + /// + /// true if the WebSocket service host cleans up the inactive WebSocket service instances every 60 seconds; /// otherwise, false. The default value is true. /// public bool KeepClean { @@ -219,6 +239,26 @@ namespace WebSocketSharp.Server { #endregion + #region Private Methods + + private void stop (ushort code, string reason) + { + var data = code.Append (reason); + if (data.Length > 125) + { + var msg = "The payload length of a Close frame must be 125 bytes or less."; + Log.Error (String.Format ("{0}\ncode: {1}\nreason: {2}", msg, code, reason)); + Error (msg); + + return; + } + + base.Stop (); + _sessions.Stop (code, reason); + } + + #endregion + #region Protected Methods /// @@ -227,61 +267,241 @@ namespace WebSocketSharp.Server { /// /// A that contains the WebSocket connection request objects. /// - protected override void AcceptWebSocket(TcpListenerWebSocketContext context) + protected override void AcceptWebSocket (TcpListenerWebSocketContext context) { var ws = context.WebSocket; - var path = context.Path.UrlDecode(); + var path = context.Path.UrlDecode (); ws.Log = Log; - if (path != Uri.GetAbsolutePath().UrlDecode()) + if (path != Uri.GetAbsolutePath ().UrlDecode ()) { - ws.Close(HttpStatusCode.NotImplemented); + ws.Close (HttpStatusCode.NotImplemented); return; } if (Uri.IsAbsoluteUri) ws.Url = Uri; - ((IServiceHost)this).BindWebSocket(context); + ((IServiceHost) this).BindWebSocket (context); } #endregion #region Public Methods + /// + /// Broadcasts the specified array of to all clients. + /// + /// + /// An array of to broadcast. + /// + public void Broadcast (byte [] data) + { + if (data == null) + { + var msg = "'data' must not be null."; + Log.Error (msg); + Error (msg); + + return; + } + + _sessions.Broadcast (data); + } + /// /// Broadcasts the specified to all clients. /// /// /// A to broadcast. /// - public void Broadcast(string data) + public void Broadcast (string data) { - _sessions.Broadcast(data); + if (data == null) + { + var msg = "'data' must not be null."; + Log.Error (msg); + Error (msg); + + return; + } + + _sessions.Broadcast (data); } /// - /// Pings with the specified to all clients. + /// Sends Pings with the specified to all clients. /// /// /// A Dictionary<string, bool> that contains the collection of session IDs and values - /// indicating whether the server received the Pongs from each clients in a time. + /// indicating whether the service host received the Pongs from each clients in a time. /// /// - /// A that contains a message. + /// A that contains a message to send. /// - public Dictionary Broadping(string message) + public Dictionary Broadping (string message) { - return _sessions.Broadping(message); + if (message.IsNullOrEmpty ()) + return _sessions.Broadping (String.Empty); + + var len = Encoding.UTF8.GetBytes (message).Length; + if (len > 125) + { + var msg = "The payload length of a Ping frame must be 125 bytes or less."; + Log.Error (msg); + Error (msg); + + return null; + } + + return _sessions.Broadping (message); + } + + /// + /// Sends a Ping with the specified to the client associated with + /// the specified ID. + /// + /// + /// true if the service host receives a Pong from the client in a time; otherwise, false. + /// + /// + /// A that contains an ID that represents the destination for the Ping. + /// + /// + /// A that contains a message to send. + /// + public bool PingTo (string id, string message) + { + if (message == null) + message = String.Empty; + + var msg = id.IsNullOrEmpty () + ? "'id' must not be null or empty." + : Encoding.UTF8.GetBytes (message).Length > 125 + ? "The payload length of a Ping frame must be 125 bytes or less." + : String.Empty; + + if (msg.Length > 0) + { + Log.Error (msg); + Error (msg); + + return false; + } + + return _sessions.PingTo (id, message); + } + + /// + /// Sends a binary data to the client associated with the specified ID. + /// + /// + /// true if the client associated with is successfully found; + /// otherwise, false. + /// + /// + /// A that contains an ID that represents the destination for the data. + /// + /// + /// An array of that contains a binary data to send. + /// + public bool SendTo (string id, byte [] data) + { + var msg = id.IsNullOrEmpty () + ? "'id' must not be null or empty." + : data == null + ? "'data' must not be null." + : String.Empty; + + if (msg.Length > 0) + { + Log.Error (msg); + Error (msg); + + return false; + } + + return _sessions.SendTo (id, data); + } + + /// + /// Sends a text data to the client associated with the specified ID. + /// + /// + /// true if the client associated with is successfully found; + /// otherwise, false. + /// + /// + /// A that contains an ID that represents the destination for the data. + /// + /// + /// A that contains a text data to send. + /// + public bool SendTo (string id, string data) + { + var msg = id.IsNullOrEmpty () + ? "'id' must not be null or empty." + : data == null + ? "'data' must not be null." + : String.Empty; + + if (msg.Length > 0) + { + Log.Error (msg); + Error (msg); + + return false; + } + + return _sessions.SendTo (id, data); } /// /// Stops receiving the WebSocket connection requests. /// - public override void Stop() + public override void Stop () { - base.Stop(); - _sessions.Stop(); + base.Stop (); + _sessions.Stop (); + } + + /// + /// Stops receiving the WebSocket connection requests with the specified and + /// . + /// + /// + /// A that contains a status code indicating the reason for stop. + /// + /// + /// A that contains the reason for stop. + /// + public void Stop (ushort code, string reason) + { + if (!code.IsCloseStatusCode ()) + { + var msg = "Invalid status code for stop."; + Log.Error (String.Format ("{0}\ncode: {1}", msg, code)); + Error (msg); + + return; + } + + stop (code, reason); + } + + /// + /// Stops receiving the WebSocket connection requests with the specified + /// and . + /// + /// + /// A that contains a status code indicating the reason for stop. + /// + /// + /// A that contains the reason for stop. + /// + public void Stop (CloseStatusCode code, string reason) + { + stop ((ushort) code, reason); } #endregion @@ -294,11 +514,118 @@ namespace WebSocketSharp.Server { /// /// A that contains the WebSocket connection request objects to bind. /// - void IServiceHost.BindWebSocket(WebSocketContext context) + void IServiceHost.BindWebSocket (WebSocketContext context) { - T service = new T(); - service.Bind(context, _sessions); - service.Start(); + T service = new T (); + service.Bind (context, _sessions); + service.Start (); + } + + /// + /// Broadcasts the specified array of to all clients. + /// + /// + /// An array of to broadcast. + /// + void IServiceHost.Broadcast (byte [] data) + { + _sessions.Broadcast (data); + } + + /// + /// Broadcasts the specified to all clients. + /// + /// + /// A to broadcast. + /// + void IServiceHost.Broadcast (string data) + { + _sessions.Broadcast (data); + } + + /// + /// Sends Pings with the specified to all clients. + /// + /// + /// A Dictionary<string, bool> that contains the collection of session IDs and values + /// indicating whether the service host received the Pongs from each clients in a time. + /// + /// + /// A that contains a message to send. + /// + Dictionary IServiceHost.Broadping (string message) + { + return _sessions.Broadping (message); + } + + /// + /// Sends a Ping with the specified to the client associated with + /// the specified ID. + /// + /// + /// true if the service host receives a Pong from the client in a time; otherwise, false. + /// + /// + /// A that contains an ID that represents the destination for the Ping. + /// + /// + /// A that contains a message to send. + /// + bool IServiceHost.PingTo (string id, string message) + { + return _sessions.PingTo (id, message); + } + + /// + /// Sends a binary data to the client associated with the specified ID. + /// + /// + /// true if the client associated with is successfully found; + /// otherwise, false. + /// + /// + /// A that contains an ID that represents the destination for the data. + /// + /// + /// An array of that contains a binary data to send. + /// + bool IServiceHost.SendTo (string id, byte [] data) + { + return _sessions.SendTo (id, data); + } + + /// + /// Sends a text data to the client associated with the specified ID. + /// + /// + /// true if the client associated with is successfully found; + /// otherwise, false. + /// + /// + /// A that contains an ID that represents the destination for the data. + /// + /// + /// A that contains a text data to send. + /// + bool IServiceHost.SendTo (string id, string data) + { + return _sessions.SendTo (id, data); + } + + /// + /// Stops receiving the WebSocket connection requests with the specified and + /// . + /// + /// + /// A that contains a status code indicating the reason for stop. + /// + /// + /// A that contains the reason for stop. + /// + void IServiceHost.Stop (ushort code, string reason) + { + base.Stop (); + _sessions.Stop (code, reason); } #endregion diff --git a/websocket-sharp/Server/WebSocketServiceManager.cs b/websocket-sharp/Server/WebSocketServiceManager.cs index 6f23ad28..5e00e287 100644 --- a/websocket-sharp/Server/WebSocketServiceManager.cs +++ b/websocket-sharp/Server/WebSocketServiceManager.cs @@ -41,6 +41,7 @@ namespace WebSocketSharp.Server #region Private Fields private object _forSweep; + private Logger _logger; private Dictionary _services; private volatile bool _stopped; private volatile bool _sweeping; @@ -52,7 +53,13 @@ namespace WebSocketSharp.Server #region Internal Constructors internal WebSocketServiceManager () + : this (new Logger ()) { + } + + internal WebSocketServiceManager (Logger logger) + { + _logger = logger; _forSweep = new object (); _services = new Dictionary (); _stopped = false; @@ -133,6 +140,38 @@ namespace WebSocketSharp.Server } } + /// + /// Gets the instance with the specified ID + /// from the . + /// + /// + /// A instance with if it is successfully found; + /// otherwise, . + /// + /// + /// A that contains an ID to find. + /// + public WebSocketService this [string id] { + get { + if (id.IsNullOrEmpty ()) + { + _logger.Error ("'id' must not be null or empty."); + return null; + } + + lock (_sync) + { + try { + return _services [id]; + } + catch { + _logger.Error ("'id' not found.\nid: " + id); + return null; + } + } + } + } + /// /// Gets a value indicating whether the cleans up /// the inactive instances periodically. @@ -157,26 +196,37 @@ namespace WebSocketSharp.Server } } + /// + /// Gets the collection of the instances + /// managed by the . + /// + /// + /// An IEnumerable<WebSocketService> that contains the collection of + /// the instances. + /// + public IEnumerable ServiceInstances { + get { + lock (_sync) + { + return _services.Values; + } + } + } + #endregion #region Private Methods private void broadcast (byte [] data) { - lock (_sync) - { - foreach (var service in _services.Values) - service.Send (data); - } + foreach (var service in ServiceInstances) + service.Send (data); } private void broadcast (string data) { - lock (_sync) - { - foreach (var service in _services.Values) - service.Send (data); - } + foreach (var service in ServiceInstances) + service.Send (data); } private void broadcastAsync (byte [] data) @@ -280,33 +330,6 @@ namespace WebSocketSharp.Server } } - internal bool Remove (string id) - { - lock (_sync) - { - return _services.Remove (id); - } - } - - internal void Stop () - { - stop (0, null, true); - } - - internal void Stop (ushort code, string reason) - { - stop (code, reason, false); - } - - internal void Stop (CloseStatusCode code, string reason) - { - Stop ((ushort) code, reason); - } - - #endregion - - #region Public Methods - /// /// Broadcasts the specified array of to the clients of every /// instances managed by the . @@ -314,7 +337,7 @@ namespace WebSocketSharp.Server /// /// An array of to broadcast. /// - public void Broadcast (byte [] data) + internal void Broadcast (byte [] data) { if (_stopped) broadcast (data); @@ -329,7 +352,7 @@ namespace WebSocketSharp.Server /// /// A to broadcast. /// - public void Broadcast (string data) + internal void Broadcast (string data) { if (_stopped) broadcast (data); @@ -348,7 +371,7 @@ namespace WebSocketSharp.Server /// /// A that contains a message to send. /// - public Dictionary Broadping (string message) + internal Dictionary Broadping (string message) { var result = new Dictionary (); foreach (var session in copy ()) @@ -357,6 +380,109 @@ namespace WebSocketSharp.Server return result; } + /// + /// Sends a Ping with the specified to the client of the + /// instance with the specified ID. + /// + /// + /// true if the instance receives a Pong in a time; + /// otherwise, false. + /// + /// + /// A that contains an ID that represents the destination for the Ping. + /// + /// + /// A that contains a message to send. + /// + internal bool PingTo (string id, string message) + { + WebSocketService service; + if (TryGetServiceInstance (id, out service)) + return service.Ping (message); + + _logger.Error ( + "The WebSocket service instance with the specified ID not found.\nID: " + id); + return false; + } + + internal bool Remove (string id) + { + lock (_sync) + { + return _services.Remove (id); + } + } + + /// + /// Sends a binary data to the client of the instance + /// with the specified ID. + /// + /// + /// true if the instance with + /// is successfully found; otherwise, false. + /// + /// + /// A that contains an ID that represents the destination for the data. + /// + /// + /// An array of that contains a binary data to send. + /// + internal bool SendTo (string id, byte [] data) + { + WebSocketService service; + if (TryGetServiceInstance (id, out service)) + { + service.Send (data); + return true; + } + + _logger.Error ( + "The WebSocket service instance with the specified ID not found.\nID: " + id); + return false; + } + + /// + /// Sends a text data to the client of the instance + /// with the specified ID. + /// + /// + /// true if the instance with + /// is successfully found; otherwise, false. + /// + /// + /// A that contains an ID that represents the destination for the data. + /// + /// + /// A that contains a text data to send. + /// + internal bool SendTo (string id, string data) + { + WebSocketService service; + if (TryGetServiceInstance (id, out service)) + { + service.Send (data); + return true; + } + + _logger.Error ( + "The WebSocket service instance with the specified ID not found.\nID: " + id); + return false; + } + + internal void Stop () + { + stop (0, null, true); + } + + internal void Stop (ushort code, string reason) + { + stop (code, reason, false); + } + + #endregion + + #region Public Methods + /// /// Cleans up the inactive instances. /// @@ -397,20 +523,20 @@ namespace WebSocketSharp.Server } /// - /// Tries to get the associated with the specified ID. + /// Tries to get the instance with the specified ID. /// /// - /// true if the manages the - /// with ; otherwise, false. + /// true if the instance with + /// is successfully found; otherwise, false. /// /// /// A that contains an ID to find. /// /// - /// When this method returns, contains a with - /// if it is found; otherwise, . + /// When this method returns, contains a instance with + /// if it is successfully found; otherwise, . /// - public bool TryGetWebSocketService (string id, out WebSocketService service) + public bool TryGetServiceInstance (string id, out WebSocketService service) { lock (_sync) { diff --git a/websocket-sharp/WebSocket.cs b/websocket-sharp/WebSocket.cs index 604c59e3..bf596363 100644 --- a/websocket-sharp/WebSocket.cs +++ b/websocket-sharp/WebSocket.cs @@ -588,7 +588,7 @@ namespace WebSocketSharp if (data.Length > 125) { var msg = "The payload length of a Close frame must be 125 bytes or less."; - _logger.Error (msg); + _logger.Error (String.Format ("{0}\ncode: {1}\nreason: {2}", msg, code, reason)); error (msg); return; @@ -1378,8 +1378,8 @@ namespace WebSocketSharp { if (!code.IsCloseStatusCode ()) { - var msg = String.Format ("Invalid close status code: {0}", code); - _logger.Error (msg); + var msg = "Invalid close status code."; + _logger.Error (String.Format ("{0}\ncode: {1}", msg, code)); error (msg); return;