diff --git a/src/Websocket.Client/DisconnectionType.cs b/src/Websocket.Client/DisconnectionType.cs new file mode 100644 index 0000000..a0072e9 --- /dev/null +++ b/src/Websocket.Client/DisconnectionType.cs @@ -0,0 +1,33 @@ +namespace Websocket.Client +{ + /// + /// Type that specify happenend disconnection + /// + public enum DisconnectionType + { + /// + /// Type used for exit event, disposing of the websocket client + /// + Exit = 0, + + /// + /// Type used when connection to websocket was lost in meantime + /// + Lost = 1, + + /// + /// Type used when connection to websocket was lost by not receiving any message in given timerange + /// + NoMessageReceived = 2, + + /// + /// Type used when connection or reconnection returned error + /// + Error = 3, + + /// + /// Type used when disconnection was requested by user + /// + ByUser = 4 + } +} diff --git a/src/Websocket.Client/IWebsocketClient.cs b/src/Websocket.Client/IWebsocketClient.cs index dc39c3c..4c7cd03 100644 --- a/src/Websocket.Client/IWebsocketClient.cs +++ b/src/Websocket.Client/IWebsocketClient.cs @@ -18,6 +18,11 @@ public interface IWebsocketClient : IDisposable /// IObservable ReconnectionHappened { get; } + /// + /// Stream for disconnection event (trigerred after the connection was lost) + /// + IObservable DisconnectionHappened { get; } + /// /// Time range in ms, how long to wait before reconnecting if no message comes from server. /// Default 60000 ms (1 minute) diff --git a/src/Websocket.Client/ReconnectionType.cs b/src/Websocket.Client/ReconnectionType.cs index 649c7b0..c6fd9c8 100644 --- a/src/Websocket.Client/ReconnectionType.cs +++ b/src/Websocket.Client/ReconnectionType.cs @@ -1,30 +1,33 @@ namespace Websocket.Client { + /// + /// Type that specify happenend reconnection + /// public enum ReconnectionType { /// /// Type used for initial connection to websocket stream /// - Initial, + Initial = 0, /// /// Type used when connection to websocket was lost in meantime /// - Lost, + Lost = 1, /// /// Type used when connection to websocket was lost by not receiving any message in given timerange /// - NoMessageReceived, + NoMessageReceived = 2, /// /// Type used after unsuccessful previous reconnection /// - Error, + Error = 3, /// /// Type used when reconnection was requested by user /// - ByUser + ByUser = 4 } } diff --git a/src/Websocket.Client/WebsocketClient.cs b/src/Websocket.Client/WebsocketClient.cs index c3a5ce2..aecf2a5 100644 --- a/src/Websocket.Client/WebsocketClient.cs +++ b/src/Websocket.Client/WebsocketClient.cs @@ -28,6 +28,7 @@ public class WebsocketClient : IWebsocketClient private readonly Subject _messageReceivedSubject = new Subject(); private readonly Subject _reconnectionSubject = new Subject(); + private readonly Subject _disconnectedSubject = new Subject(); private readonly BlockingCollection _messagesToSendQueue = new BlockingCollection(); @@ -53,6 +54,11 @@ public WebsocketClient(Uri url, Func clientFactory = null) /// public IObservable ReconnectionHappened => _reconnectionSubject.AsObservable(); + /// + /// Stream for disconnection event (trigerred after the connection was lost) + /// + public IObservable DisconnectionHappened => _disconnectedSubject.AsObservable(); + /// /// Time range in ms, how long to wait before reconnecting if no message comes from server. /// Default 60000 ms (1 minute) @@ -82,15 +88,24 @@ public void Dispose() { _disposing = true; Log.Debug(L("Disposing..")); - _lastChanceTimer?.Dispose(); - _cancelation?.Cancel(); - _cancelationTotal?.Cancel(); - _client?.Abort(); - _client?.Dispose(); - _cancelation?.Dispose(); - _cancelationTotal?.Dispose(); - _messagesToSendQueue?.Dispose(); + try + { + _lastChanceTimer?.Dispose(); + _cancelation?.Cancel(); + _cancelationTotal?.Cancel(); + _client?.Abort(); + _client?.Dispose(); + _cancelation?.Dispose(); + _cancelationTotal?.Dispose(); + _messagesToSendQueue?.Dispose(); + } + catch (Exception e) + { + Log.Error(e, L($"Failed to dispose client, error: {e.Message}")); + } + IsStarted = false; + _disconnectedSubject.OnNext(DisconnectionType.Exit); } /// @@ -161,7 +176,14 @@ private async Task SendFromQueue() { foreach (var message in _messagesToSendQueue.GetConsumingEnumerable(_cancelationTotal.Token)) { - await SendInternal(message).ConfigureAwait(false); + try + { + await SendInternal(message).ConfigureAwait(false); + } + catch (Exception e) + { + Log.Error(L($"Failed to send message: '{message}'. Error: {e.Message}")); + } } } catch (TaskCanceledException) @@ -214,6 +236,7 @@ private async Task StartClient(Uri uri, CancellationToken token, ReconnectionTyp } catch (Exception e) { + _disconnectedSubject.OnNext(DisconnectionType.Error); Log.Error(e, L("Exception while connecting. " + $"Waiting {ErrorReconnectTimeoutMs/1000} sec before next reconnection try.")); await Task.Delay(ErrorReconnectTimeoutMs, token).ConfigureAwait(false); @@ -230,11 +253,14 @@ private async Task GetClient() return _client; } - private async Task Reconnect( ReconnectionType type) + private async Task Reconnect(ReconnectionType type) { IsRunning = false; if (_disposing) return; + if(type != ReconnectionType.Error) + _disconnectedSubject.OnNext(TranslateTypeToDisconnection(type)); + Log.Debug(L("Reconnecting...")); _cancelation.Cancel(); await Task.Delay(1000).ConfigureAwait(false); @@ -313,5 +339,11 @@ private string L(string msg) { return $"[WEBSOCKET CLIENT] {msg}"; } + + private DisconnectionType TranslateTypeToDisconnection(ReconnectionType type) + { + // beaware enum indexes must correspond to each other + return (DisconnectionType) type; + } } } diff --git a/test_integration/Websocket.Client.Sample/Program.cs b/test_integration/Websocket.Client.Sample/Program.cs index 70f8ceb..de6c311 100644 --- a/test_integration/Websocket.Client.Sample/Program.cs +++ b/test_integration/Websocket.Client.Sample/Program.cs @@ -38,10 +38,12 @@ static void Main(string[] args) client.ReconnectTimeoutMs = (int)TimeSpan.FromSeconds(30).TotalMilliseconds; client.ReconnectionHappened.Subscribe(type => Log.Information($"Reconnection happened, type: {type}")); + client.DisconnectionHappened.Subscribe(type => + Log.Warning($"Disconnection happened, type: {type}")); client.MessageReceived.Subscribe(msg => Log.Information($"Message received: {msg}")); - client.Start(); + client.Start().Wait(); Task.Run(() => StartSendingPing(client));