public static TStreamingHub Connect <TStreamingHub, TReceiver>(CallInvoker callInvoker, TReceiver receiver, string host = null, CallOptions option = default(CallOptions), MessagePackSerializerOptions serializerOptions = null, IMagicOnionClientLogger logger = null) where TStreamingHub : IStreamingHub <TStreamingHub, TReceiver> { var client = CreateClient <TStreamingHub, TReceiver>(callInvoker, receiver, host, option, serializerOptions, logger); async void ConnectAndForget() { var task = client.__ConnectAndSubscribeAsync(receiver, CancellationToken.None); try { await task; } catch (Exception e) { logger?.Error(e, "An error occurred while connecting to the server."); } } ConnectAndForget(); return((TStreamingHub)(object)client); }
async Task StartSubscribe() { var syncContext = SynchronizationContext.Current; // capture SynchronizationContext. var reader = connection.RawStreamingCall.ResponseStream; try { while (await reader.MoveNext(cts.Token).ConfigureAwait(false)) // avoid Post to SyncContext(it losts one-frame per operation) { try { // MessageFormat: // broadcast: [methodId, [argument]] // response: [messageId, methodId, response] // error-response: [messageId, statusCode, detail, StringMessage] void ConsumeData(byte[] data) { var messagePackReader = new MessagePackReader(data); var arrayLength = messagePackReader.ReadArrayHeader(); if (arrayLength == 3) { var messageId = messagePackReader.ReadInt32(); object future; if (responseFutures.TryRemove(messageId, out future)) { var methodId = messagePackReader.ReadInt32(); try { var offset = (int)messagePackReader.Consumed; var rest = new ArraySegment <byte>(data, offset, data.Length - offset); OnResponseEvent(methodId, future, rest); } catch (Exception ex) { if (!(future as ITaskCompletion).TrySetException(ex)) { throw; } } } } else if (arrayLength == 4) { var messageId = messagePackReader.ReadInt32(); object future; if (responseFutures.TryRemove(messageId, out future)) { var statusCode = messagePackReader.ReadInt32(); var detail = messagePackReader.ReadString(); var offset = (int)messagePackReader.Consumed; var rest = new ArraySegment <byte>(data, offset, data.Length - offset); var error = MessagePackSerializer.Deserialize <string>(rest, serializerOptions); var ex = default(RpcException); if (string.IsNullOrWhiteSpace(error)) { ex = new RpcException(new Status((StatusCode)statusCode, detail)); } else { ex = new RpcException(new Status((StatusCode)statusCode, detail), detail + Environment.NewLine + error); } (future as ITaskCompletion).TrySetException(ex); } } else { var methodId = messagePackReader.ReadInt32(); var offset = (int)messagePackReader.Consumed; if (syncContext != null) { var tuple = Tuple.Create(methodId, data, offset, data.Length - offset); syncContext.Post(state => { var t = (Tuple <int, byte[], int, int>)state; OnBroadcastEvent(t.Item1, new ArraySegment <byte>(t.Item2, t.Item3, t.Item4)); }, tuple); } else { OnBroadcastEvent(methodId, new ArraySegment <byte>(data, offset, data.Length - offset)); } } } ConsumeData(reader.Current); } catch (Exception ex) { const string msg = "Error on consume received message, but keep subscribe."; // log post on main thread. if (syncContext != null) { syncContext.Post(state => logger.Error((Exception)state, msg), ex); } else { logger.Error(ex, msg); } } } } catch (Exception ex) { if (ex is OperationCanceledException) { return; } const string msg = "Error on subscribing message."; // log post on main thread. if (syncContext != null) { syncContext.Post(state => logger.Error((Exception)state, msg), ex); } else { logger.Error(ex, msg); } } finally { try { // set syncContext before await if (syncContext != null && SynchronizationContext.Current == null) { SynchronizationContext.SetSynchronizationContext(syncContext); } await DisposeAsyncCore(false); } finally { waitForDisconnect.TrySetResult(null); } } }
async Task StartSubscribe(SynchronizationContext syncContext, Task <bool> firstMoveNext) { var reader = connection.RawStreamingCall.ResponseStream; try { var moveNext = firstMoveNext; while (await moveNext.ConfigureAwait(false)) // avoid Post to SyncContext(it losts one-frame per operation) { try { ConsumeData(syncContext, reader.Current); } catch (Exception ex) { const string msg = "An error occurred when consuming a received message, but the subscription is still alive."; // log post on main thread. if (syncContext != null) { syncContext.Post(state => logger.Error((Exception)state, msg), ex); } else { logger.Error(ex, msg); } } moveNext = reader.MoveNext(cts.Token); } } catch (Exception ex) { if (ex is OperationCanceledException) { return; } const string msg = "An error occurred while subscribing to messages."; // log post on main thread. if (syncContext != null) { syncContext.Post(state => logger.Error((Exception)state, msg), ex); } else { logger.Error(ex, msg); } } finally { try { // set syncContext before await if (syncContext != null && SynchronizationContext.Current == null) { SynchronizationContext.SetSynchronizationContext(syncContext); } await DisposeAsyncCore(false); } finally { waitForDisconnect.TrySetResult(null); } } }