private async Task Reconnect(ReconnectionType type, bool failFast, System.Exception causedException) { IsRunning = false; if (_disposing) { return; } _reconnecting = true; var disType = TranslateTypeToDisconnection(type); var disInfo = DisconnectionInfo.Create(disType, _client, causedException); if (type != ReconnectionType.Error) { _disconnectedSubject.OnNext(disInfo); if (disInfo.CancelReconnection) { // reconnection canceled by user, do nothing Logger.Info(L($"Reconnecting canceled by user, exiting.")); } } _cancellation.Cancel(); _client?.Abort(); _client?.Dispose(); if (!IsReconnectionEnabled || disInfo.CancelReconnection) { // reconnection disabled, do nothing IsStarted = false; _reconnecting = false; return; } Logger.Debug(L("Reconnecting...")); _cancellation = new CancellationTokenSource(); await StartClient(_url, _cancellation.Token, type, failFast).ConfigureAwait(false); _reconnecting = false; }
private async Task Listen(WebSocket client, CancellationToken token) { System.Exception causedException = null; try { // define buffer here and reuse, to avoid more allocation const int chunkSize = 1024 * 4; var buffer = new ArraySegment <byte>(new byte[chunkSize]); do { WebSocketReceiveResult result; byte[] resultArrayWithTrailing = null; var resultArraySize = 0; var isResultArrayCloned = false; MemoryStream ms = null; while (true) { result = await client.ReceiveAsync(buffer, token); var currentChunk = buffer.Array; var currentChunkSize = result.Count; var isFirstChunk = resultArrayWithTrailing == null; if (isFirstChunk) { // first chunk, use buffer as reference, do not allocate anything resultArraySize += currentChunkSize; resultArrayWithTrailing = currentChunk; isResultArrayCloned = false; } else if (currentChunk == null) { // weird chunk, do nothing } else { // received more chunks, lets merge them via memory stream if (ms == null) { // create memory stream and insert first chunk ms = new MemoryStream(); ms.Write(resultArrayWithTrailing, 0, resultArraySize); } // insert current chunk ms.Write(currentChunk, buffer.Offset, currentChunkSize); } if (result.EndOfMessage) { break; } if (isResultArrayCloned) { continue; } // we got more chunks incoming, need to clone first chunk resultArrayWithTrailing = resultArrayWithTrailing?.ToArray(); isResultArrayCloned = true; } ms?.Seek(0, SeekOrigin.Begin); ResponseMessage message; if (result.MessageType == WebSocketMessageType.Text && IsTextMessageConversionEnabled) { var data = ms != null? GetEncoding().GetString(ms.ToArray()) : resultArrayWithTrailing != null? GetEncoding().GetString(resultArrayWithTrailing, 0, resultArraySize) : null; message = ResponseMessage.New(data); } else if (result.MessageType == WebSocketMessageType.Close) { Logger.Trace(L($"Received close message")); var info = DisconnectionInfo.Create(DisconnectionType.ByServer, client, null); _disconnectedSubject.OnNext(info); if (info.CancelClosing) { // closing canceled, reconnect if enabled if (IsReconnectionEnabled) { throw new OperationCanceledException("Websocket connection was closed by server"); } continue; } await StopInternal(client, WebSocketCloseStatus.NormalClosure, "Closing", token, false, true); // reconnect if enabled if (IsReconnectionEnabled && !ShouldIgnoreReconnection(client)) { _ = ReconnectSynchronized(ReconnectionType.Lost, false, null); } return; } else { if (ms != null) { message = ResponseMessage.New(ms.ToArray()); } else { Array.Resize(ref resultArrayWithTrailing, resultArraySize); message = ResponseMessage.New(resultArrayWithTrailing); } } ms?.Dispose(); Logger.Trace(L($"Received: {message}")); _lastReceivedMsg = DateTime.UtcNow; _messageReceivedSubject.OnNext(message); } while (client.State == WebSocketState.Open && !token.IsCancellationRequested); } catch (TaskCanceledException e) { // task was canceled, ignore causedException = e; } catch (OperationCanceledException e) { // operation was canceled, ignore causedException = e; } catch (ObjectDisposedException e) { // client was disposed, ignore causedException = e; } catch (System.Exception e) { Logger.Error(e, L($"Error while listening to websocket stream, error: '{e.Message}'")); causedException = e; } if (ShouldIgnoreReconnection(client) || !IsStarted) { // reconnection already in progress or client stopped/disposed, do nothing return; } // listening thread is lost, we have to reconnect _ = ReconnectSynchronized(ReconnectionType.Lost, false, causedException); }