示例#1
0
        // Receive from udp socket and push value to subscribers.
        async void RunReceiveLoop()
        {
            var token     = cancellationTokenSource.Token;
            var udpServer = server.Value;

            while (!token.IsCancellationRequested)
            {
                ReadOnlyMemory <byte> value;
                try
                {
                    value = await udpServer.ReceiveAsync(token).ConfigureAwait(false);

                    if (value.Length == 0)
                    {
                        return;                    // invalid data?
                    }
                    var len = MessageBuilder.FetchMessageLength(value.Span);
                    if (len != value.Length - 4)
                    {
                        throw new InvalidOperationException("Receive invalid message size.");
                    }
                    value = value.Slice(4);
                }
                catch (Exception ex)
                {
                    if (ex is OperationCanceledException)
                    {
                        return;
                    }
                    if (token.IsCancellationRequested)
                    {
                        return;
                    }

                    // network error, terminate.
                    options.UnhandledErrorHandler("network error, receive loop will terminate." + Environment.NewLine, ex);
                    return;
                }

                try
                {
                    var message = MessageBuilder.ReadPubSubMessage(value.ToArray());
                    publisher.Publish(message, message, CancellationToken.None);
                }
                catch (Exception ex)
                {
                    if (ex is OperationCanceledException)
                    {
                        return;
                    }
                    if (token.IsCancellationRequested)
                    {
                        return;
                    }
                    options.UnhandledErrorHandler("", ex);
                }
            }
        }
示例#2
0
        // Receive from udp socket and push value to subscribers.
        async void RunReceiveLoop(Stream pipeStream, Func <CancellationToken, Task>?waitForConnection)
        {
RECONNECT:
            var token = cancellationTokenSource.Token;

            if (waitForConnection != null)
            {
                try
                {
                    await waitForConnection(token).ConfigureAwait(false);
                }
                catch (IOException)
                {
                    return; // connection closed.
                }
            }
            var buffer = new byte[65536];

            while (!token.IsCancellationRequested)
            {
                ReadOnlyMemory <byte> value = Array.Empty <byte>();
                try
                {
                    var readLen = await pipeStream.ReadAsync(buffer, 0, buffer.Length, token).ConfigureAwait(false);

                    if (readLen == 0)
                    {
                        if (waitForConnection != null)
                        {
                            server.Value.Dispose();
                            server     = CreateLazyServerStream();
                            pipeStream = server.Value;
                            goto RECONNECT; // end of stream(disconnect, wait reconnect)
                        }
                    }

                    var messageLen = MessageBuilder.FetchMessageLength(buffer);
                    if (readLen == (messageLen + 4))
                    {
                        value = buffer.AsMemory(4, messageLen); // skip length header
                    }
                    else
                    {
                        // read more
                        if (buffer.Length < (messageLen + 4))
                        {
                            Array.Resize(ref buffer, messageLen + 4);
                        }
                        var remain = messageLen - (readLen - 4);
                        await ReadFullyAsync(buffer, pipeStream, readLen, remain, token).ConfigureAwait(false);

                        value = buffer.AsMemory(4, messageLen);
                    }
                }
                catch (IOException)
                {
                    return; // connection closed.
                }
                catch (Exception ex)
                {
                    if (ex is OperationCanceledException)
                    {
                        return;
                    }
                    if (token.IsCancellationRequested)
                    {
                        return;
                    }

                    // network error, terminate.
                    options.UnhandledErrorHandler("network error, receive loop will terminate." + Environment.NewLine, ex);
                    return;
                }

                try
                {
                    var message = MessageBuilder.ReadPubSubMessage(value.ToArray()); // can avoid copy?
                    switch (message.MessageType)
                    {
                    case MessageType.PubSub:
                        publisher.Publish(message, message, CancellationToken.None);
                        break;

                    case MessageType.RemoteRequest:
                    {
                        // NOTE: should use without reflection(Expression.Compile)
                        var header = Deserialize <RequestHeader>(message.KeyMemory, options.MessagePackSerializerOptions);
                        var(mid, reqTypeName, resTypeName) = (header.MessageId, header.RequestType, header.ResponseType);
                        byte[] resultBytes;
                        try
                        {
                            var t             = AsyncRequestHandlerRegistory.Get(reqTypeName, resTypeName);
                            var interfaceType = t.GetInterfaces().Where(x => x.IsGenericType && x.Name.StartsWith("IAsyncRequestHandler"))
                                                .First(x => x.GetGenericArguments().Any(x => x.FullName == header.RequestType));
                            var coreInterfaceType = t.GetInterfaces().Where(x => x.IsGenericType && x.Name.StartsWith("IAsyncRequestHandlerCore"))
                                                    .First(x => x.GetGenericArguments().Any(x => x.FullName == header.RequestType));
                            var service      = provider.GetRequiredService(interfaceType); // IAsyncRequestHandler<TRequest,TResponse>
                            var genericArgs  = interfaceType.GetGenericArguments();        // [TRequest, TResponse]
                            var request      = MessagePackSerializer.Deserialize(genericArgs[0], message.ValueMemory, options.MessagePackSerializerOptions);
                            var responseTask = coreInterfaceType.GetMethod("InvokeAsync") !.Invoke(service, new[] { request, CancellationToken.None });
                            var task         = typeof(ValueTask <>).MakeGenericType(genericArgs[1]).GetMethod("AsTask") !.Invoke(responseTask, null);
                            await((System.Threading.Tasks.Task)task !);         // Task<T> -> Task
                            var result = task.GetType().GetProperty("Result") !.GetValue(task);
                            resultBytes = MessageBuilder.BuildRemoteResponseMessage(mid, genericArgs[1], result !, options.MessagePackSerializerOptions);
                        }
                        catch (Exception ex)
                        {
                            // NOTE: ok to send stacktrace?
                            resultBytes = MessageBuilder.BuildRemoteResponseError(mid, ex.ToString(), options.MessagePackSerializerOptions);
                        }

                        await pipeStream.WriteAsync(resultBytes, 0, resultBytes.Length).ConfigureAwait(false);
                    }
                    break;

                    case MessageType.RemoteResponse:
                    case MessageType.RemoteError:
                    {
                        var mid = Deserialize <int>(message.KeyMemory, options.MessagePackSerializerOptions);
                        if (responseCompletions.TryRemove(mid, out var tcs))
                        {
                            if (message.MessageType == MessageType.RemoteResponse)
                            {
                                tcs.TrySetResult(message);         // synchronous completion, use memory buffer immediately.
                            }
                            else
                            {
                                var errorMsg = MessagePackSerializer.Deserialize <string>(message.ValueMemory, options.MessagePackSerializerOptions);
                                tcs.TrySetException(new RemoteRequestException(errorMsg));
                            }
                        }
                    }
                    break;

                    default:
                        break;
                    }
                }
                catch (IOException)
                {
                    return; // connection closed.
                }
                catch (Exception ex)
                {
                    if (ex is OperationCanceledException)
                    {
                        continue;
                    }
                    options.UnhandledErrorHandler("", ex);
                }
            }
        }
