当前位置: 首页 > 工具软件 > Dotnetty > 使用案例 >

Dotnetty TcpClient 自动重连服务器

申屠飞
2023-12-01

目标 

    使用Dotnetty 写一个Tcp client,在网络故障或服务器关闭tcp监听再开启后能够自动重连,即保持连接状态。

主要问题

    遇到了发声 I/O 错误(I/O Error Occured)问题,在github的Dotnetty项目也有人遇到,作为一个问题仍然OPEN。

自动重连主要代码

    在连接未OPEN、连接关闭、连接异常时,等待5秒后重连

protected override async Task<bool> connectAsync()
{
	if (eventLoopGroup == null) eventLoopGroup = new MultithreadEventLoopGroup();

	X509Certificate2 tlsCertificate = null;
	string targetHost = null;
	if (isSsl)
	{
		tlsCertificate = new X509Certificate2(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, sslKeyFile), sslPassword);
		targetHost = tlsCertificate.GetNameInfo(X509NameType.DnsName, false);
	}
	var bootstrap = new Bootstrap();
	bootstrap
		.Group(eventLoopGroup)
		.Channel<TcpSocketChannel>()
		.Option(ChannelOption.TcpNodelay, true)
		.Handler(new ActionChannelInitializer<IChannel>(channel =>
		{
			IChannelPipeline pipeline = channel.Pipeline;
			if (tlsCertificate != null)
			{
				pipeline.AddLast(TlsHandler.Server(tlsCertificate));
			}
			if (enableNettyLog) pipeline.AddLast(new LoggingHandler(loggerSource));
			pipeline.AddLast(handlerList.ToArray());
		}));

	try
	{
		IChannel clientChannel = await bootstrap.ConnectAsync(serverEndPoint);
		if (clientChannel.Open)
		{
			channel = clientChannel;
			Logger.Info($"与 {channel.RemoteAddress} 建立连接", loggerSource);
			_ = clientChannel.CloseCompletion.ContinueWith((t, s) =>
			{
				Logger.Info($"与 {channel.RemoteAddress} 断开连接", loggerSource);
				scheduleReconnect();//auto reconnect when channel closed.
			}, this, TaskContinuationOptions.ExecuteSynchronously);
		}
		else
		{
			Logger.Warning($"clientChannel not open, retry after {reconnectDelay.Seconds} s", loggerSource);
			scheduleReconnect();//auto reconnect when connect failed.
		}
	}
	catch(Exception ex)
	{
		Logger.Warning($"can't connect to {serverIp} port:{serverPort}: {ex.Message}, retry after {reconnectDelay.Seconds} s", loggerSource); 
		scheduleReconnect(); //auto reconnect when connect error.
	}
	return true;
}

private void scheduleReconnect()
{
	if (!disconnected)
	{
		eventLoopGroup.Schedule(async () => await connectAsync(), reconnectDelay);
	}
}

排除I/O错误

    在.net6 或者 .net framework4.6.1/4.7.2 的Console Application 中,都能够自动重连,唯独.net framework Class Library 不行,我用dotnetty的源代码跟踪2个小时,发现不是dotnetty重连问题,是我的Handler在重连成功后ChannelActive里有个发送握手报文helloMsg的逻辑,而该握手报文的IByteBuffer在重连时已经被释放freeed,是不能重复使用的,I/O 错误就是这里抛出的。

    只需要在ChannelActive里调用Unpooled.WrappedBuffer(helloMsg)就没问题了。

ctor()
{
    helloBuffer = Unpooled.WrappedBuffer(helloMsg);// helloMsg doesn't change
}
public override void ChannelActive(IChannelHandlerContext ctx)
{
    this.ctx = ctx;
    if (helloBuffer != null)
    {
        ctx.WriteAndFlushAsync(helloBuffer);
    }
}

 类似资料: