KTUCP实现:以KCP为应用层,TCP/UDP为多通道协议层
KCP是一个基于udp的快速可靠协议(udp),能以比 TCP浪费10%-20%的带宽的代价,换取平均延迟降低 30%-40%,且最大延迟降低三倍的传输效果。
基于原作者的开源项目的修改:https://github.com/l42111996/java-Kcp.git
原项目:
通信架构
应用层 <--> UDP <--> KCP
实现功能
- java版kcp基本实现
- 优化kcp的flush策略
- 基于事件驱动,利用多核性能
- 支持配置多种kcp参数
- 支持配置conv或address(ip+port)确定唯一连接
- 支持fec(降低延迟)
- 支持crc32校验
基于原项目的新增和优化:
通信架构
应用层
┌┴┐
UDP TCP ...(N个网络)
└┬┘
KCP
优化和新增
- 支持配置多个TCP/UDP底层网络服务
- 支持TCP和UDP通道切换
- 支持自定义配置底层网络的Netty参数
- 支持添加底层网络的自定义Handler
- 支持自定义编解码
- 支持切换KCP下层的网络
- 支持强制使用某一个网络发送数据
- 支持使用自定义时间服务(可以不用System.currentTimeMillis方法而使用自己系统的缓存时间系统)
根据原作者对KCP的使用建议 实际使用中,最好是通过TCP和UDP结合的方式使用:
- 中国网络情况特殊,可能出现UDP包被防火墙拦下
- TCP网络在使用LB的情况下,两端中的一端可能出现感知不到对方断开的情况
- 可通过TCP的可靠连接作为备用线路,UDP不通的情况下可使用备用TCP
结合以上需求,这套开源库的目的就是整合TCP和UDP网络到同一套KCP机制中,甚至可以支持启动多TCP多UDP服务。 并且最大程度的开放底层Netty配置权限,用户可根据自己的需求定制化自己的网络框架
欢迎大家使用,有任何bug以及优化需求,欢迎提issue讨论
https://hjcenry.com/ktucp/doc/
<dependency>
<groupId>io.github.hjcenry</groupId>
<artifactId>ktucp-net</artifactId>
<version>1.2</version>
</dependency>
ChannelConfig channelConfig = new ChannelConfig();
channelConfig.nodelay(true, 40, 2, true);
channelConfig.setSndWnd(512);
channelConfig.setRcvWnd(512);
channelConfig.setMtu(512);
channelConfig.setTimeoutMillis(10000);
channelConfig.setUseConvChannel(true);
// 这里可以配置大部分的参数
// ...
KtucpListener ktucpListener = new KtucpListener() {
@Override
public void onConnected(int netId, Uktucp uktucp) {
System.out.println("onConnected:" + uktucp);
}
@Override
public void handleReceive(Object object, Uktucp uktucp) throws Exception {
System.out.println("handleReceive:" + uktucp);
ByteBuf byteBuf = (ByteBuf) object;
// TODO read byteBuf
}
@Override
public void handleException(Throwable ex, Uktucp uktucp) {
System.out.println("handleException:" + uktucp);
ex.printStackTrace();
}
@Override
public void handleClose(Uktucp uktucp) {
System.out.println("handleClose:" + uktucp);
System.out.println("snmp:" + uktucp.getSnmp());
}
@Override
public void handleIdleTimeout(Uktucp uktucp) {
System.out.println("handleIdleTimeout:" + uktucp);
}
};
KtucpServer ktucpServer = new KtucpServer();
// 默认启动一个UDP端口
ktucpServer.init(ktucpListener, channelConfig, 8888);
[main] INFO com.hjcenry.log.KtucpLog - KtucpServer Start :
===========================================================
TcpNetServer{bindPort=8888, bossGroup.num=1, ioGroup.num=8}
UdpNetServer{bindPort=8888, bossGroup.num=8, ioGroup.num=0}
===========================================================
ChannelConfig channelConfig = new ChannelConfig();
// 客户端比服务端多一个设置convId
channelConfig.setConv(1);
channelConfig.nodelay(true, 40, 2, true);
channelConfig.setSndWnd(512);
channelConfig.setRcvWnd(512);
channelConfig.setMtu(512);
channelConfig.setTimeoutMillis(10000);
channelConfig.setUseConvChannel(true);
// 这里可以配置大部分的参数
// ...
KtucpListener ktucpListener = new KtucpListener() {
@Override
public void onConnected(int netId, Uktucp uktucp) {
System.out.println("onConnected:" + uktucp);
}
@Override
public void handleReceive(Object object, Uktucp uktucp) throws Exception {
System.out.println("handleReceive:" + uktucp);
ByteBuf byteBuf = (ByteBuf) object;
// TODO read byteBuf
}
@Override
public void handleException(Throwable ex, Uktucp uktucp) {
System.out.println("handleException:" + uktucp);
ex.printStackTrace();
}
@Override
public void handleClose(Uktucp uktucp) {
System.out.println("handleClose:" + uktucp);
System.out.println("snmp:" + uktucp.getSnmp());
}
@Override
public void handleIdleTimeout(Uktucp uktucp) {
System.out.println("handleIdleTimeout:" + uktucp);
}
};
// 默认启动一个UDP端口
KtucpClient ktucpClient = new KtucpClient();
ktucpClient.init(ktucpListener, channelConfig, new InetSocketAddress("127.0.0.1", 8888));
[main] INFO com.hjcenry.log.KtucpLog - KtucpClient Connect :
===========================================================
TcpNetClient{connect= local:null -> remote:/127.0.0.1:8888, ioGroup.num=8}
UdpNetClient{connect= local:null -> remote:/127.0.0.1:8888, ioGroup.num=0}
===========================================================
以上是简单的示例,可快速启动ktucp服务和客户端。关于多网络的详细使用方法,可参考下面的例子3和4
- 客户端实现:该框架仅实现了Java版本,其他版本的客户端需要根据此通信架构进行实现(单纯使用UDP通道的话,也是能和原版KCP兼容的)
- convId的唯一性:因不能校验udp的address或tcp的channel,只能依靠convId获取唯一Uktucp对象
- convId的有效性校验:需要判断convId的来源,防止伪造。因convId从消息包读取,框架底层对TCP连接的消息包做了Channel唯一性判断处理,但UDP暂时没有好的判断方法。如果有安全性需求,应用层需要自己做一个防伪检测,比如服务端给客户端分配一个token,客户端在每个消息包头把token带过来,服务端对每个包头的token做一个校验
- 处理好多网络连接管理:因底层配置较为开放,默认为KCP超时即断开所有连接,如有其他配置,请注意连接释放时机
- https://github.com/skywind3000/kcp 原版c版本的kcp
- https://github.com/xtaci/kcp-go go版本kcp,有大量优化
- https://github.com/Backblaze/JavaReedSolomon java版本fec
- https://github.com/LMAX-Exchange/disruptor 高性能的线程间消息传递库
- https://github.com/JCTools/JCTools 高性能并发库
- https://github.com/szhnet/kcp-netty java版本的一个kcp
- https://github.com/l42111996/csharp-kcp 基于dotNetty的c#版本kcp,完美兼容
- https://github.com/l42111996/java-Kcp.git 此开源库的原版本
- 微信:hjcenry