示例#3
0
        // Receive from tcp socket and push value to subscribers.
        async void RunReceiveLoop(SocketTcpClient client)
        {
            var token  = cancellationTokenSource.Token;
            var buffer = new byte[65536];
            ReadOnlyMemory <byte> readBuffer = Array.Empty <byte>();

            while (!token.IsCancellationRequested)
            {
                ReadOnlyMemory <byte> value = Array.Empty <byte>();
                try
                {
                    if (readBuffer.Length == 0)
                    {
                        var readLen = await client.ReceiveAsync(buffer, 0, buffer.Length, token).ConfigureAwait(false);

                        if (readLen == 0)
                        {
                            return;               // end of stream(disconnect)
                        }
                        readBuffer = buffer.AsMemory(0, readLen);
                    }
                    else if (readBuffer.Length < 4) // rare case
                    {
                        var readLen = await client.ReceiveAsync(buffer, 0, buffer.Length, token).ConfigureAwait(false);

                        if (readLen == 0)
                        {
                            return;
                        }
                        var newBuffer = new byte[readBuffer.Length + readLen];
                        readBuffer.CopyTo(newBuffer);
                        buffer.AsSpan(readLen).CopyTo(newBuffer.AsSpan(readBuffer.Length));
                        readBuffer = newBuffer;
                    }

                    var messageLen = MessageBuilder.FetchMessageLength(readBuffer.Span);
                    if (readBuffer.Length == (messageLen + 4))        // just size
                    {
                        value      = readBuffer.Slice(4, messageLen); // skip length header
                        readBuffer = Array.Empty <byte>();
                        goto PARSE_MESSAGE;
                    }
                    else if (readBuffer.Length > (messageLen + 4)) // over size
                    {
                        value      = readBuffer.Slice(4, messageLen);
                        readBuffer = readBuffer.Slice(messageLen + 4);
                        goto PARSE_MESSAGE;
                    }
                    else // needs to read more
                    {
                        var readLen = readBuffer.Length;
                        if (readLen < (messageLen + 4))
                        {
                            if (readBuffer.Length != buffer.Length)
                            {
                                var newBuffer = new byte[buffer.Length];
                                readBuffer.CopyTo(newBuffer);
                                buffer = newBuffer;
                            }

                            if (buffer.Length < messageLen + 4)
                            {
                                Array.Resize(ref buffer, messageLen + 4);
                            }
                        }
                        var remain = messageLen - (readLen - 4);
                        await ReadFullyAsync(buffer, client, readLen, remain, token).ConfigureAwait(false);

                        value      = buffer.AsMemory(4, messageLen);
                        readBuffer = Array.Empty <byte>();
                        goto PARSE_MESSAGE;
                    }
                }
                catch (Exception ex)
                {
                    if (ex is OperationCanceledException)
                    {
                        return;
                    }
                    if (token.IsCancellationRequested)
                    {
                        return;
                    }

                    // network error, terminate.
                    options.UnhandledErrorHandler("network error, receive loop will terminate." + Environment.NewLine, ex);
                    return;
                }
PARSE_MESSAGE:
                try
                {
                    var message = MessageBuilder.ReadPubSubMessage(value.ToArray()); // can avoid copy?
                    switch (message.MessageType)
                    {
                    case MessageType.PubSub:
                        publisher.Publish(message, message, CancellationToken.None);
                        break;

                    case MessageType.RemoteRequest:
                    {
                        // NOTE: should use without reflection(Expression.Compile)
                        var header = Deserialize <RequestHeader>(message.KeyMemory, options.MessagePackSerializerOptions);
                        var(mid, reqTypeName, resTypeName) = (header.MessageId, header.RequestType, header.ResponseType);
                        byte[] resultBytes;
                        try
                        {
                            var t                 = AsyncRequestHandlerRegistory.Get(reqTypeName, resTypeName);
                            var interfaceType     = t.GetInterfaces().First(x => x.IsGenericType && x.Name.StartsWith("IAsyncRequestHandler"));
                            var coreInterfaceType = t.GetInterfaces().First(x => x.IsGenericType && x.Name.StartsWith("IAsyncRequestHandlerCore"));
                            var service           = provider.GetRequiredService(interfaceType); // IAsyncRequestHandler<TRequest,TResponse>
                            var genericArgs       = interfaceType.GetGenericArguments();        // [TRequest, TResponse]
                            // Unity IL2CPP does not work(can not invoke nongenerics MessagePackSerializer)
                            var request      = MessagePackSerializer.Deserialize(genericArgs[0], message.ValueMemory, options.MessagePackSerializerOptions);
                            var responseTask = coreInterfaceType.GetMethod("InvokeAsync") !.Invoke(service, new[] { request, CancellationToken.None });
#if !UNITY_2018_3_OR_NEWER
                            var task = typeof(ValueTask <>).MakeGenericType(genericArgs[1]).GetMethod("AsTask") !.Invoke(responseTask, null);
#else
                            var asTask = typeof(UniTaskExtensions).GetMethods().First(x => x.IsGenericMethod && x.Name == "AsTask")
                                         .MakeGenericMethod(genericArgs[1]);
                            var task = asTask.Invoke(null, new[] { responseTask });
#endif
                            await((System.Threading.Tasks.Task)task !);         // Task<T> -> Task
                            var result = task.GetType().GetProperty("Result") !.GetValue(task);
                            resultBytes = MessageBuilder.BuildRemoteResponseMessage(mid, genericArgs[1], result !, options.MessagePackSerializerOptions);
                        }
                        catch (Exception ex)
                        {
                            // NOTE: ok to send stacktrace?
                            resultBytes = MessageBuilder.BuildRemoteResponseError(mid, ex.ToString(), options.MessagePackSerializerOptions);
                        }

                        await client.SendAsync(resultBytes).ConfigureAwait(false);
                    }
                    break;

                    case MessageType.RemoteResponse:
                    case MessageType.RemoteError:
                    {
                        var mid = Deserialize <int>(message.KeyMemory, options.MessagePackSerializerOptions);
                        if (responseCompletions.TryRemove(mid, out var tcs))
                        {
                            if (message.MessageType == MessageType.RemoteResponse)
                            {
                                tcs.TrySetResult(message);         // synchronous completion, use memory buffer immediately.
                            }
                            else
                            {
                                var errorMsg = MessagePackSerializer.Deserialize <string>(message.ValueMemory, options.MessagePackSerializerOptions);
                                tcs.TrySetException(new RemoteRequestException(errorMsg));
                            }
                        }
                    }
                    break;

                    default:
                        break;
                    }
                }
                catch (Exception ex)
                {
                    if (ex is OperationCanceledException)
                    {
                        continue;
                    }
                    options.UnhandledErrorHandler("", ex);
                }
            }
        }