• (七)分布式通信----Netty实现NIO通信


    ==>>点击查看本系列文章目录

    目录

    1. 消息监听器

    2. 指令执行器

    3. 消息发送器

    4. 客户端工厂

    5. 序列化工具

    6. 通信主机

    项目文件结构图

     通信主机:

    1. 消息监听器(黄色框)

    这部分由 Netty 实现,Netty是一个异步且非阻塞的通信框架。TCP通信实现服务端和客户端的交互。

    Netty 的简单描述如下:

    客户端(调用方):负责发送要执行的指令。

    服务端(接收方):分为主从线程。主线程负责接收指令,将指令存入缓存区中,等待执行完成后再通知客户端(非阻塞);

                    从线程,有不止一个线程(异步),负责从缓存池中取出线程依次执行(按队列先后顺序执行),我们通过程序来决定先执行哪个,比如先解码,后执行,再编码。

    上层接口:

        /// <summary>
        /// 接受到消息的委托。
        /// </summary>
        /// <param name="sender">消息发送者。</param>
        /// <param name="message">接收到的消息。</param>
        public delegate Task ReceivedDelegate(IMessageSender sender, TransportMessage message);
    
        /// <summary>
        /// 一个抽象的消息监听者。
        /// </summary>
        public interface IMessageListener
        {
            /// <summary>
            /// 接收到消息的事件。
            /// </summary>
            event ReceivedDelegate Received;
    
            /// <summary>
            /// 触发接收到消息事件。
            /// </summary>
            /// <param name="sender">消息发送者。</param>
            /// <param name="message">接收到的消息。</param>
            /// <returns>一个任务。</returns>
            Task OnReceived(IMessageSender sender, TransportMessage message);
        }

    客户端:

    /// <summary>
        /// 消息监听者。
        /// </summary>
        public class DotNettyClientMessageListener : IMessageListener
        {
            #region Implementation of IMessageListener
    
            /// <summary>
            /// 接收到消息的事件。
            /// </summary>
            public event ReceivedDelegate Received;
    
            /// <summary>
            /// 触发接收到消息事件。
            /// </summary>
            /// <param name="sender">消息发送者。</param>
            /// <param name="message">接收到的消息。</param>
            /// <returns>一个任务。</returns>
            public async Task OnReceived(IMessageSender sender, TransportMessage message)
            {
                if (Received == null)
                    return;
                await Received(sender, message);
            }
    
            #endregion Implementation of IMessageListener
        }

    服务端:

        public class DotNettyServerMessageListener : IMessageListener, IDisposable
        {
            #region Field
    
            private readonly ILogger<DotNettyServerMessageListener> _logger;
            private readonly ITransportMessageDecoder _transportMessageDecoder;
            private readonly ITransportMessageEncoder _transportMessageEncoder;
            private IChannel _channel;
    
            #endregion Field
    
            #region Constructor
    
            public DotNettyServerMessageListener(ILogger<DotNettyServerMessageListener> logger, ITransportMessageCodecFactory codecFactory)
            {
                _logger = logger;
                _transportMessageEncoder = codecFactory.GetEncoder();
                _transportMessageDecoder = codecFactory.GetDecoder();
            }
    
            #endregion Constructor
    
            #region Implementation of IMessageListener
    
            public event ReceivedDelegate Received;
    
            /// <summary>
            /// 触发接收到消息事件。
            /// </summary>
            /// <param name="sender">消息发送者。</param>
            /// <param name="message">接收到的消息。</param>
            /// <returns>一个任务。</returns>
            public async Task OnReceived(IMessageSender sender, TransportMessage message)
            {
                if (Received == null)
                    return;
                await Received(sender, message);
            }
    
            #endregion Implementation of IMessageListener
    
            public async Task StartAsync(EndPoint endPoint)
            {
                if (_logger.IsEnabled(LogLevel.Debug))
                    _logger.LogDebug($"准备启动服务主机,监听地址:{endPoint}。");
    
                IEventLoopGroup bossGroup = new MultithreadEventLoopGroup(1);
                IEventLoopGroup workerGroup = new MultithreadEventLoopGroup();//Default eventLoopCount is Environment.ProcessorCount * 2
                var bootstrap = new ServerBootstrap();
                bootstrap
                .Channel<TcpServerSocketChannel>()
                .Option(ChannelOption.SoBacklog, 128)
                .ChildOption(ChannelOption.Allocator, PooledByteBufferAllocator.Default)
                .Group(bossGroup, workerGroup)
                .ChildHandler(new ActionChannelInitializer<IChannel>(channel =>
                {
                    var pipeline = channel.Pipeline;
                    pipeline.AddLast(new LengthFieldPrepender(4));
                    pipeline.AddLast(new LengthFieldBasedFrameDecoder(int.MaxValue, 0, 4, 0, 4));
                    pipeline.AddLast(new TransportMessageChannelHandlerAdapter(_transportMessageDecoder));
                    pipeline.AddLast(new ServerHandler(async (contenxt, message) =>
                    {
                        var sender = new DotNettyServerMessageSender(_transportMessageEncoder, contenxt);
                        await OnReceived(sender, message);
                    }, _logger));
                }));
                try
                {
                    _channel = await bootstrap.BindAsync(endPoint);
                    if (_logger.IsEnabled(LogLevel.Debug))
                        _logger.LogDebug($"服务主机启动成功,监听地址:{endPoint}。");
                }
                catch
                {
                    _logger.LogError($"服务主机启动失败,监听地址:{endPoint}。 ");
                }
            }
    
            public void CloseAsync()
            {
                Task.Run(async () =>
                {
                    await _channel.EventLoop.ShutdownGracefullyAsync();
                    await _channel.CloseAsync();
                }).Wait();
            }
    
            #region Implementation of IDisposable
    
            /// <summary>Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.</summary>
            public void Dispose()
            {
                Task.Run(async () =>
                {
                    await _channel.DisconnectAsync();
                }).Wait();
            }
    
            #endregion Implementation of IDisposable
    
            #region Help Class
    
            private class ServerHandler : ChannelHandlerAdapter
            {
                private readonly Action<IChannelHandlerContext, TransportMessage> _readAction;
                private readonly ILogger _logger;
    
                public ServerHandler(Action<IChannelHandlerContext, TransportMessage> readAction, ILogger logger)
                {
                    _readAction = readAction;
                    _logger = logger;
                }
    
                #region Overrides of ChannelHandlerAdapter
    
                public override void ChannelRead(IChannelHandlerContext context, object message)
                {
                    Task.Run(() =>
                    {
                        var transportMessage = (TransportMessage)message;
                        _readAction(context, transportMessage);
                    });
                }
    
                public override void ChannelReadComplete(IChannelHandlerContext context)
                {
                    context.Flush();
                }
    
                public override void ExceptionCaught(IChannelHandlerContext context, Exception exception)
                {
                    context.CloseAsync();//客户端主动断开需要应答,否则socket变成CLOSE_WAIT状态导致socket资源耗尽
                    if (_logger.IsEnabled(LogLevel.Error))
                        _logger.LogError(null,exception, $"与服务器:{context.Channel.RemoteAddress}通信时发送了错误。");
                }
    
                #endregion Overrides of ChannelHandlerAdapter
            }
    
            #endregion Help Class
        }

    2. 指令执行器(红色框)

    上图中只有服务端的实现,服务端接收到指令后会回调执行器中的过程。

    客户端的时候需要在业务场景中来实现,根据业务不同,接收到服务端消息后执行的过程也不同。然后通过控制反转,由程序自动找到该过程。

    上层接口:

        /// <summary>
        /// 一个抽象的服务执行器。
        /// </summary>
        public interface IServiceExecutor
        {
            /// <summary>
            /// 执行。
            /// </summary>
            /// <param name="sender">消息发送者。</param>
            /// <param name="message">调用消息。</param>
            Task ExecuteAsync(IMessageSender sender, TransportMessage message);
        }

    实现类,代码中没有处理逻辑(服务发现、执行,后续有专题来谈),只有输出打印 "服务提供者接收到消息。" :

        public class HttpServiceExecutor : IServiceExecutor
        {
            #region Field
            
            private readonly ILogger<HttpServiceExecutor> _logger;
            #endregion Field
    
            #region Constructor
    
            public HttpServiceExecutor(ILogger<HttpServiceExecutor> logger)
            {
                _logger = logger;
            }
    
            #endregion Constructor
    
            #region Implementation of IServiceExecutor
    
            /// <summary>
            /// 执行。
            /// </summary>
            /// <param name="sender">消息发送者。</param>
            /// <param name="message">调用消息。</param>
            public async Task ExecuteAsync(IMessageSender sender, TransportMessage message)
            {
                if (_logger.IsEnabled(LogLevel.Trace))
                    _logger.LogTrace("服务提供者接收到消息。");
                return;
    
            }
            #endregion Implementation of IServiceExecutor
        }

    3. 消息发送器(蓝色框)

    将要发送的消息写入到通信管道中,服务端和客户端都有实现。

    上层接口:

        /// <summary>
        /// 一个抽象的发送者。
        /// </summary>
        public interface IMessageSender
        {
            /// <summary>
            /// 发送消息。
            /// </summary>
            /// <param name="message">消息内容。</param>
            /// <returns>一个任务。</returns>
            Task SendAsync(TransportMessage message);
    
            /// <summary>
            /// 发送消息并清空缓冲区。
            /// </summary>
            /// <param name="message">消息内容。</param>
            /// <returns>一个任务。</returns>
            Task SendAndFlushAsync(TransportMessage message);
        }

    实现类基类:

        /// <summary>
        /// 基于DotNetty的消息发送者基类。
        /// </summary>
        public abstract class DotNettyMessageSender
        {
            private readonly ITransportMessageEncoder _transportMessageEncoder;
    
            protected DotNettyMessageSender(ITransportMessageEncoder transportMessageEncoder)
            {
                _transportMessageEncoder = transportMessageEncoder;
            }
    
            protected IByteBuffer GetByteBuffer(TransportMessage message)
            {
                var data = _transportMessageEncoder.Encode(message);
                //var buffer = PooledByteBufferAllocator.Default.Buffer();
                return Unpooled.WrappedBuffer(data);
            }
        }

    服务端:

        /// <summary>
        /// 基于DotNetty服务端的消息发送者。
        /// </summary>
        public class DotNettyServerMessageSender : DotNettyMessageSender, IMessageSender
        {
            private readonly IChannelHandlerContext _context;
    
            public DotNettyServerMessageSender(ITransportMessageEncoder transportMessageEncoder, IChannelHandlerContext context) : base(transportMessageEncoder)
            {
                _context = context;
            }
    
            #region Implementation of IMessageSender
    
            /// <summary>
            /// 发送消息。
            /// </summary>
            /// <param name="message">消息内容。</param>
            /// <returns>一个任务。</returns>
            public async Task SendAsync(TransportMessage message)
            {
                var buffer = GetByteBuffer(message);
                await _context.WriteAsync(buffer);
            }
    
            /// <summary>
            /// 发送消息并清空缓冲区。
            /// </summary>
            /// <param name="message">消息内容。</param>
            /// <returns>一个任务。</returns>
            public async Task SendAndFlushAsync(TransportMessage message)
            {
                var buffer = GetByteBuffer(message);
                await _context.WriteAndFlushAsync(buffer);
            }
    
            #endregion Implementation of IMessageSender
        }

    客户端:

    /// <summary>
        /// 基于DotNetty客户端的消息发送者。
        /// </summary>
        public class DotNettyMessageClientSender : DotNettyMessageSender, IMessageSender, IDisposable
        {
            private readonly IChannel _channel;
    
            public DotNettyMessageClientSender(ITransportMessageEncoder transportMessageEncoder, IChannel channel) : base(transportMessageEncoder)
            {
                _channel = channel;
            }
    
            #region Implementation of IDisposable
    
            /// <summary>Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.</summary>
            public void Dispose()
            {
                Task.Run(async () =>
                {
                    await _channel.DisconnectAsync();
                }).Wait();
            }
    
            #endregion Implementation of IDisposable
    
            #region Implementation of IMessageSender
    
            /// <summary>
            /// 发送消息。
            /// </summary>
            /// <param name="message">消息内容。</param>
            /// <returns>一个任务。</returns>
            public async Task SendAsync(TransportMessage message)
            {
                var buffer = GetByteBuffer(message);
                await _channel.WriteAndFlushAsync(buffer);
            }
    
            /// <summary>
            /// 发送消息并清空缓冲区。
            /// </summary>
            /// <param name="message">消息内容。</param>
            /// <returns>一个任务。</returns>
            public async Task SendAndFlushAsync(TransportMessage message)
            {
                var buffer = GetByteBuffer(message);
                await _channel.WriteAndFlushAsync(buffer);
            }
    
            #endregion Implementation of IMessageSender
        }

    4. 客户端工厂(绿色框)

    为啥没有服务端工厂呢,因为服务端是由服务端主机(Host)直接创建的,主机直接调用监听器监听端口。既然我们重写了通信过程,就不能用微软原有的WebHost,后续会讲到如何搭建自己的主机。

    客户端工厂用来创建客户端,然后与服务端主机通信。

    上层接口:

        /// <summary>
        /// 一个抽象的传输客户端工厂。
        /// </summary>
        public interface ITransportClientFactory
        {
            /// <summary>
            /// 创建客户端。
            /// </summary>
            /// <param name="endPoint">终结点。</param>
            /// <returns>传输客户端实例。</returns>
            Task<ITransportClient> CreateClientAsync(EndPoint endPoint);
        }
        /// <summary>
        /// 一个抽象的传输客户端。
        /// </summary>
        public interface ITransportClient
        {
            /// <summary>
            /// 发送消息。
            /// </summary>
            /// <param name="message">远程调用消息模型。</param>
            /// <returns>远程调用消息的传输消息。</returns>
            Task SendAsync(TransportMessage transportMessage);
        }

    实现类:

        /// <summary>
        /// 基于DotNetty的传输客户端工厂。
        /// </summary>
        public class DotNettyTransportClientFactory : ITransportClientFactory, IDisposable
        {
            #region Field
    
            private readonly ITransportMessageEncoder _transportMessageEncoder;
            private readonly ITransportMessageDecoder _transportMessageDecoder;
            private readonly ILogger<DotNettyTransportClientFactory> _logger;
            private readonly IServiceExecutor _serviceExecutor;
            private readonly ConcurrentDictionary<EndPoint, Lazy<Task<ITransportClient>>> _clients = new ConcurrentDictionary<EndPoint, Lazy<Task<ITransportClient>>>();
            private readonly Bootstrap _bootstrap;
    
            private static readonly AttributeKey<IMessageSender> messageSenderKey = AttributeKey<IMessageSender>.ValueOf(typeof(DotNettyTransportClientFactory), nameof(IMessageSender));
            private static readonly AttributeKey<IMessageListener> messageListenerKey = AttributeKey<IMessageListener>.ValueOf(typeof(DotNettyTransportClientFactory), nameof(IMessageListener));
            private static readonly AttributeKey<EndPoint> origEndPointKey = AttributeKey<EndPoint>.ValueOf(typeof(DotNettyTransportClientFactory), nameof(EndPoint));
    
            #endregion Field
    
            #region Constructor
    
            public DotNettyTransportClientFactory(ITransportMessageCodecFactory codecFactory, ILogger<DotNettyTransportClientFactory> logger)
                : this(codecFactory,  logger, null)
            {
            }
    
            public DotNettyTransportClientFactory(ITransportMessageCodecFactory codecFactory, ILogger<DotNettyTransportClientFactory> logger, IServiceExecutor serviceExecutor)
            {
                _transportMessageEncoder = codecFactory.GetEncoder();
                _transportMessageDecoder = codecFactory.GetDecoder();
                _logger = logger;
                _serviceExecutor = serviceExecutor;
                _bootstrap = GetBootstrap();
                _bootstrap.Handler(new ActionChannelInitializer<ISocketChannel>(c =>
                {
                    var pipeline = c.Pipeline;
                    pipeline.AddLast(new LengthFieldPrepender(4));
                    pipeline.AddLast(new LengthFieldBasedFrameDecoder(int.MaxValue, 0, 4, 0, 4));
                    pipeline.AddLast(new TransportMessageChannelHandlerAdapter(_transportMessageDecoder));
                    pipeline.AddLast(new DefaultChannelHandler(this));
                }));
            }
    
            #endregion Constructor
    
            #region Implementation of ITransportClientFactory
    
            /// <summary>
            /// 创建客户端。
            /// </summary>
            /// <param name="endPoint">终结点。</param>
            /// <returns>传输客户端实例。</returns>
            public async Task<ITransportClient> CreateClientAsync(EndPoint endPoint)
            {
                var key = endPoint;
                if (_logger.IsEnabled(LogLevel.Debug))
                    _logger.LogDebug($"准备为服务端地址:{key}创建客户端。");
                try
                {
                    return await _clients.GetOrAdd(key
                        , k => new Lazy<Task<ITransportClient>>(async () =>
                        {
                            //客户端对象
                            var bootstrap = _bootstrap;
                            //异步连接返回channel
                            var channel = await bootstrap.ConnectAsync(k);
                            var messageListener = new DotNettyClientMessageListener();
                            //设置监听
                            channel.GetAttribute(messageListenerKey).Set(messageListener);
                            //实例化发送者
                            var messageSender = new DotNettyMessageClientSender(_transportMessageEncoder, channel);
                            //设置channel属性
                            channel.GetAttribute(messageSenderKey).Set(messageSender);
                            channel.GetAttribute(origEndPointKey).Set(k);
                            //创建客户端
                            var client = new DotNettyTransportClient(messageSender, messageListener, _logger, _serviceExecutor);
                            return client;
                        }
                        )).Value;//返回实例
                }
                catch
                {
                    throw;
                }
            }
    
            #endregion Implementation of ITransportClientFactory
    
            #region Implementation of IDisposable
    
            /// <summary>Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.</summary>
            public void Dispose()
            {
                foreach (var client in _clients.Values.Where(i => i.IsValueCreated))
                {
                    (client.Value as IDisposable)?.Dispose();
                }
            }
    
            #endregion Implementation of IDisposable
    
            private static Bootstrap GetBootstrap()
            {
                IEventLoopGroup group;
    
                var bootstrap = new Bootstrap();
    
                group = new MultithreadEventLoopGroup();
                bootstrap.Channel<TcpServerSocketChannel>();
                
                bootstrap
                    .Channel<TcpSocketChannel>()
                    .Option(ChannelOption.TcpNodelay, true)
                    .Option(ChannelOption.Allocator, PooledByteBufferAllocator.Default)
                    .Group(group);
    
                return bootstrap;
            }
    
            protected class DefaultChannelHandler : ChannelHandlerAdapter
            {
                private readonly DotNettyTransportClientFactory _factory;
    
                public DefaultChannelHandler(DotNettyTransportClientFactory factory)
                {
                    this._factory = factory;
                }
    
                #region Overrides of ChannelHandlerAdapter
    
                public override void ChannelInactive(IChannelHandlerContext context)
                {
                    _factory._clients.TryRemove(context.Channel.GetAttribute(origEndPointKey).Get(), out var value);
                }
    
                public override void ChannelRead(IChannelHandlerContext context, object message)
                {
                    var transportMessage = message as TransportMessage;
    
                    var messageListener = context.Channel.GetAttribute(messageListenerKey).Get();
                    var messageSender = context.Channel.GetAttribute(messageSenderKey).Get();
                    messageListener.OnReceived(messageSender, transportMessage);
                }
    
                #endregion Overrides of ChannelHandlerAdapter
            }
        }
        /// <summary>
        /// 一个默认的传输客户端实现。
        /// </summary>
        public class DotNettyTransportClient : ITransportClient, IDisposable
        {
            #region Field
    
            private readonly IMessageSender _messageSender;
            private readonly IMessageListener _messageListener;
            private readonly ILogger _logger;
            private readonly IServiceExecutor _serviceExecutor;
    
            #endregion Field
    
            #region Constructor
    
            public DotNettyTransportClient(IMessageSender messageSender, IMessageListener messageListener, ILogger logger, IServiceExecutor serviceExecutor)
            {
                _messageSender = messageSender;
                _messageListener = messageListener;
                _logger = logger;
                _serviceExecutor = serviceExecutor;
                messageListener.Received += MessageListener_Received;
            }
    
            #endregion Constructor
    
            #region Implementation of ITransportClient
    
            /// <summary>
            /// 发送消息。
            /// </summary>
            /// <param name="message">远程调用消息模型。</param>
            /// <returns>远程调用消息的传输消息。</returns>
            public async Task SendAsync(TransportMessage transportMessage)
            {
                try
                {
                    if (_logger.IsEnabled(LogLevel.Debug))
                        _logger.LogDebug("准备发送消息。");
    
                    try
                    {
                        //发送
                        await _messageSender.SendAndFlushAsync(transportMessage);
                    }
                    catch (Exception exception)
                    {
                        throw new Exception("与服务端通讯时发生了异常。", exception);
                    }
    
                    if (_logger.IsEnabled(LogLevel.Debug))
                        _logger.LogDebug("消息发送成功。");
                    
                }
                catch (Exception exception)
                {
                    if (_logger.IsEnabled(LogLevel.Error))
                        _logger.LogError(null,exception, "消息发送失败。");
                    throw;
                }
            }
    
            #endregion Implementation of ITransportClient
    
            #region Implementation of IDisposable
    
            /// <summary>Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.</summary>
            public void Dispose()
            {
                (_messageSender as IDisposable)?.Dispose();
                (_messageListener as IDisposable)?.Dispose();
            }
    
            #endregion Implementation of IDisposable
    
            #region Private Method
    
            private async Task MessageListener_Received(IMessageSender sender, TransportMessage message)
            {
                if (_logger.IsEnabled(LogLevel.Trace))
                    _logger.LogTrace("服务消费者接收到消息。");
                
                if (_serviceExecutor != null)
                    await _serviceExecutor.ExecuteAsync(sender, message);
            }
    
            #endregion Private Method
        }

    5. 序列化工具(白色框)

    上节中我们用MessagePack实现了序列化与反序列化,本节为通信,自然离不开消息序列化。

    需要继承自 DotNetty.Transport.Channels.ChannelHandlerAdapter,才能被 netty 调用:

    public class TransportMessageChannelHandlerAdapter : ChannelHandlerAdapter
        {
            private readonly ITransportMessageDecoder _transportMessageDecoder;
    
            public TransportMessageChannelHandlerAdapter(ITransportMessageDecoder transportMessageDecoder)
            {
                _transportMessageDecoder = transportMessageDecoder;
            }
    
            #region Overrides of ChannelHandlerAdapter
    
            public override void ChannelRead(IChannelHandlerContext context, object message)
            {
                var buffer = (IByteBuffer)message;
                var data = new byte[buffer.ReadableBytes];
                buffer.ReadBytes(data);
                var transportMessage = _transportMessageDecoder.Decode(data);
                context.FireChannelRead(transportMessage);
                ReferenceCountUtil.Release(buffer);
            }
    
            #endregion Overrides of ChannelHandlerAdapter
        }

    6. 通信主机(黄色框)

    用于启动通信监听端口

    内部包含消息监听器(_serverMessageListener)和消息执行器(_serverMessageListener)。

    接口:

    public interface ITransportHost : IDisposable
        {
            /// <summary>
            /// 启动主机。
            /// </summary>
            /// <param name="endPoint">主机终结点。</param>
            /// <returns>一个任务。</returns>
            Task StartAsync(EndPoint endPoint);
    
            /// <summary>
            /// 启动主机。
            /// </summary>
            /// <param name="endPoint">ip地址。</param>
            Task StartAsync(string ip, int port);
        }

    实现类:

    public class DotNettyTransportHost : ITransportHost
        {
            #region Field
    
            private IServiceExecutor _serviceExecutor;
            public IServiceExecutor ServiceExecutor { get => _serviceExecutor; }
            private readonly Func<EndPoint, Task<IMessageListener>> _messageListenerFactory;
            private IMessageListener _serverMessageListener;
    
            #endregion Field
    
            public DotNettyTransportHost(Func<EndPoint, Task<IMessageListener>> messageListenerFactory, IServiceExecutor serviceExecutor)
            {
                _messageListenerFactory = messageListenerFactory;
                _serviceExecutor = serviceExecutor;
            }
    
            /// <summary>Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.</summary>
            public void Dispose()
            {
                (_serverMessageListener as IDisposable)?.Dispose();
            }
    
            /// <summary>
            /// 启动主机。
            /// </summary>
            /// <param name="endPoint">主机终结点。</param>
            /// <returns>一个任务。</returns>
            public async Task StartAsync(EndPoint endPoint)
            {
                if (_serverMessageListener != null)
                    return;
                _serverMessageListener = await _messageListenerFactory(endPoint);
                _serverMessageListener.Received += MessageListener_Received;
            }
    
            public async Task StartAsync(string ip, int port)
            {
                if (_serverMessageListener != null)
                    return;
                _serverMessageListener = await _messageListenerFactory(new IPEndPoint(IPAddress.Parse(ip), port));
                _serverMessageListener.Received += MessageListener_Received;
                //await StartAsync(new IPEndPoint(IPAddress.Parse(ip), port));
            }
    
            /// <summary>
            /// 监听并回调
            /// </summary>
            /// <param name="sender">消息发送器</param>
            /// <param name="message">监听到的消息</param>
            /// <returns></returns>
            private async Task MessageListener_Received(IMessageSender sender, TransportMessage message)
            {
                await _serviceExecutor.ExecuteAsync(sender, message);
            }
        }
  • 相关阅读:
    在Linux中安装Oracle(较详细图解)
    SecureCRT
    MHA配置文件说明
    MySQL建表规范与常见问题 (go)
    Shell编程时常用的系统文件(转)
    Leetcode: Excel Sheet Column Title
    Leetcode: Find Peak Element
    Leetcode: Intersection of Two Linked Lists
    Leetcode: Majority Element
    Summary: Class Variable vs. Instance Variable && Class Method
  • 原文地址:https://www.cnblogs.com/hongwei918/p/11302181.html
Copyright © 2020-2023  润新知