DotNetty使用之心跳機制

因爲DotNetty是從java的Netty框架仿寫過來的,介紹的文檔特別少,加之官方也沒有提供api文檔,所以之前一直不理解心跳的用法。最近忙裏偷閒,稍稍研究了一番,終於有點明白了。

現在將代碼複製上來,留作日後查看(ps:精髓都在代碼裏):

Uptime.Client:

public class Program
    {
        const string HOST = "127.0.0.1";
        const int PORT = 8045;
        // Sleep 5 seconds before a reconnection attempt.
        public const int RECONNECT_DELAY = 5;
        // Reconnect when the server sends nothing for 10 seconds.
        private const int READ_TIMEOUT = 10;

        private static UptimeClientHandler handler = new UptimeClientHandler();
        private static Bootstrap bs = new Bootstrap();

        static void Main(string[] args)
        {
            EventLoopGroup group = new EventLoopGroup();
            bs.Group(group)
                .Channel<TcpSocketChannel>()
                .RemoteAddress(HOST, PORT)
                 .Handler(new ActionChannelInitializer<ISocketChannel>(channel =>
                 {
                     //IdleStateHandler心跳檢測處理器,添加自定義處理Handler類實現userEventTriggered()方法作爲超時事件的邏輯處理
                     //IdleStateHandler心跳檢測每十秒進行一次讀檢測,如果十秒內ChannelRead()方法未被調用則觸發一次userEventTrigger()方法.
                     IChannelPipeline pipeline = channel.Pipeline;
                     pipeline.AddLast(new IdleStateHandler(READ_TIMEOUT, 0, 0), handler);//第一個參數爲讀,第二個爲寫,第三個爲讀寫全部

                 }));
        }
    }

Uptime.Client.UptimeClientHandler:

 public class UptimeClientHandler : SimpleChannelInboundHandler<object>
    {
        long startTime = -1;

        //ChannelActive:活躍狀態,可接收和發送數據
        public override void ChannelActive(IChannelHandlerContext ctx)
        {
            if (startTime < 0)
            {
                startTime = GetTimeStamp();
            }
           Console.WriteLine("Connected to: " + ctx.Channel.RemoteAddress);
        }

        protected override void ChannelRead0(IChannelHandlerContext context, object message)
        {
            var byteBuffer = message as IByteBuffer;
            if (byteBuffer != null)
            {
                Console.WriteLine("Received from server: " + byteBuffer.ToString(Encoding.UTF8));
            }
            context.WriteAsync(message);
        }

        public override void UserEventTriggered(IChannelHandlerContext ctx, object evt)
        {
            if (!(evt is IdleStateEvent)) 
            {
                return;
            }

            IdleStateEvent e = evt as IdleStateEvent;
            if (e.State == IdleState.ReaderIdle)
            {
                // The connection was OK but there was no traffic for last period.
                Console.WriteLine("Disconnecting due to no inbound traffic");
                ctx.CloseAsync();
            }
        }

        //channelInactive: 處於非活躍狀態,沒有連接到遠程主機。
        public override void ChannelInactive(IChannelHandlerContext context)
        {
            Console.WriteLine("Disconnected from: " + context.Channel.RemoteAddress);
        }

        //channelUnregistered: 已創建但未註冊到一個 EventLoop。
        public override void ChannelUnregistered(IChannelHandlerContext context)
        {
            
        }
  
        public override void ExceptionCaught(IChannelHandlerContext context, Exception exception)
        {
            Console.WriteLine("Exception: " + exception);
            context.CloseAsync();
        }

        /// <summary>
        /// 獲取時間戳
        /// </summary>
        /// <returns></returns>
        private long GetTimeStamp()
        {
            TimeSpan ts = DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0, 0);
            return Convert.ToInt64(ts.TotalMilliseconds);
        }

    }

Uptime.Server:

class Program
    {
        private static readonly int PORT = 8045;
        private static readonly UptimeServerHandler handler = new UptimeServerHandler();

        static void Main(string[] args) => RunServerAsync().Wait();

        static async Task RunServerAsync()
        {
            IEventLoopGroup bossGroup = new MultithreadEventLoopGroup(1);
            IEventLoopGroup workerGroup = new MultithreadEventLoopGroup();
            try
            {
                ServerBootstrap b = new ServerBootstrap();
                b.Group(bossGroup, workerGroup)
                    .Channel<TcpServerSocketChannel>()
                      .Handler(new LoggingHandler("SRV-LSTN"))
                      .ChildHandler(new ActionChannelInitializer<IChannel>(channel =>
                      {
                          //工作線程連接器 是設置了一個管道,服務端主線程所有接收到的信息都會通過這個管道一層層往下傳輸
                          //同時所有出棧的消息也要這個管道的所有處理器進行一步步處理
                          IChannelPipeline pipeline = channel.Pipeline;
                          pipeline.AddLast("Uptime", handler);
                      }));

                // Bind and start to accept incoming connections.
                IChannel boundChannel = await b.BindAsync(PORT);

                // Wait until the server socket is closed.
                // In this example, this does not happen, but you can do that to gracefully
                // shut down your server.
                await boundChannel.CloseAsync();
            }
            finally
            {
                await Task.WhenAll(
                     bossGroup.ShutdownGracefullyAsync(TimeSpan.FromMilliseconds(100), TimeSpan.FromSeconds(1)),
                     workerGroup.ShutdownGracefullyAsync(TimeSpan.FromMilliseconds(100), TimeSpan.FromSeconds(1)));
            }
        }
    }

Uptime.Server.UptimeServerHandler:

 public class UptimeServerHandler : SimpleChannelInboundHandler<object> {
 
        protected override void ChannelRead0(IChannelHandlerContext ctx, object msg)
        {
            // discard
        }

        //捕獲 異常,並輸出到控制檯後斷開鏈接,提示:客戶端意外斷開鏈接,也會觸發
        public override void ExceptionCaught(IChannelHandlerContext context, Exception exception)
        {
            Console.WriteLine("Exception: " + exception);
            context.CloseAsync();
        }
    }

最後,附上參考文檔鏈接:DotNetty系列三:編碼解碼器,IdleStateHandler心跳機制,羣發

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章