diff --git a/README.md b/README.md index 862b829..1d7ca38 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ A spinoff library of Dache that provides highly efficient, scalable, simple sock **NUGET:** http://www.nuget.org/packages/SimplSockets -**WEB:** http://www.getdache.net +**WEB:** http://www.getdache.net **EMAIL:** info@getdache.net @@ -15,6 +15,23 @@ VERSION HISTORY ============================================ +1.1.0 +------------------ + + +- Fixed multiple bugs in how SimplSocket class handled communication errors. + +- Fixed bugs that could cause infinite client hang in the case that the server stopped responding. + +- Fixed semaphore calculation bugs. + +- Errors are now handled gracefully and correctly, with atomic exception raising via the passed in error handler event delegate. + +- Slight performance improvements enabled by smoother connection error handling. + +- Special thanks to Ruslan (https://github.com/ruzhovt) for discovering and documenting the source of these errors. + + 1.0.1 ------------------ diff --git a/SimplSockets/ISimplSocketClient.cs b/SimplSockets/ISimplSocketClient.cs index 1fa83e8..8770863 100644 --- a/SimplSockets/ISimplSocketClient.cs +++ b/SimplSockets/ISimplSocketClient.cs @@ -9,13 +9,11 @@ namespace SimplSockets public interface ISimplSocketClient : IDisposable { /// - /// Connects to an endpoint. Once this is called, you must call Close before calling Connect or Listen again. The errorHandler method - /// will not be called if the connection fails. Instead this method will return false. + /// Connects to an endpoint. Once this is called, you must call Close before calling Connect or Listen again. This method will not raise an error. /// /// The endpoint. - /// The error handler. /// true if connection is successful, false otherwise. - bool Connect(EndPoint endPoint, EventHandler errorHandler); + bool Connect(EndPoint endPoint); /// /// Sends a message to the server without waiting for a response (one-way communication). diff --git a/SimplSockets/ISimplSocketServer.cs b/SimplSockets/ISimplSocketServer.cs index deab448..3ceab59 100644 --- a/SimplSockets/ISimplSocketServer.cs +++ b/SimplSockets/ISimplSocketServer.cs @@ -12,8 +12,7 @@ public interface ISimplSocketServer : IDisposable /// Begin listening for incoming connections. Once this is called, you must call Close before calling Connect or Listen again. /// /// The local endpoint to listen on. - /// The error handler. - void Listen(EndPoint localEndpoint, EventHandler errorHandler); + void Listen(EndPoint localEndpoint); /// /// Sends a message back to the client. diff --git a/SimplSockets/SimplSocket.cs b/SimplSockets/SimplSocket.cs index 6d8a9bf..2421adc 100644 --- a/SimplSockets/SimplSocket.cs +++ b/SimplSockets/SimplSocket.cs @@ -58,40 +58,57 @@ public class SimplSocket : ISimplSocketClient, ISimplSocketServer, IDisposable /// Create a client. /// /// The function that creates a new socket. Use this to specify your socket constructor and initialize settings. + /// The error handler that is raised when a Socket error occurs. /// The message buffer size to use for send/receive. /// The maximum connections to allow to use the socket simultaneously. /// Whether or not to use the Nagle algorithm. - public static ISimplSocketClient CreateClient(Func socketFunc, int messageBufferSize, int maximumConnections, bool useNagleAlgorithm) + public static ISimplSocketClient CreateClient(Func socketFunc, EventHandler errorHandler, int messageBufferSize, int maximumConnections, bool useNagleAlgorithm) { - return new SimplSocket(socketFunc, messageBufferSize, maximumConnections, useNagleAlgorithm); + return new SimplSocket(socketFunc, errorHandler, messageBufferSize, maximumConnections, useNagleAlgorithm); } /// /// Create a server. /// /// The function that creates a new socket. Use this to specify your socket constructor and initialize settings. + /// The error handler that is raised when a Socket error occurs. + /// The message handler that handles incoming messages. /// The message buffer size to use for send/receive. /// The maximum connections to allow to use the socket simultaneously. /// Whether or not to use the Nagle algorithm. - public static ISimplSocketServer CreateServer(Func socketFunc, int messageBufferSize, int maximumConnections, bool useNagleAlgorithm) + public static ISimplSocketServer CreateServer(Func socketFunc, EventHandler errorHandler, EventHandler messageHandler, + int messageBufferSize, int maximumConnections, bool useNagleAlgorithm) { - return new SimplSocket(socketFunc, messageBufferSize, maximumConnections, useNagleAlgorithm); + // Sanitize + if (messageHandler == null) + { + throw new ArgumentNullException("messageHandler"); + } + + var simplSocket = new SimplSocket(socketFunc, errorHandler, messageBufferSize, maximumConnections, useNagleAlgorithm); + simplSocket.MessageReceived += messageHandler; + return simplSocket; } /// /// The private constructor - used to enforce factor-style instantiation. /// /// The function that creates a new socket. Use this to specify your socket constructor and initialize settings. + /// The error handler that is raised when a Socket error occurs. /// The message buffer size to use for send/receive. /// The maximum connections to allow to use the socket simultaneously. /// Whether or not to use the Nagle algorithm. - private SimplSocket(Func socketFunc, int messageBufferSize, int maximumConnections, bool useNagleAlgorithm) + private SimplSocket(Func socketFunc, EventHandler errorHandler, int messageBufferSize, int maximumConnections, bool useNagleAlgorithm) { // Sanitize if (socketFunc == null) { throw new ArgumentNullException("socketFunc"); } + if (errorHandler == null) + { + throw new ArgumentNullException("errorHandler"); + } if (messageBufferSize < 128) { throw new ArgumentException("must be >= 128", "messageBufferSize"); @@ -102,6 +119,7 @@ private SimplSocket(Func socketFunc, int messageBufferSize, int maximumC } _socketFunc = socketFunc; + Error += errorHandler; _messageBufferSize = messageBufferSize; _maximumConnections = maximumConnections; _maxConnectionsSemaphore = new Semaphore(maximumConnections, maximumConnections); @@ -158,9 +176,8 @@ public int CurrentlyConnectedClients /// will not be called if the connection fails. Instead this method will return false. /// /// The endpoint. - /// The error handler. /// true if connection is successful, false otherwise. - public bool Connect(EndPoint endPoint, EventHandler errorHandler) + public bool Connect(EndPoint endPoint) { // Sanitize if (_isDoingSomething) @@ -171,10 +188,6 @@ public bool Connect(EndPoint endPoint, EventHandler errorHandle { throw new ArgumentNullException("endPoint"); } - if (errorHandler == null) - { - throw new ArgumentNullException("errorHandler"); - } _isDoingSomething = true; _useClientMultiplexer = true; @@ -196,12 +209,10 @@ public bool Connect(EndPoint endPoint, EventHandler errorHandle } catch (SocketException ex) { + _isDoingSomething = false; return false; } - // Register error handler - Error += errorHandler; - // Get a message state from the pool var messageState = _messageStatePool.Pop(); messageState.Data = new MemoryStream(); @@ -233,8 +244,7 @@ public bool Connect(EndPoint endPoint, EventHandler errorHandle /// Begin listening for incoming connections. Once this is called, you must call Close before calling Connect or Listen again. /// /// The local endpoint to listen on. - /// The error handler. - public void Listen(EndPoint localEndpoint, EventHandler errorHandler) + public void Listen(EndPoint localEndpoint) { // Sanitize if (_isDoingSomething) @@ -245,16 +255,9 @@ public void Listen(EndPoint localEndpoint, EventHandler errorHa { throw new ArgumentNullException("localEndpoint"); } - if (errorHandler == null) - { - throw new ArgumentNullException("errorHandler"); - } _isDoingSomething = true; - // Register error handler - Error += errorHandler; - // Create socket _socket = _socketFunc();