DotNetty客户端断线重连

参考:https://blog.csdn.net/zuochao_2013/article/details/72872315

          https://blog.csdn.net/w592376568/article/details/79898062

          https://blog.csdn.net/u010739551/article/details/80607191

1、代码如下:

using System;
using System.Collections.Generic;
using DotNetty.Buffers;
using DotNetty.Transport.Bootstrapping;
using DotNetty.Transport.Channels;
using DotNetty.Transport.Channels.Sockets;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading.Tasks;
using System.IO;
using System.Threading;
using Test.Model;
using DotNetty.Handlers.Timeout;
using DotNetty.Handlers.Logging;

//https://blog.csdn.net/zuochao_2013/article/details/72872315
namespace Test
{
    public class ExliveNettyClient
    {
        MultithreadEventLoopGroup group;
        Bootstrap bootstrap;
        ExliveClientHandler handler;

        public ExliveNettyClient()
        {
            group = new MultithreadEventLoopGroup();
            bootstrap = new Bootstrap();
            handler = new ExliveClientHandler(this);
            bootstrap
                    .Group(group)
                    .Channel<TcpSocketChannel>()
                    .Option(ChannelOption.TcpNodelay, true)
                    .Option(ChannelOption.ConnectTimeout, TimeSpan.FromSeconds(3))
                    .Handler(new ActionChannelInitializer<ISocketChannel>(c =>
                    {
                        var pipeline = c.Pipeline;
                        //pipeline.AddLast("timeout", new IdleStateHandler(TimeSpan.FromSeconds(5), TimeSpan.FromSeconds(5), TimeSpan.FromSeconds(5)));
                        pipeline.AddLast(handler);
                    }));
            bootstrap.RemoteAddress(new IPEndPoint(IPAddress.Parse(AppConfig.ServerIP), AppConfig.ServerPort));

            RunClientAsync().Wait();
        }

        public async Task ConnectToServer()
        {
            try
            {
                IChannel clientChannel = null;
                try
                {                   
                    clientChannel = await bootstrap.ConnectAsync();
                }
                catch (Exception ex)
                {
                    System.Threading.Thread.Sleep(5000);
                    await ConnectToServer();
                }
            }
            finally
            {
                Console.ReadLine();
                await group.ShutdownGracefullyAsync(TimeSpan.FromMilliseconds(100), TimeSpan.FromSeconds(1));
            }
        }

        private async Task RunClientAsync()
        {
            await ConnectToServer();
        }
    }


    class ExliveClientHandler : ChannelHandlerAdapter, IExliveNetInterface
    {
        private int total = 0;

        /// <summary>
        /// 通道
        /// </summary>
        private IChannelHandlerContext context;

        /// <summary>
        /// 消息处理
        /// </summary>
        private MessageProcess mp;

        /// <summary>
        /// 客户端
        /// </summary>
        private ExliveNettyClient client;

        /// <summary>
        /// https://blog.csdn.net/w592376568/article/details/79898062
        /// 报错:is not a @Sharable handler
        /// 要么就是IsSharable设为true,要么就是每次都new一个新的handler的实例
        /// </summary>
        public override bool IsSharable => true;

        public ExliveClientHandler(ExliveNettyClient client)
        {
            this.client = client;

            mp = new MessageProcess(this);
        }

        public override void HandlerAdded(IChannelHandlerContext context)
        {
            this.context = context;

            base.HandlerAdded(context);
        }

        public override void ChannelRegistered(IChannelHandlerContext context)
        {            
            base.ChannelRegistered(context);
        }

        public override void ChannelUnregistered(IChannelHandlerContext context)
        {

        }

        public override void ChannelActive(IChannelHandlerContext context)
        {
            total = 0;

            mp.SendData(ExliveCmd.LOGIN);

            base.ChannelActive(context);
        }

        public override void ChannelInactive(IChannelHandlerContext context)
        {
            Reconnect(context);            

            base.ChannelInactive(context);
        }

        public override void ChannelRead(IChannelHandlerContext context, object message)
        {
            if (message is IByteBuffer buffer)
            {
                string data = buffer.ToString(System.Text.Encoding.GetEncoding("gb2312"));
                mp.AfterReceiveData(data);

                string recieveText = "recieve:" + context.Channel.RemoteAddress.ToString() + "-> " +
                                context.Channel.LocalAddress.ToString() + "\r\n" +
                                (string.IsNullOrEmpty(data) ? "" : data.Trim(new char[] { '\r', '\n' }));

                //Console.WriteLine(recieveText);
                Log.WriteLog(recieveText);
            }
        }

        public override void ChannelReadComplete(IChannelHandlerContext context) => context.Flush();

        public override void HandlerRemoved(IChannelHandlerContext context)
        {
            Reconnect(context);

            Console.WriteLine($"服务端{context}下线.");
            base.HandlerRemoved(context);
        }

        public override void ExceptionCaught(IChannelHandlerContext context, Exception exception)
        {
            Reconnect(context);

            base.ExceptionCaught(context, exception);
        }

        public override void UserEventTriggered(IChannelHandlerContext context, object evt)
        {
            if (evt is IdleStateEvent)
            {
                var eventState = evt as IdleStateEvent;
                if (eventState != null)
                {
                    switch (eventState.State)
                    {
                        case IdleState.ReaderIdle:
                            break;
                        case IdleState.WriterIdle:
                            // 长时间未写入数据
                            // 则发送心跳数据
                            // context.WriteAndFlushAsync();
                            mp.SendData(ExliveCmd.HEART);

                            break;
                        case IdleState.AllIdle:
                            break;
                    }
                }
            }
        }

        public void SendPackage(string data)
        {
            byte[] messageBytes = System.Text.Encoding.GetEncoding("gb2312").GetBytes(data);
            IByteBuffer initialMessage = Unpooled.Buffer(messageBytes.Length);
            initialMessage.WriteBytes(messageBytes);
            if (context != null)
                context.WriteAndFlushAsync(initialMessage);
        }

        public void SendPackage(object obj)
        {
            var sendMessage = Json.ToCamelCaseJson(obj);
            SendPackage(sendMessage);
        }

        public void Reconnect(IChannelHandlerContext context)
        {
            Interlocked.Increment(ref total);
            if (total == 1)
            {
                new Action(async () =>
                {
                    await this.client.ConnectToServer();
                }).BeginInvoke(null, null);
            }
        }
    }
}

2、事件顺序Added->Registered->Active, 如果连接成功会执行到Active,未成功会在ConnectToServer中获取到异常,再进行重连。已经连接到服务器后的断线,通过ExceptionCaught、ChannelInactive、HandlerRemoved获取,然后重连,重连时进行判断,只执行一次ConnectToServer。

3、加入 new IdleStateHandler后,会触发UserEventTriggered事件,可以该事件中进行心跳检测。

4、报错:is not a @Sharable handler,要么就是IsSharable设为true,要么就是每次都new一个新的handler的实例

5、demo下载地址https://download.csdn.net/download/XinShun/12094822

 

评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值