参考: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()
.Option(ChannelOption.TcpNodelay, true)
.Option(ChannelOption.ConnectTimeout, TimeSpan.FromSeconds(3))
.Handler(new ActionChannelInitializer(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;
///
/// 通道
///
private IChannelHandlerContext context;
///
/// 消息处理
///
private MessageProcess mp;
///
/// 客户端
///
private ExliveNettyClient client;
///
/// https://blog.csdn.net/w592376568/article/details/79898062
/// 报错:is not a @Sharable handler
/// 要么就是IsSharable设为true,要么就是每次都new一个新的handler的实例
///
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