upm_guru_kcp/Runtime/csharp-kcp/dotNetty-kcp/KcpClient.cs

208 lines
7.1 KiB
C#
Raw Permalink Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

using System;
using System.Net;
using System.Net.Sockets;
using base_kcp;
using DotNetty.Transport.Bootstrapping;
using DotNetty.Transport.Channels;
using DotNetty.Transport.Channels.Sockets;
using dotNetty_kcp.thread;
using fec;
using fec.fec;
namespace dotNetty_kcp
{
/**
* kcp客户端
* 客户端使用方式:
* 1,与服务器tcp通讯得到conv
* 2,kcp通过conv标识与服务器通讯
* 3,客户端发现网络断开重连之后必须通过kcp发送一个心跳包出去 用于服务器确定客户端的出口地址
* 4,客户端需要最少每60秒发送一个心跳数据包服务端收到后回复客户端用于 路由表记录映射信息
*/
public class KcpClient
{
private Bootstrap bootstrap;
private IExecutorPool _executorPool;
private IChannelManager _channelManager;
private IEventLoopGroup _eventLoopGroup;
private IScheduleThread _scheduleThread;
private static IChannel bindLocal(Bootstrap bootstrap, EndPoint localAddress = null)
{
if (localAddress == null)
{
localAddress = new IPEndPoint(IPAddress.Any, 0);
}
return bootstrap.BindAsync(localAddress).Result;
}
public System.Threading.Tasks.Task<IChannel> BindLocal()
{
var localAddress = new IPEndPoint(IPAddress.Any, 0);
//var local_ip = GetLocalIPAddress();
//var localAddress = new IPEndPoint(IPAddress.Parse(local_ip), 0);
return bootstrap.BindAsync(localAddress);
}
public static string GetLocalIPAddress()
{
var host = Dns.GetHostEntry(Dns.GetHostName());
foreach (var ip in host.AddressList)
{
if (ip.AddressFamily == AddressFamily.InterNetwork)
{
return ip.ToString();
}
}
throw new Exception("No network adapters with an IPv4 address in the system!");
}
public void init(ChannelConfig channelConfig, ExecutorPool executorPool, IEventLoopGroup eventLoopGroup)
{
if (channelConfig.UseConvChannel)
{
var convIndex = 0;
if (channelConfig.Crc32Check)
{
convIndex += Ukcp.HEADER_CRC;
}
if (channelConfig.FecDataShardCount != 0 && channelConfig.FecParityShardCount != 0)
{
convIndex += Fec.fecHeaderSizePlus2;
}
_channelManager = new ClientConvChannelManager(convIndex);
}
else
{
_channelManager = new ClientEndPointChannelManager();
}
//初始化线程池 创建一个线程就够了
_executorPool = executorPool;
_executorPool.CreateMessageExecutor();
_eventLoopGroup = eventLoopGroup;
_scheduleThread = new EventLoopScheduleThread();
bootstrap = new Bootstrap();
bootstrap.Group(_eventLoopGroup);
bootstrap.ChannelFactory(() => new SocketDatagramChannel(AddressFamily.InterNetwork));
bootstrap.Handler(new ActionChannelInitializer<SocketDatagramChannel>(channel =>
{
var pipeline = channel.Pipeline;
pipeline.AddLast(new ClientChannelHandler(_channelManager, channelConfig));
}));
}
public void init(ChannelConfig channelConfig)
{
var executorPool = new ExecutorPool();
executorPool.CreateMessageExecutor();
init(channelConfig, executorPool, new MultithreadEventLoopGroup());
}
/**
* 重连接口
* 使用旧的kcp对象出口ip和端口替换为新的
* 在4G切换为wifi等场景使用
* @param ukcp
*/
public void reconnect(Ukcp ukcp)
{
if (!(_channelManager is ServerConvChannelManager))
{
throw new Exception("reconnect can only be used in convChannel");
}
ukcp.IMessageExecutor.execute(new ReconnectTask(ukcp, bootstrap));
}
private class ReconnectTask : ITask
{
private readonly Ukcp _ukcp;
private readonly Bootstrap _bootstrap;
public ReconnectTask(Ukcp ukcp, Bootstrap bootstrap)
{
_ukcp = ukcp;
_bootstrap = bootstrap;
}
public void execute()
{
_ukcp.user().Channel.CloseAsync();
var iChannel = bindLocal(_bootstrap);
_ukcp.user().Channel = iChannel;
}
}
public Ukcp connect(IChannel localChannel, EndPoint remoteAddress, ChannelConfig channelConfig, KcpListener kcpListener)
{
KcpOutput kcpOutput = new KcpOutPutImp();
ReedSolomon reedSolomon = null;
if (channelConfig.FecDataShardCount != 0 && channelConfig.FecParityShardCount != 0)
{
reedSolomon = ReedSolomon.create(channelConfig.FecDataShardCount, channelConfig.FecParityShardCount);
}
var _messageExecutor = _executorPool.GetAutoMessageExecutor();
var ukcp = new Ukcp(kcpOutput, kcpListener, _messageExecutor, reedSolomon, channelConfig);
var user = new User(localChannel, remoteAddress, localChannel.LocalAddress);
ukcp.user(user);
_channelManager.New(localChannel.LocalAddress, ukcp, null);
_messageExecutor.execute(new ConnectTask(ukcp, kcpListener));
var scheduleTask = new ScheduleTask(_channelManager, ukcp, _scheduleThread);
_scheduleThread.schedule(scheduleTask, TimeSpan.FromMilliseconds(ukcp.getInterval()));
return ukcp;
}
/**
* 连接一个服务器
*/
public Ukcp connect(EndPoint localAddress, EndPoint remoteAddress, ChannelConfig channelConfig, KcpListener kcpListener)
{
var channel = bindLocal(bootstrap, localAddress);
return connect(channel, remoteAddress, channelConfig, kcpListener);
}
/**
* 连接一个服务器
*/
public Ukcp connect(EndPoint remoteAddress, ChannelConfig channelConfig, KcpListener kcpListener)
{
var channel = bindLocal(bootstrap);
return connect(channel, remoteAddress, channelConfig, kcpListener);
}
public void stop()
{
foreach (var ukcp in _channelManager.getAll())
{
ukcp.close();
}
_executorPool?.stop(false);
if (_eventLoopGroup != null && !_eventLoopGroup.IsShuttingDown)
{
_eventLoopGroup?.ShutdownGracefullyAsync().Wait();
}
_scheduleThread.stop();
}
}
}