├── .gitignore ├── Benchmark.md ├── LICENSE ├── QA.md ├── README.en.md ├── README.md ├── java-Kcp.iml ├── kcp-base ├── kcp-base.iml ├── pom.xml └── src │ ├── main │ └── java │ │ ├── internal │ │ ├── CodecOutputList.java │ │ ├── ReItrCollection.java │ │ ├── ReItrHashMap.java │ │ ├── ReItrLinkedList.java │ │ ├── ReItrSet.java │ │ ├── ReusableIterator.java │ │ ├── ReusableListIterator.java │ │ └── package-info.java │ │ ├── kcp │ │ ├── ChannelConfig.java │ │ ├── ClientAddressChannelManager.java │ │ ├── ClientChannelHandler.java │ │ ├── ClientConvChannelManager.java │ │ ├── Crc32Decode.java │ │ ├── Crc32Encode.java │ │ ├── FecOutPut.java │ │ ├── IChannelManager.java │ │ ├── IKcp.java │ │ ├── Kcp.java │ │ ├── KcpClient.java │ │ ├── KcpListener.java │ │ ├── KcpOutPutImp.java │ │ ├── KcpOutput.java │ │ ├── KcpServer.java │ │ ├── ReadTask.java │ │ ├── ScheduleTask.java │ │ ├── ServerAddressChannelManager.java │ │ ├── ServerChannelHandler.java │ │ ├── ServerConvChannelManager.java │ │ ├── Ukcp.java │ │ ├── User.java │ │ └── WriteTask.java │ │ ├── test │ │ ├── DelayPacket.java │ │ ├── LatencySimulator.java │ │ ├── TestCrc32.java │ │ ├── TestEncryption.java │ │ └── Utils.java │ │ └── threadPool │ │ ├── IMessageExecutor.java │ │ ├── IMessageExecutorPool.java │ │ ├── ITask.java │ │ ├── disruptor │ │ ├── DisruptorExecutorPool.java │ │ ├── DisruptorSingleExecutor.java │ │ ├── DisruptorThread.java │ │ ├── DistriptorEventFactory.java │ │ ├── DistriptorEventHandler.java │ │ └── DistriptorHandler.java │ │ ├── netty │ │ ├── NettyMessageExecutor.java │ │ └── NettyMessageExecutorPool.java │ │ └── order │ │ ├── IOrderTask.java │ │ ├── OrderedThreadPoolExecutor.java │ │ ├── OrderedThreadSession.java │ │ └── waiteStrategy │ │ ├── BlockingWaitConditionStrategy.java │ │ ├── BusySpinWaitConditionStrategy.java │ │ ├── SleepingWaitConditionStrategy.java │ │ ├── WaitCondition.java │ │ ├── WaitConditionStrategy.java │ │ └── YieldingWaitConditionStrategy.java │ └── test │ └── java │ └── main │ ├── BytebufBenchmark.java │ ├── KcpThreadBenchmark.java │ ├── MpmcBenchmark.java │ ├── MpscBenchmark.java │ ├── MpscaBenchmark.java │ ├── SignalBenchmark.java │ ├── TestOrderThreadPoolOrder.java │ ├── TestThreadLoop.java │ └── VolatileAtomicBenchmark.java ├── kcp-example ├── README.md ├── kcp-example.iml ├── pom.xml └── src │ └── main │ └── java │ └── test │ ├── Kcp4GoExampleClient.java │ ├── Kcp4sharpExampleServer.java │ ├── KcpDisconnectExampleClient.java │ ├── KcpDisconnectExampleServer.java │ ├── KcpIdleExampleClient.java │ ├── KcpIdleExampleServer.java │ ├── KcpMultiplePingPongExampleClient.java │ ├── KcpMultiplePingPongExampleServer.java │ ├── KcpPingPongExampleClient.java │ ├── KcpPingPongExampleServer.java │ ├── KcpReconnectExampleClient.java │ ├── KcpReconnectExampleServer.java │ ├── KcpRttExampleClient.java │ ├── KcpRttExampleServer.java │ ├── SpeedExampleClient.java │ └── SpeedExampleServer.java ├── kcp-fec ├── kcp-fec.iml ├── pom.xml └── src │ ├── main │ └── java │ │ └── com │ │ └── backblaze │ │ └── erasure │ │ ├── ByteInputOutputExpCodingLoop.java │ │ ├── ByteInputOutputTableCodingLoop.java │ │ ├── ByteOutputInputExpCodingLoop.java │ │ ├── ByteOutputInputTableCodingLoop.java │ │ ├── CodingLoop.java │ │ ├── CodingLoopBase.java │ │ ├── FecAdapt.java │ │ ├── Galois.java │ │ ├── IFecDecode.java │ │ ├── IFecEncode.java │ │ ├── InputByteOutputExpCodingLoop.java │ │ ├── InputByteOutputTableCodingLoop.java │ │ ├── InputOutputByteExpCodingLoop.java │ │ ├── InputOutputByteTableCodingLoop.java │ │ ├── Matrix.java │ │ ├── OutputByteInputExpCodingLoop.java │ │ ├── OutputByteInputTableCodingLoop.java │ │ ├── OutputInputByteExpCodingLoop.java │ │ ├── OutputInputByteTableCodingLoop.java │ │ ├── ReedSolomon.java │ │ ├── ReedSolomonBenchmark.java │ │ ├── SampleDecoder.java │ │ ├── SampleEncoder.java │ │ ├── bytebuf │ │ ├── ByteBufCodingLoop.java │ │ ├── ByteBufCodingLoopBase.java │ │ ├── InputOutputByteBufHeapTableCodingLoop.java │ │ └── InputOutputByteBufTableCodingLoop.java │ │ ├── fec │ │ ├── Fec.java │ │ ├── FecDecode.java │ │ ├── FecEncode.java │ │ ├── FecException.java │ │ ├── FecPacket.java │ │ ├── MyArrayList.java │ │ └── Snmp.java │ │ └── fecNative │ │ ├── FecDecode.java │ │ ├── FecEncode.java │ │ ├── ReedSolomonC.java │ │ ├── ReedSolomonNative.java │ │ └── native │ │ ├── jni.dll │ │ └── libjni.dylib │ └── test │ └── java │ └── com │ └── backblaze │ └── erasure │ ├── BytebufBenchmark.java │ ├── BytebufBenchmarkNative.java │ ├── FecCorrect.java │ ├── FecDecodeBenchmark.java │ ├── FecTest.java │ ├── FecTestNative.java │ ├── GaloisTest.java │ ├── MatrixTest.java │ └── ReedSolomonTest.java ├── kcp-lockStepSynchronization ├── kcp-lockStepSynchronization.iml ├── pom.xml └── src │ └── main │ └── java │ └── test │ ├── IWriter.java │ ├── KcpLockStepSynchronizationClient.java │ ├── KcpLockStepSynchronizationServer.java │ ├── Player.java │ ├── Room.java │ ├── RoomManager.java │ ├── TcpLockStepSynchronizationClient.java │ ├── TcpLockStepSynchronizationServer.java │ ├── TimerThreadPool.java │ ├── TpsTest │ ├── GameTestRoomManager.java │ ├── KcpGameTestClient.java │ ├── KcpGameTestServer.java │ ├── TestRoom.java │ ├── Tps.java │ ├── TpsChannelClientCache.java │ ├── TpsChannelServerCache.java │ └── TpsCounter.java │ └── tcp │ ├── NetAcceptor.java │ ├── NetConnector.java │ └── TcpChannelInitializer.java └── pom.xml /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled class file 2 | *.class 3 | 4 | # Log file 5 | *.log 6 | 7 | # BlueJ files 8 | *.ctxt 9 | 10 | # Mobile Tools for Java (J2ME) 11 | .mtj.tmp/ 12 | 13 | # Package Files # 14 | *.jar 15 | *.war 16 | *.nar 17 | *.ear 18 | *.zip 19 | *.tar.gz 20 | *.rar 21 | 22 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 23 | hs_err_pid* 24 | 25 | .idea* 26 | kcp-base/target 27 | kcp-fec/target 28 | kcp-lockStepSynchronization/target 29 | kcp-example/target 30 | target 31 | -------------------------------------------------------------------------------- /Benchmark.md: -------------------------------------------------------------------------------- 1 | #一些测试结果 2 | 3 | # rtt测试 4 | 1. [测试代码](https://github.com/l42111996/java-Kcp/blob/master/kcp-example/src/main/java/test/KcpRttExampleClient.java) 5 | rtt:1450 参数 lag:300 drop:10% snd:512 rnd:512 mtu:512 nodelay:true interval:10 nocwnd:true ackNoDelay:true fastresend:2 6 | 7 | 8 | 2. [fec测试代码](https://github.com/l42111996/java-Kcp/blob/master/kcp-example/src/main/java/test/KcpRttExampleClient.java) 9 | rtt:890 参数 lag:300 drop:10% snd:512 rnd:512 mtu:512 nodelay:true interval:10 nocwnd:true ackNoDelay:true fastresend:2 fecDataShardCount:3 fecParityShardCount:1 10 | 11 | 12 | # cpu测试 13 | -------------------------------------------------------------------------------- /QA.md: -------------------------------------------------------------------------------- 1 | # 测试遇到过的问题和经验 2 | 1. 大量客户端测试时服务器或者客户端收不到对方信息,但发送成功,因为bind时候bind到了Ipv6,相同的的ipv4端口被其他进程占用导致, 3 | 在启动参数加上 -Djava.net.preferIPv4Stack=true -Djava.net.preferIPv6Addresses=false 禁用ipv6 4 | 2. 大吞吐量测试时发现系统丢包严重,epoll模型接收只占用一个线程且cpu泡满,后开启SO_REUSEPORT参数优化为多核,参考https://www.jianshu.com/p/61df929aa98b 5 | 3. 大量连接时定时任务使用java api定时线程耗时太多,优化为时间轮 6 | 4. 朋友使用kcp在游戏内连接保持和心跳设计分享: 7 | 8 | 使用conv用作连接唯一标识 9 | 10 | 服务器端设置心跳10分钟超时 11 | 12 | 客户端设置心跳时间10秒钟超时 13 | 14 | 客户端在没有发数据包的时候,每秒发送一个心跳包,服务器收到立即回一个,如果10秒没有收到服务器的回执则认为超时断开了 15 | 16 | 客户端超时后提示游戏界面网络断开需要重连,重连时将本地udp重新绑定端口使用新的端口发送数据,原kcp对象不变 17 | 18 | 如果是游戏关掉了再连接,使用新的conv值 19 | 20 | -------------------------------------------------------------------------------- /README.en.md: -------------------------------------------------------------------------------- 1 | # java-Kcp 2 | 3 | [![Powered][2]][1] 4 | 5 | [1]: https://github.com/skywind3000/kcp 6 | [2]: http://skywind3000.github.io/word/images/kcp.svg 7 | 8 | Kcp based on netty version (including implementation of fec function) 9 | 10 | KCP is a udp-based fast and reliable protocol (rudp), which can reduce the average delay by 30% -40% at the cost of wasting 10% -20% of bandwidth over TCP, and reduce the maximum delay by three times the transmission effect. 11 | 12 | # maven repository: 13 | 14 | ```xml 15 | 16 | com.github.l42111996 17 | kcp-base 18 | 1.6 19 | 20 | ``` 21 | # Using method and parameters 22 | 1. [Server-side example](https://github.com/l42111996/java-Kcp/blob/master/kcp-example/src/main/java/test/KcpRttExampleServer.java) 23 | 2. [Client Example](https://github.com/l42111996/java-Kcp/blob/master/kcp-example/src/main/java/test/KcpRttExampleClient.java) 24 | 3. [Best Practices](https://github.com/skywind3000/kcp/wiki/KCP-Best-Practice) 25 | 4. [A lot of information](https://github.com/skywind3000/kcp) 26 | 5. Compatible with C #, [java server](https://github.com/l42111996/java-Kcp/blob/master/kcp-example/src/main/java/test/Kcp4sharpExampleServer.java), [c #Client](https://github.com/l42111996/csharp-kcp/blob/master/example-Kcp/KcpRttExampleClient.cs) 27 | 6. [Encountered problems](https://github.com/l42111996/java-Kcp/blob/master/QA.md) 28 | 7. [Performance test results](https://github.com/l42111996/java-Kcp/blob/master/Benchmark.md) 29 | 8. [Compatible with kcp-go, including fec compatible](https://github.com/l42111996/java-Kcp/blob/master/kcp-example/src/main/java/test/Kcp4GoExampleClient.java) 30 | 31 | # compatibility: 32 | 1. Compatible with c version of kcp 33 | 2. fec implementation based on https://github.com/Backblaze/JavaReedSolomon 34 | 3. Perfectly compatible C# version, https://github.com/l42111996/csharp-kcp, quickly build the network library before the game 35 | 36 | # optimization: 37 | 1. Based on event-driven, make full use of multi-core 38 | 2. Optimize fastack logic and reduce traffic by 10% 39 | 3. Optimize the check function. 40 | 4. Optimize collection iterators. 41 | 5. Include fec to reduce latency 42 | 6. With crc32 check 43 | 7. Use the time wheel to optimize the CPU usage of a large number of connections 44 | 8. Use directbuf and object pool, no gc pressure 45 | 9. Increase the use of conv or ip + port to determine the uniqueness of the channel. The game is recommended to use conv and tcp configuration. [Related information](https://github.com/skywind3000/kcp/wiki/Cooperate-With-Tcp-Server) 46 | 10. Changes in export ip such as 4G switching wifi when adding games will not cause disconnection 47 | 48 | 49 | # Relevant information 50 | 1. https://github.com/skywind3000/kcp The original ccp version of kcp 51 | 2. https://github.com/xtaci/kcp-go go version kcp, with a lot of optimization 52 | 3. https://github.com/Backblaze/JavaReedSolomon java version fec 53 | 4. https://github.com/LMAX-Exchange/disruptor High-performance inter-thread messaging library 54 | 5. https://github.com/JCTools/JCTools efficient concurrent library 55 | 6. https://github.com/szhnet/kcp-netty A kcp for java version 56 | 7. https://github.com/l42111996/csharp-kcp C# version of kcp based on dotNetty, perfectly compatible 57 | 58 | # communicate with 59 | QQ: 526167774 -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # java-Kcp 2 | 3 | [![Powered][2]][1] 4 | 5 | [1]: https://github.com/skywind3000/kcp 6 | [2]: http://skywind3000.github.io/word/images/kcp.svg 7 | 8 | [README in english](https://github.com/l42111996/java-Kcp/blob/master/README.en.md) 9 | 10 | 基于netty版本实现的kcp(包含fec功能的实现) 11 | 12 | KCP是一个基于udp的快速可靠协议(rudp),能以比 TCP浪费10%-20%的带宽的代价,换取平均延迟降低 30%-40%,且最大延迟降低三倍的传输效果。 13 | 14 | # maven地址: 15 | 16 | ```xml 17 | 18 | com.github.l42111996 19 | kcp-base 20 | 1.6 21 | 22 | ``` 23 | 24 | # 使用方法以及参数 25 | 1. [server端示例](https://github.com/l42111996/java-Kcp/blob/master/kcp-example/src/main/java/test/KcpRttExampleServer.java) 26 | 2. [client端实例](https://github.com/l42111996/java-Kcp/blob/master/kcp-example/src/main/java/test/KcpRttExampleClient.java) 27 | 3. [最佳实践](https://github.com/skywind3000/kcp/wiki/KCP-Best-Practice) 28 | 4. [大量资料](https://github.com/skywind3000/kcp) 29 | 5. 兼容c#端,[java服务端](https://github.com/l42111996/java-Kcp/blob/master/kcp-example/src/main/java/test/Kcp4sharpExampleServer.java) , [c#客户端](https://github.com/l42111996/csharp-kcp/blob/master/example-Kcp/KcpRttExampleClient.cs) 30 | 6. [遇到过的问题](https://github.com/l42111996/java-Kcp/blob/master/QA.md) 31 | 7. [性能测试结果](https://github.com/l42111996/java-Kcp/blob/master/Benchmark.md) 32 | 8. [兼容kcp-go,包含fec兼容](https://github.com/l42111996/java-Kcp/blob/master/kcp-example/src/main/java/test/Kcp4GoExampleClient.java) 33 | 34 | 35 | # 兼容性: 36 | 1. 兼容c版本kcp 37 | 2. fec基于 https://github.com/Backblaze/JavaReedSolomon 实现 38 | 3. 完美兼容的C#版本,https://github.com/l42111996/csharp-kcp ,快速构建游戏前后端网络库 39 | 40 | # 稳定性: 41 | 已经是稳定版本,据统计有5~10款上线项目接入,包含腾讯,快手等公司产品使用 42 | 43 | # 优化: 44 | 1. 基于事件驱动,充分利用多核 45 | 2. 优化fastack逻辑,降低10%流量 46 | 3. 优化check函数。 47 | 4. 优化集合迭代器。 48 | 5. 包含fec,降低延迟 49 | 6. 附带crc32校验 50 | 7. 使用时间轮,优化大量连接cpu占用 51 | 8. 使用directbuf和对象池,无gc压力 52 | 9. 增加使用conv或者ip+port确定channel唯一性,游戏建议使用conv与tcp配置使用,[相关资料](https://github.com/skywind3000/kcp/wiki/Cooperate-With-Tcp-Server) 53 | 10. 增加游戏使用时4G切换wifi等出口ip变动不会导致连接断开 54 | 55 | # 相关资料 56 | 1. https://github.com/skywind3000/kcp 原版c版本的kcp 57 | 2. https://github.com/xtaci/kcp-go go版本kcp,有大量优化 58 | 3. https://github.com/Backblaze/JavaReedSolomon java版本fec 59 | 4. https://github.com/LMAX-Exchange/disruptor 高性能的线程间消息传递库 60 | 5. https://github.com/JCTools/JCTools 高性能并发库 61 | 6. https://github.com/szhnet/kcp-netty java版本的一个kcp 62 | 7. https://github.com/l42111996/csharp-kcp 基于dotNetty的c#版本kcp,完美兼容 63 | 64 | 65 | # 交流 66 | QQ:526167774 67 | 68 | -------------------------------------------------------------------------------- /java-Kcp.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /kcp-base/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | java-Kcp 7 | com.github.l42111996 8 | 1.6.1 9 | 10 | 4.0.0 11 | 12 | 13 | 3.4.4 14 | 15 | 16 | kcp-base 17 | 18 | 19 | com.github.l42111996 20 | kcp-fec 21 | ${project.version} 22 | 23 | 24 | com.lmax 25 | disruptor 26 | ${disruptor.version} 27 | true 28 | 29 | 30 | org.jctools 31 | jctools-core 32 | 3.3.0 33 | 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /kcp-base/src/main/java/internal/ReItrCollection.java: -------------------------------------------------------------------------------- 1 | package internal; 2 | 3 | import java.util.Collection; 4 | 5 | /** 6 | * @author szh 7 | */ 8 | public interface ReItrCollection extends Collection { 9 | 10 | @Override 11 | ReusableIterator iterator(); 12 | 13 | } 14 | -------------------------------------------------------------------------------- /kcp-base/src/main/java/internal/ReItrSet.java: -------------------------------------------------------------------------------- 1 | package internal; 2 | 3 | import java.util.Set; 4 | 5 | /** 6 | * @author szh 7 | */ 8 | public interface ReItrSet extends Set, ReItrCollection { 9 | 10 | } 11 | -------------------------------------------------------------------------------- /kcp-base/src/main/java/internal/ReusableIterator.java: -------------------------------------------------------------------------------- 1 | package internal; 2 | 3 | import java.util.Iterator; 4 | 5 | /** 6 | * Reusable iterator 7 | * 8 | * @author szh 9 | */ 10 | public interface ReusableIterator extends Iterator { 11 | 12 | /** 13 | * Reset the iterator to initial state. 14 | * 15 | * @return this object 16 | */ 17 | ReusableIterator rewind(); 18 | 19 | } 20 | -------------------------------------------------------------------------------- /kcp-base/src/main/java/internal/ReusableListIterator.java: -------------------------------------------------------------------------------- 1 | package internal; 2 | 3 | import java.util.ListIterator; 4 | 5 | /** 6 | * Reusable list iterator 7 | * 8 | * @author szh 9 | */ 10 | public interface ReusableListIterator extends ListIterator, ReusableIterator { 11 | 12 | /** 13 | * This method is identical to {@code rewind(0)}. 14 | * 15 | * @return this iterator 16 | */ 17 | @Override 18 | ReusableListIterator rewind(); 19 | 20 | 21 | /** 22 | * Reset the iterator and specify index of the first element to be returned from the iterator. 23 | * 24 | * @param index index of the first element to be returned from the iterator 25 | * @return this iterator 26 | */ 27 | ReusableListIterator rewind(int index); 28 | 29 | } 30 | -------------------------------------------------------------------------------- /kcp-base/src/main/java/internal/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Internal Utilities 3 | */ 4 | package internal; -------------------------------------------------------------------------------- /kcp-base/src/main/java/kcp/ClientAddressChannelManager.java: -------------------------------------------------------------------------------- 1 | package kcp; 2 | 3 | 4 | import io.netty.channel.socket.DatagramPacket; 5 | 6 | import java.net.SocketAddress; 7 | import java.util.Collection; 8 | import java.util.Map; 9 | import java.util.concurrent.ConcurrentHashMap; 10 | 11 | /** 12 | * Created by JinMiao 13 | * 2019/10/16. 14 | */ 15 | public class ClientAddressChannelManager implements IChannelManager { 16 | private Map ukcpMap = new ConcurrentHashMap<>(); 17 | 18 | @Override 19 | public Ukcp get(DatagramPacket msg) { 20 | return ukcpMap.get(msg.recipient()); 21 | } 22 | 23 | @Override 24 | public void New(SocketAddress socketAddress, Ukcp ukcp, DatagramPacket msg) { 25 | ukcpMap.put(socketAddress, ukcp); 26 | } 27 | 28 | 29 | @Override 30 | public void del(Ukcp ukcp) { 31 | ukcpMap.remove(ukcp.user().getLocalAddress()); 32 | ukcp.user().getChannel().close(); 33 | } 34 | 35 | @Override 36 | public Collection getAll() { 37 | return this.ukcpMap.values(); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /kcp-base/src/main/java/kcp/ClientChannelHandler.java: -------------------------------------------------------------------------------- 1 | package kcp; 2 | 3 | import io.netty.channel.ChannelHandlerContext; 4 | import io.netty.channel.ChannelInboundHandlerAdapter; 5 | import io.netty.channel.socket.DatagramPacket; 6 | import org.slf4j.Logger; 7 | import org.slf4j.LoggerFactory; 8 | 9 | /** 10 | * Created by JinMiao 11 | * 2019-06-26. 12 | */ 13 | public class ClientChannelHandler extends ChannelInboundHandlerAdapter { 14 | static final Logger logger = LoggerFactory.getLogger(ClientChannelHandler.class); 15 | 16 | private IChannelManager channelManager; 17 | 18 | public ClientChannelHandler(IChannelManager channelManager) { 19 | this.channelManager = channelManager; 20 | } 21 | @Override 22 | public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { 23 | logger.error("",cause); 24 | //SocketAddress socketAddress = ctx.channel().localAddress(); 25 | //Ukcp ukcp = ukcpMap.get(socketAddress); 26 | //ukcp.getKcpListener().handleException(cause,ukcp); 27 | } 28 | 29 | @Override 30 | public void channelRead(ChannelHandlerContext ctx, Object object) { 31 | DatagramPacket msg = (DatagramPacket) object; 32 | Ukcp ukcp = this.channelManager.get(msg); 33 | if(ukcp!=null){ 34 | ukcp.read(msg.content()); 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /kcp-base/src/main/java/kcp/ClientConvChannelManager.java: -------------------------------------------------------------------------------- 1 | package kcp; 2 | 3 | import io.netty.buffer.ByteBuf; 4 | import io.netty.channel.socket.DatagramPacket; 5 | 6 | import java.net.SocketAddress; 7 | import java.util.Collection; 8 | import java.util.Map; 9 | import java.util.concurrent.ConcurrentHashMap; 10 | 11 | /** 12 | * 根据conv确定一个session 13 | * Created by JinMiao 14 | * 2019/10/17. 15 | */ 16 | public class ClientConvChannelManager implements IChannelManager { 17 | 18 | private int convIndex; 19 | 20 | public ClientConvChannelManager(int convIndex) { 21 | this.convIndex = convIndex; 22 | } 23 | 24 | private Map ukcpMap = new ConcurrentHashMap<>(); 25 | 26 | @Override 27 | public Ukcp get(DatagramPacket msg) { 28 | int conv = getConv(msg); 29 | return ukcpMap.get(conv); 30 | } 31 | 32 | 33 | private int getConv(DatagramPacket msg) { 34 | ByteBuf byteBuf = msg.content(); 35 | return byteBuf.getIntLE(byteBuf.readerIndex() + convIndex); 36 | } 37 | 38 | @Override 39 | public void New(SocketAddress socketAddress, Ukcp ukcp, DatagramPacket msg) { 40 | int conv = ukcp.getConv(); 41 | if (msg != null) { 42 | conv = getConv(msg); 43 | ukcp.setConv(conv); 44 | } 45 | 46 | ukcpMap.put(conv, ukcp); 47 | } 48 | 49 | @Override 50 | public void del(Ukcp ukcp) { 51 | ukcpMap.remove(ukcp.getConv()); 52 | ukcp.user().getChannel().close(); 53 | } 54 | 55 | @Override 56 | public Collection getAll() { 57 | return this.ukcpMap.values(); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /kcp-base/src/main/java/kcp/Crc32Decode.java: -------------------------------------------------------------------------------- 1 | package kcp; 2 | 3 | import com.backblaze.erasure.fec.Snmp; 4 | import io.netty.buffer.ByteBuf; 5 | import io.netty.channel.ChannelHandlerContext; 6 | import io.netty.channel.ChannelInboundHandlerAdapter; 7 | import io.netty.channel.socket.DatagramPacket; 8 | 9 | import java.nio.ByteBuffer; 10 | import java.util.zip.CRC32; 11 | 12 | /** 13 | * Created by JinMiao 14 | * 2019/12/10. 15 | */ 16 | public class Crc32Decode extends ChannelInboundHandlerAdapter 17 | { 18 | private CRC32 crc32 = new CRC32(); 19 | @Override 20 | public void channelRead(ChannelHandlerContext ctx, Object msg) { 21 | if (msg instanceof DatagramPacket) { 22 | DatagramPacket datagramPacket = (DatagramPacket) msg; 23 | ByteBuf data = datagramPacket.content(); 24 | long checksum = data.readUnsignedIntLE(); 25 | ByteBuffer byteBuffer = data.nioBuffer(data.readerIndex(),data.readableBytes()); 26 | crc32.reset(); 27 | crc32.update(byteBuffer); 28 | if(checksum!=crc32.getValue()){ 29 | Snmp.snmp.getInCsumErrors().increment(); 30 | return; 31 | } 32 | } 33 | ctx.fireChannelRead(msg); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /kcp-base/src/main/java/kcp/Crc32Encode.java: -------------------------------------------------------------------------------- 1 | package kcp; 2 | 3 | import io.netty.buffer.ByteBuf; 4 | import io.netty.buffer.Unpooled; 5 | import io.netty.channel.ChannelHandlerContext; 6 | import io.netty.channel.ChannelOutboundHandlerAdapter; 7 | import io.netty.channel.ChannelPromise; 8 | import io.netty.channel.socket.DatagramPacket; 9 | 10 | import java.nio.ByteBuffer; 11 | import java.util.zip.CRC32; 12 | 13 | /** 14 | * Created by JinMiao 15 | * 2019/12/10. 16 | */ 17 | public class Crc32Encode extends ChannelOutboundHandlerAdapter { 18 | 19 | private CRC32 crc32 = new CRC32(); 20 | 21 | @Override 22 | public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) { 23 | DatagramPacket datagramPacket = (DatagramPacket) msg; 24 | ByteBuf data = datagramPacket.content(); 25 | ByteBuffer byteBuffer = data.nioBuffer(ChannelConfig.crc32Size,data.readableBytes()-ChannelConfig.crc32Size); 26 | crc32.reset(); 27 | crc32.update(byteBuffer); 28 | long checksum = crc32.getValue(); 29 | data.setIntLE(0, (int) checksum); 30 | //ByteBuf headByteBuf = ctx.alloc().ioBuffer(4); 31 | //headByteBuf.writeIntLE((int) checksum); 32 | //ByteBuf newByteBuf = Unpooled.wrappedBuffer(headByteBuf,data); 33 | //datagramPacket = datagramPacket.replace(newByteBuf); 34 | ctx.write(datagramPacket, promise); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /kcp-base/src/main/java/kcp/FecOutPut.java: -------------------------------------------------------------------------------- 1 | package kcp; 2 | 3 | import com.backblaze.erasure.IFecEncode; 4 | import com.backblaze.erasure.fecNative.FecEncode; 5 | import io.netty.buffer.ByteBuf; 6 | 7 | /** 8 | * fec 9 | * Created by JinMiao 10 | * 2018/7/27. 11 | */ 12 | public class FecOutPut implements KcpOutput{ 13 | 14 | private KcpOutput output; 15 | 16 | private IFecEncode fecEncode; 17 | 18 | 19 | protected FecOutPut(KcpOutput output, IFecEncode fecEncode) { 20 | this.output = output; 21 | this.fecEncode = fecEncode; 22 | } 23 | 24 | @Override 25 | public void out(ByteBuf msg, IKcp kcp) { 26 | ByteBuf[] byteBufs = fecEncode.encode(msg); 27 | //out之后会自动释放你内存 28 | output.out(msg,kcp); 29 | if(byteBufs==null) { 30 | return; 31 | } 32 | for (int i = 0; i < byteBufs.length; i++) { 33 | ByteBuf parityByteBuf = byteBufs[i]; 34 | output.out(parityByteBuf,kcp); 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /kcp-base/src/main/java/kcp/IChannelManager.java: -------------------------------------------------------------------------------- 1 | package kcp; 2 | 3 | import io.netty.channel.socket.DatagramPacket; 4 | 5 | import java.net.SocketAddress; 6 | import java.util.Collection; 7 | 8 | 9 | /** 10 | * Created by JinMiao 11 | * 2019/10/16. 12 | */ 13 | public interface IChannelManager { 14 | 15 | Ukcp get(DatagramPacket msg); 16 | 17 | void New(SocketAddress socketAddress,Ukcp ukcp,DatagramPacket msg); 18 | 19 | void del(Ukcp ukcp); 20 | 21 | Collection getAll(); 22 | } 23 | -------------------------------------------------------------------------------- /kcp-base/src/main/java/kcp/IKcp.java: -------------------------------------------------------------------------------- 1 | package kcp; 2 | 3 | import io.netty.buffer.ByteBuf; 4 | import io.netty.buffer.ByteBufAllocator; 5 | import io.netty.util.Recycler; 6 | 7 | import java.util.List; 8 | 9 | /** 10 | * Created by JinMiao 11 | * 2020/7/20. 12 | */ 13 | public interface IKcp { 14 | void release(); 15 | 16 | ByteBuf mergeRecv(); 17 | 18 | /** 19 | * 1,判断是否有完整的包,如果有就抛给下一层 20 | * 2,整理消息接收队列,判断下一个包是否已经收到 收到放入rcvQueue 21 | * 3,判断接收窗口剩余是否改变,如果改变记录需要通知 22 | * @param bufList 23 | * @return 24 | */ 25 | int recv(List bufList); 26 | 27 | /** 28 | * check the size of next message in the recv queue 29 | * 检查接收队列里面是否有完整的一个包,如果有返回该包的字节长度 30 | * @return -1 没有完整包, >0 一个完整包所含字节 31 | */ 32 | int peekSize(); 33 | 34 | /** 35 | * 判断一条消息是否完整收全了 36 | * @return 37 | */ 38 | boolean canRecv(); 39 | 40 | int send(ByteBuf buf); 41 | 42 | int input(ByteBuf data, boolean regular, long current); 43 | 44 | long currentMs(long now); 45 | 46 | /** 47 | * ikcp_flush 48 | */ 49 | long flush(boolean ackOnly, long current); 50 | 51 | /** 52 | * update getState (call it repeatedly, every 10ms-100ms), or you can ask 53 | * ikcp_check when to call it again (without ikcp_input/_send calling). 54 | * 'current' - current timestamp in millisec. 55 | * 56 | * @param current 57 | */ 58 | void update(long current); 59 | 60 | /** 61 | * Determine when should you invoke ikcp_update: 62 | * returns when you should invoke ikcp_update in millisec, if there 63 | * is no ikcp_input/_send calling. you can call ikcp_update in that 64 | * time, instead of call update repeatly. 65 | * Important to reduce unnacessary ikcp_update invoking. use it to 66 | * schedule ikcp_update (eg. implementing an epoll-like mechanism, 67 | * or optimize ikcp_update when handling massive kcp connections) 68 | * 69 | * @param current 70 | * @return 71 | */ 72 | long check(long current); 73 | 74 | boolean checkFlush(); 75 | 76 | int setMtu(int mtu); 77 | 78 | int getInterval(); 79 | 80 | int nodelay(boolean nodelay, int interval, int resend, boolean nc); 81 | 82 | int waitSnd(); 83 | 84 | int getConv(); 85 | 86 | void setConv(int conv); 87 | 88 | Object getUser(); 89 | 90 | void setUser(Object user); 91 | 92 | int getState(); 93 | 94 | void setState(int state); 95 | 96 | boolean isNodelay(); 97 | 98 | void setNodelay(boolean nodelay); 99 | 100 | 101 | void setFastresend(int fastresend); 102 | 103 | 104 | void setRxMinrto(int rxMinrto); 105 | 106 | 107 | void setRcvWnd(int rcvWnd); 108 | 109 | void setAckMaskSize(int ackMaskSize); 110 | 111 | void setReserved(int reserved); 112 | 113 | int getSndWnd(); 114 | 115 | void setSndWnd(int sndWnd); 116 | 117 | boolean isStream(); 118 | 119 | void setStream(boolean stream); 120 | 121 | void setByteBufAllocator(ByteBufAllocator byteBufAllocator); 122 | 123 | KcpOutput getOutput(); 124 | 125 | void setOutput(KcpOutput output); 126 | 127 | void setAckNoDelay(boolean ackNoDelay); 128 | } 129 | -------------------------------------------------------------------------------- /kcp-base/src/main/java/kcp/KcpListener.java: -------------------------------------------------------------------------------- 1 | package kcp; 2 | 3 | import io.netty.buffer.ByteBuf; 4 | 5 | /** 6 | * Created by JinMiao 7 | * 2018/9/11. 8 | */ 9 | public interface KcpListener { 10 | 11 | 12 | /** 13 | * 连接之后 14 | * @param ukcp 15 | */ 16 | void onConnected(Ukcp ukcp); 17 | 18 | /** 19 | * kcp message 20 | * 21 | * @param byteBuf the data 22 | * @param ukcp 23 | */ 24 | void handleReceive(ByteBuf byteBuf, Ukcp ukcp); 25 | 26 | /** 27 | * 28 | * kcp异常,之后此kcp就会被关闭 29 | * 30 | * @param ex 异常 31 | * @param ukcp 发生异常的kcp,null表示非kcp错误 32 | */ 33 | void handleException(Throwable ex, Ukcp ukcp); 34 | 35 | /** 36 | * 关闭 37 | * 38 | * @param ukcp 39 | */ 40 | void handleClose(Ukcp ukcp); 41 | } 42 | -------------------------------------------------------------------------------- /kcp-base/src/main/java/kcp/KcpOutPutImp.java: -------------------------------------------------------------------------------- 1 | package kcp; 2 | 3 | import com.backblaze.erasure.fec.Snmp; 4 | import io.netty.buffer.ByteBuf; 5 | import io.netty.channel.socket.DatagramPacket; 6 | 7 | 8 | /** 9 | * Created by JinMiao 10 | * 2018/9/21. 11 | */ 12 | public class KcpOutPutImp implements KcpOutput { 13 | 14 | @Override 15 | public void out(ByteBuf data, IKcp kcp) { 16 | Snmp.snmp.OutPkts.increment(); 17 | Snmp.snmp.OutBytes.add(data.writerIndex()); 18 | User user = (User) kcp.getUser(); 19 | DatagramPacket temp = new DatagramPacket(data,user.getRemoteAddress(), user.getLocalAddress()); 20 | user.getChannel().writeAndFlush(temp); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /kcp-base/src/main/java/kcp/KcpOutput.java: -------------------------------------------------------------------------------- 1 | package kcp; 2 | 3 | import io.netty.buffer.ByteBuf; 4 | 5 | /** 6 | * @author szh 7 | */ 8 | public interface KcpOutput { 9 | 10 | void out(ByteBuf data, IKcp kcp); 11 | 12 | } 13 | -------------------------------------------------------------------------------- /kcp-base/src/main/java/kcp/ReadTask.java: -------------------------------------------------------------------------------- 1 | package kcp; 2 | 3 | import com.backblaze.erasure.fec.Snmp; 4 | import internal.CodecOutputList; 5 | import io.netty.buffer.ByteBuf; 6 | import threadPool.ITask; 7 | 8 | import java.util.Queue; 9 | 10 | /** 11 | * Created by JinMiao 12 | * 2018/9/11. 13 | */ 14 | public class ReadTask implements ITask { 15 | 16 | 17 | private final Ukcp ukcp; 18 | 19 | public ReadTask(Ukcp ukcp) { 20 | this.ukcp = ukcp; 21 | } 22 | 23 | 24 | @Override 25 | public void execute() { 26 | CodecOutputList bufList = null; 27 | Ukcp ukcp = this.ukcp; 28 | try { 29 | //查看连接状态 30 | if (!ukcp.isActive()) { 31 | return; 32 | } 33 | long current = System.currentTimeMillis(); 34 | Queue recieveList = ukcp.getReadBuffer(); 35 | int readCount =0; 36 | for (; ; ) { 37 | ByteBuf byteBuf = recieveList.poll(); 38 | if (byteBuf == null) { 39 | break; 40 | } 41 | readCount++; 42 | ukcp.input(byteBuf, current); 43 | byteBuf.release(); 44 | } 45 | if (readCount==0) { 46 | return; 47 | } 48 | if(ukcp.isControlReadBufferSize()){ 49 | ukcp.getReadBufferIncr().addAndGet(readCount); 50 | } 51 | long readBytes = 0; 52 | if (ukcp.isStream()) { 53 | int size =0; 54 | while (ukcp.canRecv()) { 55 | if (bufList == null) { 56 | bufList = CodecOutputList.newInstance(); 57 | } 58 | ukcp.receive(bufList); 59 | size= bufList.size(); 60 | } 61 | for (int i = 0; i < size; i++) { 62 | ByteBuf byteBuf = bufList.getUnsafe(i); 63 | readBytes += byteBuf.readableBytes(); 64 | readBytebuf(byteBuf,current,ukcp); 65 | } 66 | } else { 67 | while (ukcp.canRecv()) { 68 | ByteBuf recvBuf = ukcp.mergeReceive(); 69 | readBytes += recvBuf.readableBytes(); 70 | readBytebuf(recvBuf,current,ukcp); 71 | } 72 | } 73 | Snmp.snmp.BytesReceived.add(readBytes); 74 | //判断写事件 75 | if (!ukcp.getWriteBuffer().isEmpty()&& ukcp.canSend(false)) { 76 | ukcp.notifyWriteEvent(); 77 | } 78 | } catch (Throwable e) { 79 | ukcp.internalClose(); 80 | e.printStackTrace(); 81 | } finally { 82 | release(); 83 | if (bufList != null) { 84 | bufList.recycle(); 85 | } 86 | } 87 | } 88 | 89 | 90 | private void readBytebuf(ByteBuf buf,long current,Ukcp ukcp) { 91 | ukcp.setLastRecieveTime(current); 92 | try { 93 | ukcp.getKcpListener().handleReceive(buf, ukcp); 94 | } catch (Throwable throwable) { 95 | ukcp.getKcpListener().handleException(throwable, ukcp); 96 | }finally { 97 | buf.release(); 98 | } 99 | } 100 | 101 | public void release() { 102 | ukcp.getReadProcessing().set(false); 103 | } 104 | 105 | } 106 | -------------------------------------------------------------------------------- /kcp-base/src/main/java/kcp/ScheduleTask.java: -------------------------------------------------------------------------------- 1 | package kcp; 2 | 3 | import io.netty.util.HashedWheelTimer; 4 | import io.netty.util.Timeout; 5 | import io.netty.util.TimerTask; 6 | import threadPool.IMessageExecutor; 7 | import threadPool.ITask; 8 | 9 | import java.util.concurrent.TimeUnit; 10 | 11 | /** 12 | * Created by JinMiao 13 | * 2018/10/24. 14 | */ 15 | public class ScheduleTask implements ITask, Runnable, TimerTask { 16 | 17 | private final IMessageExecutor messageExecutor; 18 | 19 | private final Ukcp ukcp; 20 | 21 | private final HashedWheelTimer hashedWheelTimer; 22 | 23 | public ScheduleTask(IMessageExecutor messageExecutor, Ukcp ukcp, HashedWheelTimer hashedWheelTimer) { 24 | this.messageExecutor = messageExecutor; 25 | this.ukcp = ukcp; 26 | this.hashedWheelTimer = hashedWheelTimer; 27 | } 28 | 29 | //flush策略 30 | //1,在send调用后检查缓冲区如果可以发送直接调用update得到时间并存在ukcp内 31 | //2,定时任务到了检查ukcp的时间和自己的定时 如果可以发送则直接发送 时间延后则重新定时 32 | //定时任务发送成功后检测缓冲区 是否触发发送时间 33 | //3,读时间触发后检测检测缓冲区触发写事件 34 | //问题: 精准大量的flush触发会导致ack重复发送 流量增大? 不会的 ack只会发送一次 35 | @Override 36 | public void execute() { 37 | try { 38 | final Ukcp ukcp = this.ukcp; 39 | long now = System.currentTimeMillis(); 40 | //判断连接是否关闭 41 | if (ukcp.getTimeoutMillis() != 0 && now - ukcp.getTimeoutMillis() > ukcp.getLastRecieveTime()) { 42 | ukcp.internalClose(); 43 | } 44 | if (!ukcp.isActive()) { 45 | return; 46 | } 47 | long timeLeft = ukcp.getTsUpdate() - now; 48 | //判断执行时间是否到了 49 | if (timeLeft > 0) { 50 | hashedWheelTimer.newTimeout(this,timeLeft, TimeUnit.MILLISECONDS); 51 | return; 52 | } 53 | long next = ukcp.flush(now); 54 | hashedWheelTimer.newTimeout(this,next, TimeUnit.MILLISECONDS); 55 | //检测写缓冲区 如果能写则触发写事件 56 | if (!ukcp.getWriteBuffer().isEmpty() && ukcp.canSend(false)) 57 | { 58 | ukcp.notifyWriteEvent(); 59 | } 60 | } catch (Throwable e) { 61 | e.printStackTrace(); 62 | } 63 | } 64 | 65 | @Override 66 | public void run() { 67 | this.messageExecutor.execute(this); 68 | } 69 | 70 | @Override 71 | public void run(Timeout timeout) { 72 | run(); 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /kcp-base/src/main/java/kcp/ServerAddressChannelManager.java: -------------------------------------------------------------------------------- 1 | package kcp; 2 | 3 | import io.netty.channel.socket.DatagramPacket; 4 | 5 | import java.net.SocketAddress; 6 | import java.util.Collection; 7 | import java.util.Map; 8 | import java.util.concurrent.ConcurrentHashMap; 9 | 10 | /** 11 | * Created by JinMiao 12 | * 2019/10/17. 13 | */ 14 | public class ServerAddressChannelManager implements IChannelManager { 15 | private Map ukcpMap = new ConcurrentHashMap<>(); 16 | 17 | @Override 18 | public Ukcp get(DatagramPacket msg) { 19 | return ukcpMap.get(msg.sender()); 20 | } 21 | 22 | @Override 23 | public void New(SocketAddress socketAddress, Ukcp ukcp,DatagramPacket msg) { 24 | ukcpMap.put(socketAddress, ukcp); 25 | } 26 | 27 | @Override 28 | public void del(Ukcp ukcp) { 29 | ukcpMap.remove(ukcp.user().getRemoteAddress()); 30 | } 31 | 32 | @Override 33 | public Collection getAll() { 34 | return this.ukcpMap.values(); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /kcp-base/src/main/java/kcp/ServerConvChannelManager.java: -------------------------------------------------------------------------------- 1 | package kcp; 2 | 3 | import io.netty.buffer.ByteBuf; 4 | import io.netty.channel.socket.DatagramPacket; 5 | 6 | import java.net.SocketAddress; 7 | import java.util.Collection; 8 | import java.util.Map; 9 | import java.util.concurrent.ConcurrentHashMap; 10 | 11 | /** 12 | * 根据conv确定一个session 13 | * Created by JinMiao 14 | * 2019/10/17. 15 | */ 16 | public class ServerConvChannelManager implements IChannelManager { 17 | 18 | private int convIndex; 19 | 20 | public ServerConvChannelManager(int convIndex) { 21 | this.convIndex = convIndex; 22 | } 23 | 24 | private Map ukcpMap = new ConcurrentHashMap<>(); 25 | 26 | @Override 27 | public Ukcp get(DatagramPacket msg) { 28 | int conv = getConv(msg); 29 | return ukcpMap.get(conv); 30 | } 31 | 32 | 33 | private int getConv(DatagramPacket msg) { 34 | ByteBuf byteBuf = msg.content(); 35 | return byteBuf.getIntLE(byteBuf.readerIndex() + convIndex); 36 | } 37 | 38 | @Override 39 | public void New(SocketAddress socketAddress, Ukcp ukcp, DatagramPacket msg) { 40 | int conv = ukcp.getConv(); 41 | if (msg != null) { 42 | conv = getConv(msg); 43 | ukcp.setConv(conv); 44 | } 45 | 46 | ukcpMap.put(conv, ukcp); 47 | } 48 | 49 | @Override 50 | public void del(Ukcp ukcp) { 51 | ukcpMap.remove(ukcp.getConv()); 52 | } 53 | 54 | @Override 55 | public Collection getAll() { 56 | return this.ukcpMap.values(); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /kcp-base/src/main/java/kcp/User.java: -------------------------------------------------------------------------------- 1 | package kcp; 2 | 3 | import io.netty.channel.Channel; 4 | 5 | import java.net.InetSocketAddress; 6 | 7 | /** 8 | * Created by JinMiao 9 | * 2018/11/2. 10 | */ 11 | public class User { 12 | 13 | private Channel channel; 14 | private InetSocketAddress remoteAddress; 15 | private InetSocketAddress localAddress; 16 | 17 | private Object cache; 18 | 19 | public void setCache(Object cache) { 20 | this.cache = cache; 21 | } 22 | 23 | public T getCache() { 24 | return (T) cache; 25 | } 26 | 27 | public User(Channel channel, InetSocketAddress remoteAddress, InetSocketAddress localAddress) { 28 | this.channel = channel; 29 | this.remoteAddress = remoteAddress; 30 | this.localAddress = localAddress; 31 | } 32 | 33 | protected Channel getChannel() { 34 | return channel; 35 | } 36 | 37 | protected void setChannel(Channel channel) { 38 | this.channel = channel; 39 | } 40 | 41 | public InetSocketAddress getRemoteAddress() { 42 | return remoteAddress; 43 | } 44 | 45 | protected void setRemoteAddress(InetSocketAddress remoteAddress) { 46 | this.remoteAddress = remoteAddress; 47 | } 48 | 49 | public InetSocketAddress getLocalAddress() { 50 | return localAddress; 51 | } 52 | 53 | protected void setLocalAddress(InetSocketAddress localAddress) { 54 | this.localAddress = localAddress; 55 | } 56 | 57 | 58 | @Override 59 | public String toString() { 60 | return "User{" + 61 | "remoteAddress=" + remoteAddress + 62 | ", localAddress=" + localAddress + 63 | '}'; 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /kcp-base/src/main/java/kcp/WriteTask.java: -------------------------------------------------------------------------------- 1 | package kcp; 2 | 3 | import com.backblaze.erasure.fec.Snmp; 4 | import io.netty.buffer.ByteBuf; 5 | import threadPool.ITask; 6 | 7 | import java.io.IOException; 8 | import java.util.Queue; 9 | 10 | /** 11 | * Created by JinMiao 12 | * 2018/9/11. 13 | */ 14 | public class WriteTask implements ITask { 15 | 16 | 17 | private final Ukcp ukcp; 18 | 19 | public WriteTask(Ukcp ukcp) { 20 | this.ukcp = ukcp; 21 | } 22 | 23 | @Override 24 | public void execute() { 25 | Ukcp ukcp = this.ukcp; 26 | try { 27 | //查看连接状态 28 | if(!ukcp.isActive()){ 29 | return; 30 | } 31 | //从发送缓冲区到kcp缓冲区 32 | Queue queue = ukcp.getWriteBuffer(); 33 | int writeCount =0; 34 | long writeBytes = 0; 35 | while(ukcp.canSend(false)){ 36 | ByteBuf byteBuf = queue.poll(); 37 | if(byteBuf==null){ 38 | break; 39 | } 40 | writeCount++; 41 | try { 42 | writeBytes +=byteBuf.readableBytes(); 43 | ukcp.send(byteBuf); 44 | byteBuf.release(); 45 | } catch (IOException e) { 46 | ukcp.getKcpListener().handleException(e, ukcp); 47 | return; 48 | } 49 | } 50 | Snmp.snmp.BytesSent.add(writeBytes); 51 | if(ukcp.isControlWriteBufferSize()){ 52 | ukcp.getWriteBufferIncr().addAndGet(writeCount); 53 | } 54 | //如果有发送 则检测时间 55 | if(!ukcp.canSend(false)||(ukcp.checkFlush()&& ukcp.isFastFlush())){ 56 | long now =System.currentTimeMillis(); 57 | long next = ukcp.flush(now); 58 | ukcp.setTsUpdate(now+next); 59 | } 60 | }catch (Throwable e){ 61 | e.printStackTrace(); 62 | }finally { 63 | release(); 64 | } 65 | } 66 | 67 | 68 | public void release(){ 69 | ukcp.getWriteProcessing().set(false); 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /kcp-base/src/main/java/test/DelayPacket.java: -------------------------------------------------------------------------------- 1 | package test; 2 | 3 | import io.netty.buffer.ByteBuf; 4 | 5 | /** 6 | * Created by JinMiao 7 | * 2019-01-04. 8 | */ 9 | public class DelayPacket { 10 | private long ts; 11 | private ByteBuf ptr; 12 | 13 | 14 | public void init(ByteBuf src) { 15 | this.ptr = src.retainedSlice(); 16 | 17 | } 18 | 19 | 20 | public void release(){ 21 | ptr.release(); 22 | } 23 | 24 | public long getTs() { 25 | return ts; 26 | } 27 | 28 | public void setTs(long ts) { 29 | this.ts = ts; 30 | } 31 | 32 | public ByteBuf getPtr() { 33 | return ptr; 34 | } 35 | 36 | public void setPtr(ByteBuf ptr) { 37 | this.ptr = ptr; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /kcp-base/src/main/java/test/TestCrc32.java: -------------------------------------------------------------------------------- 1 | package test; 2 | 3 | /** 4 | * Created by JinMiao 5 | * 2019-04-16. 6 | */ 7 | public class TestCrc32 { 8 | 9 | //public static void main(String[] args) throws InterruptedException { 10 | // 11 | // CRC32 crc32 = new CRC32(); 12 | // byte[] bytes = new byte[1024]; 13 | // crc32.update(bytes); 14 | // System.out.println(crc32.getValue()); 15 | // ByteBuf byteBuf = PooledByteBufAllocator.DEFAULT.directBuffer(1024); 16 | // long now =System.currentTimeMillis(); 17 | // int i = 0; 18 | // byteBuf.writeBytes(new byte[1024]); 19 | // 20 | // ByteBuffer byteBuf1 = byteBuf.nioBuffer(); 21 | // while (true){ 22 | // i++; 23 | // if(i%10000000==0){ 24 | // System.out.println(System.currentTimeMillis()-now); 25 | // now =System.currentTimeMillis(); 26 | // } 27 | // //crc32.reset(); 28 | // //crc32.update(bytes); 29 | // crc32.reset(); 30 | // crc32.update(byteBuf1); 31 | // } 32 | // //System.out.println(crc32.getValue()); 33 | // 34 | // //new Thread(() -> { 35 | // // ByteBuf byteBuf = PooledByteBufAllocator.DEFAULT.directBuffer(20480); 36 | // // //byteBuf.writeInt(5); 37 | // // byteBuf.writeBytes(new byte[20480]); 38 | // // while (true){ 39 | // // ByteBuffer byteBuf1 = byteBuf.nioBuffer(); 40 | // // //try { 41 | // // // Thread.sleep(100); 42 | // // //} catch (InterruptedException e) { 43 | // // // e.printStackTrace(); 44 | // // //} 45 | // // } 46 | // //}).start(); 47 | // 48 | // 49 | // //Thread.sleep(50000000); 50 | //} 51 | ////@Test 52 | //public void crc32(){ 53 | // CRC32 crc32 = new CRC32(); 54 | // byte[] bytes = new byte[1024]; 55 | // crc32.update(bytes); 56 | // System.out.println(crc32.getValue()); 57 | // 58 | // //byteBuf1.putInt(0,3); 59 | // 60 | // //((DirectBuffer) byteBuf1).cleaner().clean(); 61 | // //System.out.println(byteBuf1.getInt(0)); 62 | // //System.out.println(byteBuf.getInt(0)); 63 | // 64 | // //System.out.println(); 65 | // 66 | // 67 | // 68 | // 69 | // //for (int i = 0; i < 20000000; i++) { 70 | // // crc32.update("abcdfg".getBytes()); 71 | // // crc32.getValue(); 72 | // //} 73 | // //long start = System.nanoTime(); 74 | // //for (int i = 0; i < 20000000; i++) { 75 | // // crc32.update("abcdfg".getBytes()); 76 | // // crc32.getValue(); 77 | // //} 78 | // 79 | // //System.out.println((System.nanoTime()-start)/20000000.0); 80 | //} 81 | } 82 | -------------------------------------------------------------------------------- /kcp-base/src/main/java/test/TestEncryption.java: -------------------------------------------------------------------------------- 1 | package test; 2 | 3 | import javax.crypto.*; 4 | import javax.crypto.spec.SecretKeySpec; 5 | import java.nio.ByteBuffer; 6 | import java.security.SecureRandom; 7 | 8 | /** 9 | * Created by JinMiao 10 | * 2019-06-24. 11 | */ 12 | public class TestEncryption { 13 | 14 | private Cipher encruyptCipher; 15 | 16 | private Cipher decryptCipher; 17 | public void init(){ 18 | String password = "SHA1PRNG"; 19 | try { 20 | KeyGenerator kgen = KeyGenerator.getInstance("AES"); 21 | 22 | SecureRandom random = SecureRandom.getInstance("SHA1PRNG"); 23 | random.setSeed(password.getBytes()); 24 | kgen.init(128, random);// 利用用户密码作为随机数初始化出 25 | SecretKey secretKey = kgen.generateKey();// 根据用户密码,生成一个密钥 26 | byte[] enCodeFormat = secretKey.getEncoded();// 返回基本编码格式的密钥,如果此密钥不支持编码,则返回 27 | SecretKeySpec key = new SecretKeySpec(enCodeFormat, "AES");// 转换为AES专用密钥 28 | 29 | 30 | encruyptCipher = Cipher.getInstance("AES");// 创建密码器 31 | encruyptCipher.init(Cipher.ENCRYPT_MODE, key);// 初始化为加密模式的密码器 32 | 33 | decryptCipher = Cipher.getInstance("AES");// 创建密码器 34 | decryptCipher.init(Cipher.DECRYPT_MODE, key);// 初始化为解密模式的密码器 35 | }catch (Exception e){ 36 | e.printStackTrace(); 37 | } 38 | 39 | } 40 | 41 | 42 | 43 | 44 | public void encrypt(ByteBuffer in,ByteBuffer out) throws BadPaddingException, ShortBufferException, IllegalBlockSizeException { 45 | encruyptCipher.doFinal(in,out); 46 | } 47 | 48 | 49 | public void decrypt(ByteBuffer in,ByteBuffer out) throws BadPaddingException, ShortBufferException, IllegalBlockSizeException { 50 | decryptCipher.doFinal(in,out); 51 | } 52 | 53 | 54 | //public static void main(String[] args) throws BadPaddingException, ShortBufferException, IllegalBlockSizeException { 55 | // test.TestEncryption testEncryption = new test.TestEncryption(); 56 | // testEncryption.init(); 57 | // ByteBuffer in = ByteBuffer.allocate(1024); 58 | // ByteBuffer out = ByteBuffer.allocate(1024); 59 | // 60 | // for (int i = 0; i < 100; i++) { 61 | // in.putInt(i); 62 | // } 63 | // 64 | // testEncryption.encruyptCipher.doFinal(in,out); 65 | // 66 | // 67 | // 68 | // byte[] outBytes = testEncryption.encruyptCipher.doFinal(in.array()); 69 | // byte[] inBytes = testEncryption.decryptCipher.doFinal(outBytes); 70 | // 71 | // 72 | // 73 | // while (in.hasRemaining()){ 74 | // System.out.println(in.getInt()); 75 | // } 76 | // 77 | // 78 | // 79 | // 80 | // 81 | // System.out.println(); 82 | // 83 | // 84 | // 85 | // 86 | // 87 | //} 88 | } 89 | -------------------------------------------------------------------------------- /kcp-base/src/main/java/test/Utils.java: -------------------------------------------------------------------------------- 1 | package test; 2 | 3 | import io.netty.buffer.ByteBuf; 4 | 5 | /** 6 | * Created by JinMiao 7 | * 2019/12/10. 8 | */ 9 | public class Utils { 10 | 11 | public static final synchronized void printByteBuffer(String log,ByteBuf byteBuf){ 12 | byte[] bytes = new byte[byteBuf.writerIndex()]; 13 | byteBuf.getBytes(0,bytes); 14 | System.err.println("-------"+log+" start -------------"); 15 | for (byte aByte : bytes) { 16 | System.err.print(aByte+","); 17 | } 18 | System.out.println(); 19 | System.err.println("-------"+log+" end -------------"); 20 | 21 | 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /kcp-base/src/main/java/threadPool/IMessageExecutor.java: -------------------------------------------------------------------------------- 1 | package threadPool; 2 | 3 | /** 4 | * 消息处理器 5 | */ 6 | public interface IMessageExecutor{ 7 | /** 8 | * 停止消息处理器 9 | */ 10 | void stop(); 11 | 12 | 13 | 14 | /** 15 | * 判断队列是否已经达到上限了 16 | * @return 17 | */ 18 | boolean isFull(); 19 | 20 | 21 | /** 22 | * 执行任务 23 | * 注意: 如果线程等于当前线程 则直接执行 如果非当前线程放进队列 24 | * 25 | * @param iTask 26 | */ 27 | void execute(ITask iTask); 28 | } -------------------------------------------------------------------------------- /kcp-base/src/main/java/threadPool/IMessageExecutorPool.java: -------------------------------------------------------------------------------- 1 | package threadPool; 2 | 3 | /** 4 | * Created by JinMiao 5 | * 2020/11/24. 6 | */ 7 | public interface IMessageExecutorPool { 8 | /** 9 | * 从线程池中按算法获得一个线程对象 10 | * @return 11 | */ 12 | IMessageExecutor getIMessageExecutor(); 13 | 14 | void stop(); 15 | 16 | } 17 | -------------------------------------------------------------------------------- /kcp-base/src/main/java/threadPool/ITask.java: -------------------------------------------------------------------------------- 1 | package threadPool; 2 | 3 | /** 4 | * Created by JinMiao 5 | * 2018/5/2. 6 | */ 7 | public interface ITask { 8 | void execute(); 9 | } 10 | -------------------------------------------------------------------------------- /kcp-base/src/main/java/threadPool/disruptor/DisruptorExecutorPool.java: -------------------------------------------------------------------------------- 1 | package threadPool.disruptor; 2 | 3 | import org.slf4j.Logger; 4 | import org.slf4j.LoggerFactory; 5 | import threadPool.IMessageExecutor; 6 | import threadPool.IMessageExecutorPool; 7 | 8 | import java.util.List; 9 | import java.util.Vector; 10 | import java.util.concurrent.atomic.AtomicInteger; 11 | 12 | /** 13 | * 基于disruptor的线程池 14 | * @author jinmiao 15 | * 2014-9-12 上午9:51:09 16 | */ 17 | public class DisruptorExecutorPool implements IMessageExecutorPool 18 | { 19 | private static final Logger log = LoggerFactory.getLogger(DisruptorExecutorPool.class); 20 | 21 | protected List executor = new Vector<>(); 22 | 23 | protected AtomicInteger index = new AtomicInteger(); 24 | 25 | 26 | 27 | public DisruptorExecutorPool(int workSize){ 28 | for (int i = 0; i < workSize; i++) { 29 | createDisruptorProcessor("DisruptorExecutorPool-"+i); 30 | } 31 | } 32 | 33 | 34 | /** 35 | * 创造一个线程对象 36 | * @param threadName 37 | * @return 38 | */ 39 | private IMessageExecutor createDisruptorProcessor(String threadName) 40 | { 41 | DisruptorSingleExecutor singleProcess = new DisruptorSingleExecutor(threadName); 42 | executor.add(singleProcess); 43 | singleProcess.start(); 44 | return singleProcess; 45 | } 46 | 47 | 48 | 49 | public void stop() 50 | { 51 | for(IMessageExecutor process:executor) 52 | { 53 | process.stop(); 54 | } 55 | 56 | //if(!scheduled.isShutdown()) 57 | // scheduled.shutdown(); 58 | } 59 | 60 | 61 | 62 | /** 63 | * 从线程池中按算法获得一个线程对象 64 | * @return 65 | */ 66 | public IMessageExecutor getIMessageExecutor() 67 | { 68 | int index = this.index.incrementAndGet(); 69 | return executor.get(index%executor.size()); 70 | } 71 | 72 | } 73 | -------------------------------------------------------------------------------- /kcp-base/src/main/java/threadPool/disruptor/DisruptorSingleExecutor.java: -------------------------------------------------------------------------------- 1 | package threadPool.disruptor; 2 | 3 | import com.lmax.disruptor.BlockingWaitStrategy; 4 | import com.lmax.disruptor.RingBuffer; 5 | import com.lmax.disruptor.WaitStrategy; 6 | import com.lmax.disruptor.dsl.Disruptor; 7 | import threadPool.IMessageExecutor; 8 | import threadPool.ITask; 9 | 10 | import java.util.concurrent.ThreadFactory; 11 | import java.util.concurrent.atomic.AtomicBoolean; 12 | 13 | /** 14 | * 基于 {@link #disruptor} 的单线程队列实现 15 | * @author King 16 | * 17 | */ 18 | public class DisruptorSingleExecutor implements IMessageExecutor { 19 | 20 | //65536条消息 21 | int ringBufferSize = 2<<15; 22 | 23 | private WaitStrategy strategy = new BlockingWaitStrategy(); 24 | 25 | 26 | private Disruptor disruptor = null; 27 | 28 | private RingBuffer buffer = null; 29 | 30 | private DistriptorEventFactory eventFactory = new DistriptorEventFactory(); 31 | 32 | private static final DistriptorEventHandler HANDLER = new DistriptorEventHandler(); 33 | 34 | private AtomicBoolean istop = new AtomicBoolean(); 35 | 36 | 37 | /**线程名字**/ 38 | private String threadName; 39 | 40 | private DisruptorThread currentThread; 41 | 42 | 43 | public DisruptorSingleExecutor(String threadName) 44 | { 45 | this.threadName = threadName; 46 | } 47 | 48 | 49 | @SuppressWarnings("unchecked") 50 | public void start() { 51 | LoopThreadfactory loopThreadfactory = new LoopThreadfactory(this); 52 | // disruptor = new Disruptor(eventFactory, ringBufferSize, executor, ProducerType.MULTI, strategy); 53 | disruptor = new Disruptor<>(eventFactory, ringBufferSize, loopThreadfactory); 54 | buffer = disruptor.getRingBuffer(); 55 | disruptor.handleEventsWith(DisruptorSingleExecutor.HANDLER); 56 | disruptor.start(); 57 | } 58 | 59 | 60 | 61 | /**主线程工厂**/ 62 | private class LoopThreadfactory implements ThreadFactory { 63 | IMessageExecutor iMessageExecutor; 64 | 65 | public LoopThreadfactory(IMessageExecutor iMessageExecutor) { 66 | this.iMessageExecutor = iMessageExecutor; 67 | } 68 | 69 | @Override 70 | public Thread newThread(Runnable r) { 71 | currentThread = new DisruptorThread(r,iMessageExecutor); 72 | currentThread.setName(threadName); 73 | return currentThread; 74 | } 75 | } 76 | 77 | 78 | static int num = 1; 79 | static long start = System.currentTimeMillis(); 80 | static long lastNum = 0; 81 | 82 | 83 | @Override 84 | public void stop() { 85 | if(istop.get()) { 86 | return; 87 | } 88 | disruptor.shutdown(); 89 | 90 | istop.set(true); 91 | } 92 | 93 | 94 | public static void main(String[] args) { 95 | DisruptorSingleExecutor disruptorSingleExecutor = new DisruptorSingleExecutor("aa"); 96 | disruptorSingleExecutor.start(); 97 | disruptorSingleExecutor.execute(() -> { 98 | System.out.println("hahaha"); 99 | }); 100 | 101 | 102 | try { 103 | Thread.sleep(10000); 104 | } catch (InterruptedException e) { 105 | e.printStackTrace(); 106 | } 107 | 108 | 109 | } 110 | 111 | public AtomicBoolean getIstop() { 112 | return istop; 113 | } 114 | 115 | 116 | @Override 117 | public boolean isFull() { 118 | return !buffer.hasAvailableCapacity(1); 119 | } 120 | 121 | @Override 122 | public void execute(ITask iTask){ 123 | Thread currentThread = Thread.currentThread(); 124 | //if(currentThread==this.currentThread){ 125 | // iTask.execute(); 126 | // return; 127 | //} 128 | // if(buffer.hasAvailableCapacity(1)) 129 | // { 130 | // System.out.println("没有容量了"); 131 | // } 132 | long next = buffer.next(); 133 | DistriptorHandler testEvent = buffer.get(next); 134 | testEvent.setTask(iTask); 135 | buffer.publish(next); 136 | } 137 | } 138 | -------------------------------------------------------------------------------- /kcp-base/src/main/java/threadPool/disruptor/DisruptorThread.java: -------------------------------------------------------------------------------- 1 | package threadPool.disruptor; 2 | 3 | import threadPool.IMessageExecutor; 4 | 5 | /** 6 | * Created by JinMiao 7 | * 2018/5/2. 8 | */ 9 | public class DisruptorThread extends Thread{ 10 | /** 11 | * 线程所属线程池 12 | */ 13 | private IMessageExecutor messageExecutor; 14 | 15 | 16 | public DisruptorThread(IMessageExecutor messageExecutor) { 17 | this.messageExecutor = messageExecutor; 18 | } 19 | 20 | public DisruptorThread(Runnable target, IMessageExecutor messageExecutor) { 21 | super(target); 22 | this.messageExecutor = messageExecutor; 23 | } 24 | 25 | public DisruptorThread(ThreadGroup group, Runnable target, IMessageExecutor messageExecutor) { 26 | super(group, target); 27 | this.messageExecutor = messageExecutor; 28 | } 29 | 30 | public DisruptorThread(String name, IMessageExecutor messageExecutor) { 31 | super(name); 32 | this.messageExecutor = messageExecutor; 33 | } 34 | 35 | public DisruptorThread(ThreadGroup group, String name, IMessageExecutor messageExecutor) { 36 | super(group, name); 37 | this.messageExecutor = messageExecutor; 38 | } 39 | 40 | public DisruptorThread(Runnable target, String name, IMessageExecutor messageExecutor) { 41 | super(target, name); 42 | this.messageExecutor = messageExecutor; 43 | } 44 | 45 | public DisruptorThread(ThreadGroup group, Runnable target, String name, IMessageExecutor messageExecutor) { 46 | super(group, target, name); 47 | this.messageExecutor = messageExecutor; 48 | } 49 | 50 | public DisruptorThread(ThreadGroup group, Runnable target, String name, long stackSize, IMessageExecutor messageExecutor) { 51 | super(group, target, name, stackSize); 52 | this.messageExecutor = messageExecutor; 53 | } 54 | 55 | public IMessageExecutor getMessageExecutor() { 56 | return messageExecutor; 57 | } 58 | 59 | public void setMessageExecutor(IMessageExecutor messageExecutor) { 60 | this.messageExecutor = messageExecutor; 61 | } 62 | 63 | @Override 64 | public String toString() { 65 | return "DisruptorThread{" + 66 | "messageExecutor=" + messageExecutor + 67 | '}'; 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /kcp-base/src/main/java/threadPool/disruptor/DistriptorEventFactory.java: -------------------------------------------------------------------------------- 1 | package threadPool.disruptor; 2 | 3 | import com.lmax.disruptor.EventFactory; 4 | 5 | public class DistriptorEventFactory implements EventFactory 6 | { 7 | 8 | @Override 9 | public DistriptorHandler newInstance() { 10 | return new DistriptorHandler(); 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /kcp-base/src/main/java/threadPool/disruptor/DistriptorEventHandler.java: -------------------------------------------------------------------------------- 1 | package threadPool.disruptor; 2 | 3 | import com.lmax.disruptor.EventHandler; 4 | 5 | public class DistriptorEventHandler implements EventHandler{ 6 | 7 | @Override 8 | public void onEvent(DistriptorHandler event, long sequence, 9 | boolean endOfBatch) { 10 | event.execute(); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /kcp-base/src/main/java/threadPool/disruptor/DistriptorHandler.java: -------------------------------------------------------------------------------- 1 | package threadPool.disruptor; 2 | 3 | import org.slf4j.Logger; 4 | import org.slf4j.LoggerFactory; 5 | import threadPool.ITask; 6 | 7 | public class DistriptorHandler 8 | { 9 | 10 | protected static final Logger logger = LoggerFactory.getLogger(DistriptorHandler.class); 11 | private ITask task; 12 | 13 | 14 | public void execute() 15 | { 16 | try { 17 | this.task.execute(); 18 | //得主动释放内存 19 | this.task = null; 20 | } catch (Throwable throwable) { 21 | logger.error("error",throwable); 22 | } 23 | } 24 | 25 | 26 | public void setTask(ITask task) { 27 | this.task = task; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /kcp-base/src/main/java/threadPool/netty/NettyMessageExecutor.java: -------------------------------------------------------------------------------- 1 | package threadPool.netty; 2 | 3 | import io.netty.channel.DefaultEventLoop; 4 | import io.netty.channel.EventLoop; 5 | import io.netty.util.concurrent.ThreadProperties; 6 | import threadPool.IMessageExecutor; 7 | import threadPool.ITask; 8 | 9 | /** 10 | * Created by JinMiao 11 | * 2020/11/24. 12 | */ 13 | public class NettyMessageExecutor implements IMessageExecutor { 14 | 15 | private EventLoop eventLoop; 16 | 17 | 18 | public NettyMessageExecutor(EventLoop eventLoop) { 19 | this.eventLoop = eventLoop; 20 | } 21 | 22 | @Override 23 | public void stop() { 24 | 25 | } 26 | 27 | @Override 28 | public boolean isFull() { 29 | return false; 30 | } 31 | 32 | @Override 33 | public void execute(ITask iTask) { 34 | //if(eventLoop.inEventLoop()){ 35 | // iTask.execute(); 36 | //}else{ 37 | this.eventLoop.execute(() -> iTask.execute()); 38 | //} 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /kcp-base/src/main/java/threadPool/netty/NettyMessageExecutorPool.java: -------------------------------------------------------------------------------- 1 | package threadPool.netty; 2 | 3 | import io.netty.channel.DefaultEventLoopGroup; 4 | import io.netty.channel.EventLoopGroup; 5 | import threadPool.IMessageExecutor; 6 | import threadPool.IMessageExecutorPool; 7 | 8 | import java.util.concurrent.atomic.AtomicInteger; 9 | 10 | /** 11 | * 基于netty eventloop的线程池 12 | * Created by JinMiao 13 | * 2020/11/24. 14 | */ 15 | public class NettyMessageExecutorPool implements IMessageExecutorPool { 16 | 17 | private EventLoopGroup eventExecutors; 18 | 19 | protected static final AtomicInteger index = new AtomicInteger(); 20 | 21 | public NettyMessageExecutorPool(int workSize){ 22 | eventExecutors = new DefaultEventLoopGroup(workSize, r -> { 23 | return new Thread(r,"nettyMessageExecutorPool-"+index.incrementAndGet()); 24 | }); 25 | } 26 | 27 | @Override 28 | public IMessageExecutor getIMessageExecutor() { 29 | return new NettyMessageExecutor(eventExecutors.next()); 30 | } 31 | 32 | @Override 33 | public void stop() { 34 | if(!eventExecutors.isShuttingDown()){ 35 | eventExecutors.shutdownGracefully(); 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /kcp-base/src/main/java/threadPool/order/IOrderTask.java: -------------------------------------------------------------------------------- 1 | package threadPool.order; 2 | 3 | /** 4 | * Created by JinMiao 5 | * 2020/6/19. 6 | */ 7 | public interface IOrderTask extends Runnable{ 8 | 9 | OrderedThreadSession getSession(); 10 | } 11 | -------------------------------------------------------------------------------- /kcp-base/src/main/java/threadPool/order/OrderedThreadSession.java: -------------------------------------------------------------------------------- 1 | package threadPool.order; 2 | 3 | import org.jctools.queues.MpscLinkedQueue; 4 | 5 | import java.util.Queue; 6 | import java.util.concurrent.atomic.AtomicBoolean; 7 | 8 | /** 9 | * Created by JinMiao 10 | * 2020/6/19. 11 | */ 12 | public class OrderedThreadSession { 13 | 14 | private int id; 15 | 16 | private Queue queue; 17 | 18 | /** 19 | * The current task state 20 | */ 21 | private final AtomicBoolean processingCompleted = new AtomicBoolean(true); 22 | 23 | //每次执行任务最大数量 24 | private int runTaskCount = 0; 25 | 26 | public OrderedThreadSession() { 27 | this(new MpscLinkedQueue<>()); 28 | } 29 | 30 | public OrderedThreadSession(Queue queue) { 31 | this(queue, Integer.MAX_VALUE); 32 | } 33 | 34 | public OrderedThreadSession(Queue queue, int runTaskCount) { 35 | this.queue = queue; 36 | this.runTaskCount = runTaskCount; 37 | } 38 | 39 | public void setQueue(Queue queue) { 40 | this.queue = queue; 41 | } 42 | 43 | public Queue getQueue() { 44 | return queue; 45 | } 46 | 47 | public AtomicBoolean getProcessingCompleted() { 48 | return processingCompleted; 49 | } 50 | 51 | public int getRunTaskCount() { 52 | return runTaskCount; 53 | } 54 | 55 | public int getId() { 56 | return id; 57 | } 58 | 59 | public void setId(int id) { 60 | this.id = id; 61 | } 62 | 63 | public void setRunTaskCount(int runTaskCount) { 64 | this.runTaskCount = runTaskCount; 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /kcp-base/src/main/java/threadPool/order/waiteStrategy/BlockingWaitConditionStrategy.java: -------------------------------------------------------------------------------- 1 | package threadPool.order.waiteStrategy; 2 | 3 | import java.util.concurrent.TimeUnit; 4 | import java.util.concurrent.locks.Condition; 5 | import java.util.concurrent.locks.Lock; 6 | import java.util.concurrent.locks.ReentrantLock; 7 | 8 | /** 9 | * Disruptor默认的等待策略是BlockingWaitStrategy。 10 | * 这个策略的内部适用一个锁和条件变量来控制线程的执行和等待(Java基本的同步方法)。 11 | * BlockingWaitStrategy是最慢的等待策略,但也是CPU使用率最低和最稳定的选项。 12 | * 然而,可以根据不同的部署环境调整选项以提高性能。 13 | */ 14 | public final class BlockingWaitConditionStrategy implements WaitConditionStrategy { 15 | 16 | private final Lock lock; 17 | private final Condition processorNotifyCondition; 18 | 19 | public BlockingWaitConditionStrategy() { 20 | this(false); 21 | } 22 | 23 | public BlockingWaitConditionStrategy(boolean fairSync) { 24 | this.lock = new ReentrantLock(fairSync); 25 | this.processorNotifyCondition = this.lock.newCondition(); 26 | } 27 | 28 | public void signalAllWhenBlocking() { 29 | this.lock.lock(); 30 | try { 31 | this.processorNotifyCondition.signalAll(); 32 | //this.processorNotifyCondition.signal(); 33 | } finally { 34 | this.lock.unlock(); 35 | } 36 | } 37 | 38 | 39 | @Override 40 | public T waitFor(WaitCondition waitCondition, long timeOut, TimeUnit unit) throws InterruptedException { 41 | this.lock.lock(); 42 | try { 43 | long waitTime = unit.toNanos(timeOut); 44 | T task = waitCondition.getAttach(); 45 | if(task==null) 46 | { 47 | this.processorNotifyCondition.awaitNanos(waitTime); 48 | task = waitCondition.getAttach(); 49 | } 50 | return task; 51 | } finally { 52 | this.lock.unlock(); 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /kcp-base/src/main/java/threadPool/order/waiteStrategy/BusySpinWaitConditionStrategy.java: -------------------------------------------------------------------------------- 1 | package threadPool.order.waiteStrategy; 2 | 3 | import org.slf4j.Logger; 4 | import org.slf4j.LoggerFactory; 5 | 6 | import java.util.concurrent.TimeUnit; 7 | 8 | /** 9 | * BusySpinWaitStrategy是性能最高的等待策略, 10 | * 同时也是对部署环境要求最高的策略。 11 | * 这个性能最好用在事件处理线程比物理内核数目还要小的时候。 12 | * 例如:在禁用超线程技术的时候。 13 | */ 14 | public final class BusySpinWaitConditionStrategy implements WaitConditionStrategy 15 | { 16 | protected final Logger log= LoggerFactory.getLogger(getClass()); 17 | 18 | @Override 19 | public T waitFor(WaitCondition waitCondition, long timeOut, TimeUnit unit) { 20 | long endTime=System.nanoTime()+unit.toNanos(timeOut); 21 | T task; 22 | while((task=waitCondition.getAttach())==null) 23 | { 24 | if(System.nanoTime()>=endTime) 25 | { 26 | break; 27 | } 28 | } 29 | return task; 30 | } 31 | 32 | @Override 33 | public void signalAllWhenBlocking() 34 | { 35 | 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /kcp-base/src/main/java/threadPool/order/waiteStrategy/SleepingWaitConditionStrategy.java: -------------------------------------------------------------------------------- 1 | package threadPool.order.waiteStrategy; 2 | 3 | import org.slf4j.Logger; 4 | import org.slf4j.LoggerFactory; 5 | 6 | import java.util.concurrent.TimeUnit; 7 | import java.util.concurrent.locks.LockSupport; 8 | 9 | /** 10 | * SleepingWaitStrategy和BlockingWaitStrategy一样, SpleepingWaitStrategy的CPU使用率也比较低。 11 | * 它的方式是循环等待并且在循环中间调用LockSupport.parkNanos(1)来睡眠,(在Linux系统上面睡眠时间60µs). 12 | * 然而,它的优点在于生产线程只需要计数,而不执行任何指令。并且没有条件变量的消耗。 13 | * 但是,事件对象从生产者到消费者传递的延迟变大了。SleepingWaitStrategy最好用在不需要低延迟, 14 | * 而且事件发布对于生产者的影响比较小的情况下。比如异步日志功能。 15 | */ 16 | public final class SleepingWaitConditionStrategy implements WaitConditionStrategy 17 | { 18 | protected final Logger log= LoggerFactory.getLogger(getClass()); 19 | private static final int DEFAULT_RETRIES = 200; 20 | 21 | private final int retries; 22 | 23 | public SleepingWaitConditionStrategy() 24 | { 25 | this(DEFAULT_RETRIES); 26 | } 27 | 28 | public SleepingWaitConditionStrategy(int retries) 29 | { 30 | this.retries = retries; 31 | } 32 | 33 | 34 | @Override 35 | public T waitFor(WaitCondition waitCondition, long timeOut, TimeUnit unit) { 36 | int counter = retries; 37 | long endTime=System.nanoTime()+unit.toNanos(timeOut); 38 | T task; 39 | while ((task=waitCondition.getAttach())==null) 40 | { 41 | if(System.nanoTime()>=endTime) 42 | { 43 | break; 44 | } 45 | counter = applyWaitMethod(counter); 46 | } 47 | return task; 48 | } 49 | 50 | @Override 51 | public void signalAllWhenBlocking() 52 | { 53 | } 54 | 55 | private int applyWaitMethod(int counter) 56 | { 57 | if (counter > 100) { 58 | --counter; 59 | } else if (counter > 0) { 60 | --counter; 61 | Thread.yield(); 62 | } else { 63 | LockSupport.parkNanos(1L); 64 | } 65 | return counter; 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /kcp-base/src/main/java/threadPool/order/waiteStrategy/WaitCondition.java: -------------------------------------------------------------------------------- 1 | package threadPool.order.waiteStrategy; 2 | 3 | /** 4 | * 等待条件 5 | * @param 6 | */ 7 | public interface WaitCondition { 8 | 9 | /** 10 | * 附件 11 | * @return 12 | */ 13 | T getAttach(); 14 | 15 | } 16 | -------------------------------------------------------------------------------- /kcp-base/src/main/java/threadPool/order/waiteStrategy/WaitConditionStrategy.java: -------------------------------------------------------------------------------- 1 | package threadPool.order.waiteStrategy; 2 | 3 | import java.util.concurrent.TimeUnit; 4 | 5 | public interface WaitConditionStrategy 6 | { 7 | 8 | /** 9 | * 等待一个条件,如果条件成立返回结果,如果超过时间,不管成立与否都返回结果 10 | * @param waitCondition 11 | * @param timeOut 12 | * @param unit 13 | * @return 14 | * @throws InterruptedException 15 | */ 16 | T waitFor(WaitCondition waitCondition,long timeOut,TimeUnit unit) throws InterruptedException; 17 | 18 | /** 19 | * 释放解除阻塞信号 20 | */ 21 | void signalAllWhenBlocking(); 22 | } 23 | -------------------------------------------------------------------------------- /kcp-base/src/main/java/threadPool/order/waiteStrategy/YieldingWaitConditionStrategy.java: -------------------------------------------------------------------------------- 1 | package threadPool.order.waiteStrategy; 2 | 3 | import org.slf4j.Logger; 4 | import org.slf4j.LoggerFactory; 5 | 6 | import java.util.concurrent.TimeUnit; 7 | 8 | /** 9 | * YieldingWaitStrategy是可以被用在低延迟系统中的两个策略之一, 10 | * 这种策略在减低系统延迟的同时也会增加CPU运算量。 11 | * YieldingWaitStrategy策略会循环等待sequence增加到合适的值。 12 | * 循环中调用Thread.yield()允许其他准备好的线程执行。 13 | * 如果需要高性能而且事件消费者线程比逻辑内核少的时候, 14 | * 推荐使用YieldingWaitStrategy策略。例如:在开启超线程的时候。 15 | * @author juebanlin 16 | */ 17 | public final class YieldingWaitConditionStrategy implements WaitConditionStrategy 18 | { 19 | protected final Logger log= LoggerFactory.getLogger(getClass()); 20 | private static final int SPIN_TRIES = 1000; 21 | 22 | 23 | @Override 24 | public T waitFor(WaitCondition waitCondition, long timeOut, TimeUnit unit) { 25 | long endTime=System.nanoTime()+unit.toNanos(timeOut); 26 | int counter = SPIN_TRIES; 27 | T task; 28 | while ((task=waitCondition.getAttach())==null) 29 | { 30 | if(System.nanoTime()>=endTime) 31 | { 32 | break; 33 | } 34 | counter = applyWaitMethod(counter);//等待 35 | } 36 | return task; 37 | } 38 | 39 | @Override 40 | public void signalAllWhenBlocking() 41 | { 42 | } 43 | 44 | private int applyWaitMethod(int counter) 45 | { 46 | if (0 == counter) 47 | { 48 | Thread.yield(); 49 | } 50 | else 51 | { 52 | --counter; 53 | } 54 | return counter; 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /kcp-base/src/test/java/main/BytebufBenchmark.java: -------------------------------------------------------------------------------- 1 | package main; 2 | 3 | import io.netty.buffer.ByteBuf; 4 | import io.netty.buffer.ByteBufAllocator; 5 | import org.jctools.queues.MpscLinkedQueue; 6 | import org.jctools.queues.atomic.MpscLinkedAtomicQueue; 7 | import org.openjdk.jmh.annotations.*; 8 | import org.openjdk.jmh.infra.Control; 9 | 10 | import java.util.Queue; 11 | import java.util.concurrent.TimeUnit; 12 | 13 | /** 14 | * 15 | * Created by JinMiao 16 | * 2020/6/24. 17 | */ 18 | @BenchmarkMode(Mode.Throughput) 19 | @OutputTimeUnit(TimeUnit.SECONDS) 20 | @Fork(1) 21 | @Warmup(iterations = 1) 22 | @Measurement(iterations = 3) 23 | @State(Scope.Group) 24 | public class BytebufBenchmark { 25 | 26 | private ByteBuf byteBuf; 27 | 28 | @Setup(Level.Trial) 29 | public void setUp() { 30 | byteBuf = ByteBufAllocator.DEFAULT.directBuffer(400); 31 | byteBuf.writeBytes(new byte[400]); 32 | } 33 | 34 | @Benchmark 35 | @Group("retainedDuplicate") 36 | public void retainedDuplicate() { 37 | byteBuf.retainedDuplicate().release(); 38 | } 39 | 40 | @Benchmark 41 | @Group("retain") 42 | public void retain() { 43 | byteBuf.retain().release(); 44 | 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /kcp-base/src/test/java/main/KcpThreadBenchmark.java: -------------------------------------------------------------------------------- 1 | package main; 2 | 3 | import org.openjdk.jmh.annotations.Level; 4 | import org.openjdk.jmh.annotations.Setup; 5 | import threadPool.disruptor.DisruptorSingleExecutor; 6 | import threadPool.IMessageExecutor; 7 | 8 | import java.util.Queue; 9 | 10 | /** 11 | * Created by JinMiao 12 | * 2020/5/20. 13 | */ 14 | public class KcpThreadBenchmark { 15 | 16 | private Queue recieveList; 17 | public static final int PRODUCER_THREADS_NUMBER = 4; 18 | 19 | private IMessageExecutor iMessageExecutor; 20 | @Setup(Level.Trial) 21 | public void setUp() { 22 | iMessageExecutor = new DisruptorSingleExecutor(""); 23 | //recieveList = new SpscArrayQueue<>(2<<11); 24 | 25 | 26 | } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /kcp-base/src/test/java/main/MpmcBenchmark.java: -------------------------------------------------------------------------------- 1 | package main; 2 | 3 | import org.openjdk.jmh.annotations.*; 4 | import org.openjdk.jmh.infra.Control; 5 | 6 | import java.util.Queue; 7 | import java.util.concurrent.ArrayBlockingQueue; 8 | import java.util.concurrent.TimeUnit; 9 | 10 | /** 11 | * Created by JinMiao 12 | * 2020/5/20. 13 | */ 14 | @BenchmarkMode(Mode.Throughput) 15 | @OutputTimeUnit(TimeUnit.MILLISECONDS) 16 | @Fork(1) 17 | @Warmup(iterations = 1) 18 | @Measurement(iterations = 3) 19 | @State(Scope.Group) 20 | public class MpmcBenchmark { 21 | 22 | public static final String PARAM_UNSAFE = "MpmcArrayQueue"; 23 | public static final String PARAM_AFU = "MpmcAtomicArrayQueue"; 24 | public static final String PARAM_JDK = "ArrayBlockingQueue"; 25 | 26 | public static final int PRODUCER_THREADS_NUMBER = 4; 27 | public static final int CONSUMER_THREADS_NUMBER = 4; 28 | 29 | public static final String GROUP_NAME = "MyGroup"; 30 | 31 | public static final int CAPACITY = 128; 32 | 33 | @Param({PARAM_UNSAFE, PARAM_AFU, PARAM_JDK}) 34 | public volatile String implementation; 35 | 36 | public volatile Queue queue; 37 | 38 | @Setup(Level.Trial) 39 | public void setUp() { 40 | switch (implementation) { 41 | case PARAM_UNSAFE: 42 | //queue = new MpmcArrayQueue<>(CAPACITY); 43 | break; 44 | case PARAM_AFU: 45 | //queue = new MpmcAtomicArrayQueue<>(CAPACITY); 46 | break; 47 | case PARAM_JDK: 48 | queue = new ArrayBlockingQueue<>(CAPACITY); 49 | break; 50 | default: 51 | throw new UnsupportedOperationException("Unsupported implementation " + implementation); 52 | } 53 | } 54 | 55 | 56 | @Benchmark 57 | @Group(GROUP_NAME) 58 | @GroupThreads(PRODUCER_THREADS_NUMBER) 59 | public void write(Control control) { 60 | while (!control.stopMeasurement && !queue.offer(1L)) { 61 | } 62 | } 63 | 64 | @Benchmark 65 | @Group(GROUP_NAME) 66 | @GroupThreads(CONSUMER_THREADS_NUMBER) 67 | public void read(Control control) { 68 | while (!control.stopMeasurement && queue.poll() == null) { 69 | } 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /kcp-base/src/test/java/main/SignalBenchmark.java: -------------------------------------------------------------------------------- 1 | package main; 2 | 3 | import org.openjdk.jmh.annotations.*; 4 | import org.openjdk.jmh.infra.Control; 5 | 6 | import java.util.concurrent.TimeUnit; 7 | import java.util.concurrent.locks.Condition; 8 | import java.util.concurrent.locks.Lock; 9 | import java.util.concurrent.locks.ReentrantLock; 10 | 11 | /** 12 | Benchmark Mode Cnt Score Error Units 13 | SignalBenchmark.signal thrpt 3 37230465.734 ± 50417551.328 ops/s 14 | SignalBenchmark.signal:await thrpt 3 158900.873 ± 685914.242 ops/s 15 | SignalBenchmark.signal:signal thrpt 3 37071564.860 ± 51093152.567 ops/s 16 | SignalBenchmark.signalAll thrpt 3 37483352.305 ± 32047146.318 ops/s 17 | SignalBenchmark.signalAll:awaitForSignalAll thrpt 3 152516.702 ± 616198.089 ops/s 18 | SignalBenchmark.signalAll:signalAll thrpt 3 37330835.603 ± 32604177.865 ops/s 19 | * Created by JinMiao 20 | * 2020/6/22. 21 | */ 22 | @BenchmarkMode(Mode.Throughput) 23 | @OutputTimeUnit(TimeUnit.SECONDS) 24 | @Fork(1) 25 | @Warmup(iterations = 1) 26 | @Measurement(iterations = 3) 27 | @State(Scope.Group) 28 | public class SignalBenchmark { 29 | 30 | private Condition signalCondition; 31 | private Lock signalLock; 32 | 33 | private Condition signalAllCondition; 34 | private Lock signaAlllLock; 35 | 36 | @Setup(Level.Trial) 37 | public void setUp() { 38 | signalLock = new ReentrantLock(); 39 | signalCondition = signalLock.newCondition(); 40 | signaAlllLock = new ReentrantLock(); 41 | signalAllCondition = signaAlllLock.newCondition(); 42 | } 43 | 44 | 45 | @Benchmark 46 | @GroupThreads(1) 47 | @Group("signal") 48 | public void signal(Control control) { 49 | try { 50 | signalLock.lock(); 51 | signalCondition.signal(); 52 | }finally { 53 | signalLock.unlock(); 54 | } 55 | } 56 | 57 | @Benchmark 58 | @GroupThreads(1) 59 | @Group("signal") 60 | public void await(Control control) { 61 | try { 62 | signalLock.lock(); 63 | signalCondition.await(); 64 | } catch (InterruptedException e) { 65 | e.printStackTrace(); 66 | } finally { 67 | signalLock.unlock(); 68 | } 69 | } 70 | 71 | 72 | @Benchmark 73 | @GroupThreads(1) 74 | @Group("signalAll") 75 | public void signalAll(Control control) { 76 | try { 77 | signaAlllLock.lock(); 78 | signalAllCondition.signal(); 79 | }finally { 80 | signaAlllLock.unlock(); 81 | } 82 | } 83 | 84 | 85 | @Benchmark 86 | @GroupThreads(1) 87 | @Group("signalAll") 88 | public void awaitForSignalAll(Control control) { 89 | try { 90 | signaAlllLock.lock(); 91 | signalAllCondition.await(); 92 | } catch (InterruptedException e) { 93 | e.printStackTrace(); 94 | } finally { 95 | signaAlllLock.unlock(); 96 | } 97 | } 98 | 99 | 100 | } 101 | -------------------------------------------------------------------------------- /kcp-base/src/test/java/main/VolatileAtomicBenchmark.java: -------------------------------------------------------------------------------- 1 | package main; 2 | 3 | import org.openjdk.jmh.annotations.*; 4 | import org.openjdk.jmh.infra.Control; 5 | 6 | import java.util.concurrent.TimeUnit; 7 | import java.util.concurrent.atomic.AtomicInteger; 8 | 9 | /** 10 | * Benchmark Mode Cnt Score Error Units 11 | * VolatileAtomicBenchmark.GROUP_NAME thrpt 3 11520559872.738 ± 2548748730.823 ops/s 12 | * VolatileAtomicBenchmark.GROUP_NAME:volatileRead thrpt 3 11368008033.661 ± 2518167944.554 ops/s 13 | * VolatileAtomicBenchmark.GROUP_NAME:volatileWrite thrpt 3 152551839.078 ± 30595466.236 ops/s 14 | * VolatileAtomicBenchmark.GROUP_NAME1 thrpt 3 5491817786.486 ± 25550691348.844 ops/s 15 | * VolatileAtomicBenchmark.GROUP_NAME1:atmoicRead thrpt 3 5362082320.914 ± 25385552586.081 ops/s 16 | * VolatileAtomicBenchmark.GROUP_NAME1:atmoicWrite thrpt 3 129735465.572 ± 169450075.999 ops/s 17 | * Created by JinMiao 18 | * 2020/6/22. 19 | */ 20 | @BenchmarkMode(Mode.Throughput) 21 | @OutputTimeUnit(TimeUnit.SECONDS) 22 | @Fork(1) 23 | @Warmup(iterations = 1) 24 | @Measurement(iterations = 3) 25 | @State(Scope.Group) 26 | public class VolatileAtomicBenchmark { 27 | 28 | private volatile int vol; 29 | 30 | private AtomicInteger atomicInteger = new AtomicInteger(); 31 | 32 | @Setup(Level.Trial) 33 | public void setUp() { 34 | vol = 0; 35 | } 36 | @Benchmark 37 | @GroupThreads(1) 38 | @Group("GROUP_NAME") 39 | public void volatileWrite(Control control) { 40 | vol++; 41 | } 42 | 43 | @Benchmark 44 | @GroupThreads(3) 45 | @Group("GROUP_NAME") 46 | public void volatileRead(Control control) { 47 | int b = vol; 48 | b++; 49 | } 50 | 51 | 52 | @Benchmark 53 | @GroupThreads(1) 54 | @Group("GROUP_NAME1") 55 | public void atmoicWrite(Control control) { 56 | atomicInteger.incrementAndGet(); 57 | } 58 | 59 | @Benchmark 60 | @GroupThreads(3) 61 | @Group("GROUP_NAME1") 62 | public void atmoicRead(Control control) { 63 | int c = atomicInteger.get(); 64 | c++; 65 | } 66 | 67 | } 68 | -------------------------------------------------------------------------------- /kcp-example/README.md: -------------------------------------------------------------------------------- 1 | # kcp-example kcp的例子 2 | 3 | #交流 4 | QQ:526167774 5 | 6 | -------------------------------------------------------------------------------- /kcp-example/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | java-Kcp 7 | com.github.l42111996 8 | 1.6.1 9 | 10 | 4.0.0 11 | 12 | kcp-example 13 | 14 | 15 | 16 | com.github.l42111996 17 | kcp-base 18 | ${project.version} 19 | 20 | 21 | com.lmax 22 | disruptor 23 | 3.4.2 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /kcp-example/src/main/java/test/Kcp4GoExampleClient.java: -------------------------------------------------------------------------------- 1 | package test; 2 | 3 | import com.backblaze.erasure.FecAdapt; 4 | import io.netty.buffer.ByteBuf; 5 | import io.netty.buffer.ByteBufAllocator; 6 | import kcp.ChannelConfig; 7 | import kcp.KcpClient; 8 | import kcp.KcpListener; 9 | import kcp.Ukcp; 10 | 11 | import java.net.InetSocketAddress; 12 | 13 | /** 14 | * 与go版本兼容的客户端 15 | * Created by JinMiao 16 | * 2019/11/29. 17 | */ 18 | public class Kcp4GoExampleClient implements KcpListener { 19 | 20 | public static void main(String[] args) { 21 | ChannelConfig channelConfig = new ChannelConfig(); 22 | channelConfig.nodelay(true,40,2,true); 23 | channelConfig.setSndwnd(1024); 24 | channelConfig.setRcvwnd(1024); 25 | channelConfig.setMtu(1400); 26 | channelConfig.setFecAdapt(new FecAdapt(10,3)); 27 | channelConfig.setAckNoDelay(false); 28 | //channelConfig.setTimeoutMillis(10000); 29 | 30 | //禁用参数 31 | channelConfig.setCrc32Check(false); 32 | channelConfig.setAckMaskSize(0); 33 | 34 | 35 | KcpClient kcpClient = new KcpClient(); 36 | kcpClient.init(channelConfig); 37 | 38 | 39 | Kcp4GoExampleClient kcpGoExampleClient = new Kcp4GoExampleClient(); 40 | Ukcp ukcp = kcpClient.connect(new InetSocketAddress("127.0.0.1", 10000), channelConfig, kcpGoExampleClient); 41 | String msg = "hello!!!!!11111111111111111111111111"; 42 | byte[] bytes = msg.getBytes(); 43 | ByteBuf byteBuf = ByteBufAllocator.DEFAULT.ioBuffer(bytes.length); 44 | byteBuf.writeBytes(bytes); 45 | ukcp.write(byteBuf); 46 | 47 | } 48 | @Override 49 | public void onConnected(Ukcp ukcp) { 50 | 51 | } 52 | 53 | @Override 54 | public void handleReceive(ByteBuf byteBuf, Ukcp ukcp) { 55 | 56 | } 57 | 58 | @Override 59 | public void handleException(Throwable ex, Ukcp ukcp) { 60 | 61 | } 62 | 63 | @Override 64 | public void handleClose(Ukcp ukcp) { 65 | 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /kcp-example/src/main/java/test/Kcp4sharpExampleServer.java: -------------------------------------------------------------------------------- 1 | package test; 2 | 3 | import com.backblaze.erasure.fec.Snmp; 4 | import io.netty.buffer.ByteBuf; 5 | import kcp.ChannelConfig; 6 | import kcp.KcpListener; 7 | import kcp.KcpServer; 8 | import kcp.Ukcp; 9 | 10 | /** 11 | * 与c#版本兼容的客户端 12 | * Created by JinMiao 13 | * 2019-07-23. 14 | */ 15 | public class Kcp4sharpExampleServer implements KcpListener { 16 | 17 | public static void main(String[] args) { 18 | 19 | Kcp4sharpExampleServer kcpRttExampleServer = new Kcp4sharpExampleServer(); 20 | 21 | ChannelConfig channelConfig = new ChannelConfig(); 22 | channelConfig.nodelay(true,10,2,true); 23 | channelConfig.setSndwnd(300); 24 | channelConfig.setRcvwnd(300); 25 | channelConfig.setMtu(512); 26 | channelConfig.setAckNoDelay(true); 27 | channelConfig.setTimeoutMillis(10000); 28 | //channelConfig.setFecDataShardCount(10); 29 | //channelConfig.setFecParityShardCount(3); 30 | //c# crc32未实现 31 | channelConfig.setCrc32Check(false); 32 | KcpServer kcpServer = new KcpServer(); 33 | kcpServer.init(kcpRttExampleServer,channelConfig,10009); 34 | } 35 | 36 | 37 | @Override 38 | public void onConnected(Ukcp ukcp) { 39 | System.out.println("有连接进来"+Thread.currentThread().getName()+ukcp.user().getRemoteAddress()); 40 | } 41 | 42 | @Override 43 | public void handleReceive(ByteBuf buf, Ukcp kcp) { 44 | byte[] bytes = new byte[buf.readableBytes()]; 45 | buf.getBytes(buf.readerIndex(),bytes); 46 | System.out.println("收到消息: "+new String(bytes)); 47 | kcp.write(buf); 48 | } 49 | 50 | @Override 51 | public void handleException(Throwable ex, Ukcp kcp) { 52 | ex.printStackTrace(); 53 | } 54 | 55 | @Override 56 | public void handleClose(Ukcp kcp) { 57 | System.out.println(Snmp.snmp.toString()); 58 | Snmp.snmp = new Snmp(); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /kcp-example/src/main/java/test/KcpDisconnectExampleClient.java: -------------------------------------------------------------------------------- 1 | package test; 2 | 3 | import io.netty.buffer.ByteBuf; 4 | import io.netty.buffer.UnpooledByteBufAllocator; 5 | import kcp.ChannelConfig; 6 | import kcp.KcpClient; 7 | import kcp.KcpListener; 8 | import kcp.Ukcp; 9 | 10 | import java.net.InetSocketAddress; 11 | import java.util.Timer; 12 | import java.util.TimerTask; 13 | import java.util.concurrent.atomic.AtomicInteger; 14 | 15 | /** 16 | * 重复新连接进入断开测试内存泄漏客户端 17 | * Created by JinMiao 18 | * 2019-06-27. 19 | */ 20 | public class KcpDisconnectExampleClient implements KcpListener { 21 | 22 | public static void main(String[] args) { 23 | ChannelConfig channelConfig = new ChannelConfig(); 24 | channelConfig.nodelay(true, 40, 2, true); 25 | channelConfig.setSndwnd(1024); 26 | channelConfig.setRcvwnd(1024); 27 | channelConfig.setMtu(1400); 28 | //channelConfig.setFecDataShardCount(10); 29 | //channelConfig.setFecParityShardCount(3); 30 | //channelConfig.setAckNoDelay(true); 31 | //channelConfig.setCrc32Check(true); 32 | //channelConfig.setTimeoutMillis(10000); 33 | channelConfig.setConv(55); 34 | channelConfig.setUseConvChannel(true); 35 | 36 | KcpClient kcpClient = new KcpClient(); 37 | kcpClient.init(channelConfig); 38 | 39 | KcpDisconnectExampleClient kcpClientRttExample = new KcpDisconnectExampleClient(); 40 | Timer timer = new Timer(); 41 | timer.scheduleAtFixedRate(new TimerTask() { 42 | @Override 43 | public void run() { 44 | for (int i = 0; i < 100; i++) { 45 | try { 46 | channelConfig.setConv(id.incrementAndGet()); 47 | kcpClient.connect(new InetSocketAddress("127.0.0.1", 10031), channelConfig, kcpClientRttExample); 48 | }catch (Exception e){ 49 | e.printStackTrace(); 50 | } 51 | } 52 | } 53 | }, 1000, 1000); 54 | } 55 | 56 | private static final AtomicInteger id = new AtomicInteger(); 57 | 58 | @Override 59 | public void onConnected(Ukcp ukcp) { 60 | for (int i = 0; i < 100; i++) { 61 | ByteBuf byteBuf = UnpooledByteBufAllocator.DEFAULT.buffer(1024); 62 | byteBuf.writeInt(i); 63 | byte[] bytes = new byte[1020]; 64 | byteBuf.writeBytes(bytes); 65 | ukcp.write(byteBuf); 66 | byteBuf.release(); 67 | } 68 | } 69 | 70 | @Override 71 | public void handleReceive(ByteBuf byteBuf, Ukcp ukcp) { 72 | if(byteBuf.getInt(0)==99){ 73 | ukcp.close(); 74 | } 75 | } 76 | 77 | @Override 78 | public void handleException(Throwable ex, Ukcp kcp) { 79 | ex.printStackTrace(); 80 | } 81 | 82 | @Override 83 | public void handleClose(Ukcp kcp) { 84 | System.out.println("连接断开了"); 85 | } 86 | 87 | 88 | } 89 | -------------------------------------------------------------------------------- /kcp-example/src/main/java/test/KcpDisconnectExampleServer.java: -------------------------------------------------------------------------------- 1 | package test; 2 | 3 | import com.backblaze.erasure.fec.Snmp; 4 | import io.netty.buffer.ByteBuf; 5 | import kcp.ChannelConfig; 6 | import kcp.KcpListener; 7 | import kcp.KcpServer; 8 | import kcp.Ukcp; 9 | 10 | /** 11 | * 重复新连接进入断开测试内存泄漏服务器 12 | * Created by JinMiao 13 | * 2019-06-27. 14 | */ 15 | public class KcpDisconnectExampleServer implements KcpListener { 16 | 17 | public static void main(String[] args) { 18 | 19 | KcpDisconnectExampleServer kcpRttExampleServer = new KcpDisconnectExampleServer(); 20 | ChannelConfig channelConfig = new ChannelConfig(); 21 | channelConfig.nodelay(true, 40, 2, true); 22 | channelConfig.setSndwnd(1024); 23 | channelConfig.setRcvwnd(1024); 24 | channelConfig.setMtu(1400); 25 | //channelConfig.setFecDataShardCount(10); 26 | //channelConfig.setFecParityShardCount(3); 27 | //channelConfig.setAckNoDelay(true); 28 | //channelConfig.setCrc32Check(true); 29 | channelConfig.setUseConvChannel(true); 30 | channelConfig.setTimeoutMillis(5000); 31 | KcpServer kcpServer = new KcpServer(); 32 | kcpServer.init(kcpRttExampleServer, channelConfig, 10031); 33 | } 34 | 35 | 36 | @Override 37 | public void onConnected(Ukcp ukcp) { 38 | System.out.println("有连接进来 " + Thread.currentThread().getName() + ukcp.user().getRemoteAddress()); 39 | } 40 | 41 | 42 | 43 | @Override 44 | public void handleReceive(ByteBuf buf, Ukcp kcp) { 45 | kcp.write(buf); 46 | } 47 | 48 | @Override 49 | public void handleException(Throwable ex, Ukcp kcp) { 50 | ex.printStackTrace(); 51 | } 52 | 53 | @Override 54 | public void handleClose(Ukcp kcp) { 55 | System.out.println("连接断开了 "+kcp.getConv()); 56 | } 57 | } -------------------------------------------------------------------------------- /kcp-example/src/main/java/test/KcpIdleExampleClient.java: -------------------------------------------------------------------------------- 1 | package test; 2 | 3 | import io.netty.buffer.ByteBuf; 4 | import io.netty.buffer.UnpooledByteBufAllocator; 5 | import kcp.*; 6 | 7 | import java.net.InetSocketAddress; 8 | 9 | /** 10 | * 测试大量连接不通讯的例子 11 | * Created by JinMiao 12 | * 2019-07-10. 13 | */ 14 | public class KcpIdleExampleClient implements KcpListener { 15 | 16 | public static void main(String[] args) { 17 | 18 | ChannelConfig channelConfig = new ChannelConfig(); 19 | channelConfig.nodelay(true,40,2,true); 20 | channelConfig.setSndwnd(1024); 21 | channelConfig.setRcvwnd(1024); 22 | channelConfig.setMtu(1400); 23 | //channelConfig.setFecDataShardCount(10); 24 | //channelConfig.setFecParityShardCount(3); 25 | channelConfig.setAckNoDelay(false); 26 | channelConfig.setCrc32Check(true); 27 | //channelConfig.setTimeoutMillis(10000); 28 | 29 | KcpClient kcpClient = new KcpClient(); 30 | kcpClient.init(channelConfig); 31 | 32 | 33 | for (int i = 0; i < 3; i++) { 34 | if(i%1000==0){ 35 | try { 36 | Thread.sleep(1000); 37 | } catch (InterruptedException e) { 38 | e.printStackTrace(); 39 | } 40 | } 41 | KcpIdleExampleClient kcpIdleExampleClient = new KcpIdleExampleClient(); 42 | //kcpClient.connect(new InetSocketAddress("10.60.100.191", 10020), channelConfig, kcpIdleExampleClient); 43 | kcpClient.connect(new InetSocketAddress("127.0.0.1", 10020), channelConfig, kcpIdleExampleClient); 44 | } 45 | 46 | } 47 | int i =0; 48 | 49 | @Override 50 | public void onConnected(Ukcp ukcp) { 51 | ByteBuf byteBuf = UnpooledByteBufAllocator.DEFAULT.buffer(124); 52 | byteBuf.writeInt(i++); 53 | byte[] bytes = new byte[120]; 54 | byteBuf.writeBytes(bytes); 55 | ukcp.write(byteBuf); 56 | byteBuf.release(); 57 | } 58 | //int j =0; 59 | 60 | @Override 61 | public void handleReceive(ByteBuf byteBuf, Ukcp ukcp) { 62 | //ukcp.write(byteBuf); 63 | //int id = byteBuf.getInt(0); 64 | ////if(j-id%10!=0){ 65 | //// System.out.println("id"+id +" j" +j); 66 | ////} 67 | // 68 | //j++; 69 | //if(j%100000==0){ 70 | // System.out.println(Snmp.snmp.toString()); 71 | // System.out.println("收到了 返回回去"+j); 72 | //} 73 | } 74 | 75 | @Override 76 | public void handleException(Throwable ex, Ukcp kcp) { 77 | ex.printStackTrace(); 78 | } 79 | 80 | @Override 81 | public void handleClose(Ukcp kcp) { 82 | System.out.println("连接断开了"); 83 | } 84 | 85 | 86 | } -------------------------------------------------------------------------------- /kcp-example/src/main/java/test/KcpIdleExampleServer.java: -------------------------------------------------------------------------------- 1 | package test; 2 | 3 | import com.backblaze.erasure.fec.Snmp; 4 | import io.netty.buffer.ByteBuf; 5 | import kcp.ChannelConfig; 6 | import kcp.KcpListener; 7 | import kcp.KcpServer; 8 | import kcp.Ukcp; 9 | 10 | import java.util.concurrent.atomic.AtomicInteger; 11 | 12 | /** 13 | * 测试大量连接不通讯的例子 14 | * Created by JinMiao 15 | * 2019-07-10. 16 | */ 17 | public class KcpIdleExampleServer implements KcpListener { 18 | 19 | public static void main(String[] args) { 20 | 21 | KcpIdleExampleServer kcpIdleExampleServer = new KcpIdleExampleServer(); 22 | ChannelConfig channelConfig = new ChannelConfig(); 23 | channelConfig.nodelay(true,40,2,true); 24 | channelConfig.setSndwnd(1024); 25 | channelConfig.setRcvwnd(1024); 26 | channelConfig.setMtu(1400); 27 | //channelConfig.setFecDataShardCount(10); 28 | //channelConfig.setFecParityShardCount(3); 29 | channelConfig.setAckNoDelay(false); 30 | channelConfig.setCrc32Check(true); 31 | //channelConfig.setTimeoutMillis(10000); 32 | KcpServer kcpServer = new KcpServer(); 33 | kcpServer.init(kcpIdleExampleServer, channelConfig, 10020); 34 | } 35 | 36 | private AtomicInteger atomicInteger = new AtomicInteger(); 37 | 38 | private AtomicInteger recieveAtomicInteger = new AtomicInteger(); 39 | 40 | 41 | 42 | @Override 43 | public void onConnected(Ukcp ukcp) { 44 | int id = atomicInteger.incrementAndGet(); 45 | ukcp.user().setCache(id); 46 | 47 | System.out.println("有连接进来,当前连接" + id); 48 | } 49 | 50 | int i = 0; 51 | 52 | long start = System.currentTimeMillis(); 53 | 54 | @Override 55 | public void handleReceive(ByteBuf buf, Ukcp kcp) { 56 | System.out.println("收到消息 "+recieveAtomicInteger.incrementAndGet()); 57 | i++; 58 | long now = System.currentTimeMillis(); 59 | if (now - start > 1000) { 60 | System.out.println("收到消息 time: " + (now - start) + " message :" + i); 61 | start = now; 62 | i = 0; 63 | } 64 | //kcp.write(buf); 65 | } 66 | 67 | @Override 68 | public void handleException(Throwable ex, Ukcp kcp) { 69 | ex.printStackTrace(); 70 | } 71 | 72 | @Override 73 | public void handleClose(Ukcp kcp) { 74 | System.out.println(Snmp.snmp.toString()); 75 | Snmp.snmp = new Snmp(); 76 | System.out.println("连接断开了,当前连接" + atomicInteger.decrementAndGet()); 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /kcp-example/src/main/java/test/KcpMultiplePingPongExampleClient.java: -------------------------------------------------------------------------------- 1 | package test; 2 | 3 | import io.netty.buffer.ByteBuf; 4 | import io.netty.buffer.UnpooledByteBufAllocator; 5 | import kcp.ChannelConfig; 6 | import kcp.KcpClient; 7 | import kcp.KcpListener; 8 | import kcp.Ukcp; 9 | 10 | import java.net.InetSocketAddress; 11 | import java.util.Timer; 12 | import java.util.TimerTask; 13 | 14 | /** 15 | * 测试多连接吞吐量 16 | * Created by JinMiao 17 | * 2019-06-27. 18 | */ 19 | public class KcpMultiplePingPongExampleClient implements KcpListener { 20 | 21 | public static void main(String[] args) { 22 | ChannelConfig channelConfig = new ChannelConfig(); 23 | channelConfig.nodelay(true,40,0,true); 24 | channelConfig.setSndwnd(256); 25 | channelConfig.setRcvwnd(256); 26 | channelConfig.setMtu(400); 27 | //channelConfig.setFecDataShardCount(10); 28 | //channelConfig.setFecParityShardCount(3); 29 | //channelConfig.setAckNoDelay(true); 30 | 31 | //channelConfig.setCrc32Check(true); 32 | //channelConfig.setTimeoutMillis(10000); 33 | 34 | KcpClient kcpClient = new KcpClient(); 35 | kcpClient.init(channelConfig); 36 | KcpMultiplePingPongExampleClient kcpMultiplePingPongExampleClient = new KcpMultiplePingPongExampleClient(); 37 | 38 | int clientNumber = 1000; 39 | for (int i = 0; i < clientNumber; i++) { 40 | channelConfig.setConv(i); 41 | kcpClient.connect(new InetSocketAddress("127.0.0.1", 10011), channelConfig, kcpMultiplePingPongExampleClient); 42 | } 43 | } 44 | 45 | Timer timer = new Timer(); 46 | 47 | @Override 48 | public void onConnected(Ukcp ukcp) { 49 | System.out.println(ukcp.getConv()); 50 | timer.scheduleAtFixedRate(new TimerTask() { 51 | @Override 52 | public void run() { 53 | ByteBuf byteBuf = UnpooledByteBufAllocator.DEFAULT.buffer(1004); 54 | byteBuf.writeInt(1); 55 | byte[] bytes = new byte[1000]; 56 | byteBuf.writeBytes(bytes); 57 | ukcp.write(byteBuf); 58 | byteBuf.release(); 59 | } 60 | },100,100); 61 | } 62 | 63 | @Override 64 | public void handleReceive(ByteBuf byteBuf, Ukcp ukcp) { 65 | //System.out.println("收到消息"); 66 | //ukcp.writeMessage(byteBuf); 67 | //int id = byteBuf.getInt(0); 68 | //if(j-id%10!=0){ 69 | // System.out.println("id"+id +" j" +j); 70 | //} 71 | } 72 | 73 | @Override 74 | public void handleException(Throwable ex, Ukcp kcp) { 75 | ex.printStackTrace(); 76 | } 77 | 78 | @Override 79 | public void handleClose(Ukcp kcp) { 80 | System.out.println("连接断开了"+kcp.getConv()); 81 | } 82 | 83 | 84 | } 85 | -------------------------------------------------------------------------------- /kcp-example/src/main/java/test/KcpMultiplePingPongExampleServer.java: -------------------------------------------------------------------------------- 1 | package test; 2 | 3 | import com.backblaze.erasure.fec.Snmp; 4 | import io.netty.buffer.ByteBuf; 5 | import kcp.ChannelConfig; 6 | import kcp.KcpListener; 7 | import kcp.KcpServer; 8 | import kcp.Ukcp; 9 | 10 | /** 11 | * 测试多连接吞吐量 12 | * Created by JinMiao 13 | * 2019-06-27. 14 | */ 15 | public class KcpMultiplePingPongExampleServer implements KcpListener { 16 | 17 | public static void main(String[] args) { 18 | 19 | KcpMultiplePingPongExampleServer kcpMultiplePingPongExampleServer = new KcpMultiplePingPongExampleServer(); 20 | ChannelConfig channelConfig = new ChannelConfig(); 21 | channelConfig.nodelay(true,40,2,true); 22 | channelConfig.setSndwnd(256); 23 | channelConfig.setRcvwnd(256); 24 | channelConfig.setMtu(400); 25 | //channelConfig.setFecDataShardCount(10); 26 | //channelConfig.setFecParityShardCount(3); 27 | //channelConfig.setAckNoDelay(true); 28 | channelConfig.setUseConvChannel(true); 29 | //channelConfig.setCrc32Check(true); 30 | channelConfig.setTimeoutMillis(10000); 31 | KcpServer kcpServer = new KcpServer(); 32 | kcpServer.init(kcpMultiplePingPongExampleServer, channelConfig, 10011); 33 | } 34 | 35 | 36 | @Override 37 | public void onConnected(Ukcp ukcp) { 38 | System.out.println("有连接进来" + ukcp.user().getRemoteAddress() +" conv: "+ ukcp.getConv()); 39 | } 40 | 41 | //int i = 0; 42 | // 43 | //long start = System.currentTimeMillis(); 44 | 45 | @Override 46 | public void handleReceive(ByteBuf buf, Ukcp kcp) { 47 | //i++; 48 | //long now = System.currentTimeMillis(); 49 | //if(now-start>1000){ 50 | // System.out.println("收到消息 time: "+(now-start) +" message :" +i); 51 | // start = now; 52 | // i=0; 53 | //} 54 | kcp.write(buf); 55 | } 56 | 57 | @Override 58 | public void handleException(Throwable ex, Ukcp kcp) { 59 | ex.printStackTrace(); 60 | } 61 | 62 | @Override 63 | public void handleClose(Ukcp kcp) { 64 | System.out.println(Snmp.snmp.toString()); 65 | Snmp.snmp= new Snmp(); 66 | System.out.println("连接断开了"+kcp.getConv()+" "+System.currentTimeMillis()); 67 | } 68 | } -------------------------------------------------------------------------------- /kcp-example/src/main/java/test/KcpPingPongExampleClient.java: -------------------------------------------------------------------------------- 1 | package test; 2 | 3 | import com.backblaze.erasure.FecAdapt; 4 | import com.backblaze.erasure.fec.Snmp; 5 | import io.netty.buffer.ByteBuf; 6 | import io.netty.buffer.UnpooledByteBufAllocator; 7 | import io.netty.channel.DefaultEventLoop; 8 | import io.netty.channel.EventLoop; 9 | import kcp.*; 10 | import threadPool.disruptor.DisruptorExecutorPool; 11 | import threadPool.disruptor.DisruptorSingleExecutor; 12 | 13 | import java.net.InetSocketAddress; 14 | 15 | /** 16 | * 测试单连接吞吐量 17 | * Created by JinMiao 18 | * 2019-06-27. 19 | */ 20 | public class KcpPingPongExampleClient implements KcpListener { 21 | 22 | static DefaultEventLoop logicThread = new DefaultEventLoop(); 23 | public static void main(String[] args) { 24 | ChannelConfig channelConfig = new ChannelConfig(); 25 | channelConfig.nodelay(true,40,2,true); 26 | channelConfig.setSndwnd(1024); 27 | channelConfig.setRcvwnd(1024); 28 | channelConfig.setMtu(1400); 29 | //channelConfig.setiMessageExecutorPool(new DisruptorExecutorPool(Runtime.getRuntime().availableProcessors())); 30 | //channelConfig.setFecAdapt(new FecAdapt(10,3)); 31 | channelConfig.setAckNoDelay(false); 32 | //channelConfig.setCrc32Check(true); 33 | //channelConfig.setTimeoutMillis(10000); 34 | 35 | KcpClient kcpClient = new KcpClient(); 36 | kcpClient.init(channelConfig); 37 | 38 | KcpPingPongExampleClient kcpClientRttExample = new KcpPingPongExampleClient(); 39 | kcpClient.connect(new InetSocketAddress("127.0.0.1", 10001), channelConfig, kcpClientRttExample); 40 | } 41 | int i =0; 42 | 43 | @Override 44 | public void onConnected(Ukcp ukcp) { 45 | for (int i = 0; i < 100; i++) { 46 | ByteBuf byteBuf = UnpooledByteBufAllocator.DEFAULT.buffer(1024); 47 | byteBuf.writeInt(i++); 48 | byte[] bytes = new byte[1020]; 49 | byteBuf.writeBytes(bytes); 50 | ukcp.write(byteBuf); 51 | byteBuf.release(); 52 | } 53 | } 54 | int j =0; 55 | 56 | @Override 57 | public void handleReceive(ByteBuf byteBuf, Ukcp ukcp) { 58 | ByteBuf newBuf = byteBuf.retainedDuplicate(); 59 | logicThread.execute(() -> { 60 | try { 61 | ukcp.write(newBuf); 62 | newBuf.release(); 63 | j++; 64 | if(j%100000==0){ 65 | System.out.println(Snmp.snmp.toString()); 66 | System.out.println("收到了 返回回去"+j); 67 | } 68 | }catch (Exception e){ 69 | e.printStackTrace(); 70 | } 71 | 72 | }); 73 | 74 | } 75 | 76 | @Override 77 | public void handleException(Throwable ex, Ukcp kcp) { 78 | ex.printStackTrace(); 79 | } 80 | 81 | @Override 82 | public void handleClose(Ukcp kcp) { 83 | System.out.println("连接断开了"); 84 | } 85 | 86 | 87 | } 88 | -------------------------------------------------------------------------------- /kcp-example/src/main/java/test/KcpPingPongExampleServer.java: -------------------------------------------------------------------------------- 1 | package test; 2 | 3 | import com.backblaze.erasure.FecAdapt; 4 | import com.backblaze.erasure.fec.Snmp; 5 | import io.netty.buffer.ByteBuf; 6 | import io.netty.channel.DefaultEventLoop; 7 | import io.netty.channel.EventLoop; 8 | import kcp.ChannelConfig; 9 | import kcp.KcpListener; 10 | import kcp.KcpServer; 11 | import kcp.Ukcp; 12 | import threadPool.disruptor.DisruptorExecutorPool; 13 | import threadPool.disruptor.DisruptorSingleExecutor; 14 | import threadPool.netty.NettyMessageExecutor; 15 | 16 | /** 17 | * 测试单连接吞吐量 18 | * mbp 2.3 GHz Intel Core i9 16GRam 单连接 带fec 5W/s qps 单连接 不带fec 8W/s qps 19 | * Created by JinMiao 20 | * 2019-06-27. 21 | */ 22 | public class KcpPingPongExampleServer implements KcpListener { 23 | static DefaultEventLoop logicThread = new DefaultEventLoop(); 24 | public static void main(String[] args) { 25 | 26 | KcpPingPongExampleServer kcpRttExampleServer = new KcpPingPongExampleServer(); 27 | ChannelConfig channelConfig = new ChannelConfig(); 28 | channelConfig.nodelay(true,40,2,true); 29 | channelConfig.setSndwnd(1024); 30 | channelConfig.setRcvwnd(1024); 31 | channelConfig.setMtu(1400); 32 | //channelConfig.setiMessageExecutorPool(new DisruptorExecutorPool(Runtime.getRuntime().availableProcessors())); 33 | //channelConfig.setFecAdapt(new FecAdapt(10,3)); 34 | channelConfig.setAckNoDelay(false); 35 | //channelConfig.setCrc32Check(true); 36 | channelConfig.setTimeoutMillis(10000); 37 | KcpServer kcpServer = new KcpServer(); 38 | kcpServer.init(kcpRttExampleServer, channelConfig, 10001); 39 | } 40 | 41 | 42 | @Override 43 | public void onConnected(Ukcp ukcp) { 44 | System.out.println("有连接进来" + Thread.currentThread().getName() + ukcp.user().getRemoteAddress()); 45 | } 46 | 47 | int i = 0; 48 | 49 | long start = System.currentTimeMillis(); 50 | 51 | @Override 52 | public void handleReceive(ByteBuf buf, Ukcp kcp) { 53 | ByteBuf newBuf = buf.retainedDuplicate(); 54 | logicThread.execute(() -> { 55 | try { 56 | i++; 57 | long now = System.currentTimeMillis(); 58 | if(now-start>1000){ 59 | System.out.println("收到消息 time: "+(now-start) +" message :" +i); 60 | start = now; 61 | i=0; 62 | } 63 | kcp.write(newBuf); 64 | newBuf.release(); 65 | }catch (Exception e){ 66 | e.printStackTrace(); 67 | } 68 | 69 | }); 70 | } 71 | 72 | @Override 73 | public void handleException(Throwable ex, Ukcp kcp) { 74 | ex.printStackTrace(); 75 | } 76 | 77 | @Override 78 | public void handleClose(Ukcp kcp) { 79 | System.out.println(Snmp.snmp.toString()); 80 | Snmp.snmp= new Snmp(); 81 | System.out.println("连接断开了"); 82 | } 83 | } -------------------------------------------------------------------------------- /kcp-example/src/main/java/test/KcpReconnectExampleClient.java: -------------------------------------------------------------------------------- 1 | package test; 2 | 3 | import com.backblaze.erasure.fec.Snmp; 4 | import io.netty.buffer.ByteBuf; 5 | import io.netty.buffer.UnpooledByteBufAllocator; 6 | import kcp.ChannelConfig; 7 | import kcp.KcpClient; 8 | import kcp.KcpListener; 9 | import kcp.Ukcp; 10 | 11 | import java.net.InetSocketAddress; 12 | import java.util.Timer; 13 | import java.util.TimerTask; 14 | 15 | /** 16 | * 重连测试客户端 17 | * Created by JinMiao 18 | * 2019-06-27. 19 | */ 20 | public class KcpReconnectExampleClient implements KcpListener { 21 | 22 | public static void main(String[] args) { 23 | ChannelConfig channelConfig = new ChannelConfig(); 24 | channelConfig.nodelay(true,40,2,true); 25 | channelConfig.setSndwnd(1024); 26 | channelConfig.setRcvwnd(1024); 27 | channelConfig.setMtu(1400); 28 | //channelConfig.setFecDataShardCount(10); 29 | //channelConfig.setFecParityShardCount(3); 30 | //channelConfig.setAckNoDelay(true); 31 | //channelConfig.setCrc32Check(true); 32 | //channelConfig.setTimeoutMillis(10000); 33 | channelConfig.setConv(55); 34 | channelConfig.setUseConvChannel(true); 35 | 36 | KcpClient kcpClient = new KcpClient(); 37 | kcpClient.init(channelConfig); 38 | 39 | KcpReconnectExampleClient kcpClientRttExample = new KcpReconnectExampleClient(); 40 | Ukcp ukcp = kcpClient.connect(new InetSocketAddress("127.0.0.1", 10021), channelConfig, kcpClientRttExample); 41 | Timer timer = new Timer(); 42 | timer.scheduleAtFixedRate(new TimerTask() { 43 | @Override 44 | public void run() { 45 | kcpClient.reconnect(ukcp); 46 | } 47 | }, 1000, 1000); 48 | } 49 | 50 | @Override 51 | public void onConnected(Ukcp ukcp) { 52 | for (int i = 0; i < 100; i++) { 53 | ByteBuf byteBuf = UnpooledByteBufAllocator.DEFAULT.buffer(1024); 54 | byteBuf.writeInt(i++); 55 | byte[] bytes = new byte[1020]; 56 | byteBuf.writeBytes(bytes); 57 | ukcp.write(byteBuf); 58 | byteBuf.release(); 59 | } 60 | } 61 | int j =0; 62 | 63 | @Override 64 | public void handleReceive(ByteBuf byteBuf, Ukcp ukcp) { 65 | ukcp.write(byteBuf); 66 | int id = byteBuf.getInt(0); 67 | //if(j-id%10!=0){ 68 | // System.out.println("id"+id +" j" +j); 69 | //} 70 | 71 | j++; 72 | if(j%100000==0){ 73 | System.out.println(Snmp.snmp.toString()); 74 | System.out.println("收到了 返回回去"+j); 75 | } 76 | } 77 | 78 | @Override 79 | public void handleException(Throwable ex, Ukcp kcp) { 80 | ex.printStackTrace(); 81 | } 82 | 83 | @Override 84 | public void handleClose(Ukcp kcp) { 85 | System.out.println("连接断开了"); 86 | } 87 | 88 | 89 | } 90 | -------------------------------------------------------------------------------- /kcp-example/src/main/java/test/KcpReconnectExampleServer.java: -------------------------------------------------------------------------------- 1 | package test; 2 | 3 | import com.backblaze.erasure.fec.Snmp; 4 | import io.netty.buffer.ByteBuf; 5 | import kcp.ChannelConfig; 6 | import kcp.KcpListener; 7 | import kcp.KcpServer; 8 | import kcp.Ukcp; 9 | 10 | /** 11 | * 重连测试服务器 12 | * Created by JinMiao 13 | * 2019-06-27. 14 | */ 15 | public class KcpReconnectExampleServer implements KcpListener { 16 | 17 | public static void main(String[] args) { 18 | 19 | KcpReconnectExampleServer kcpRttExampleServer = new KcpReconnectExampleServer(); 20 | ChannelConfig channelConfig = new ChannelConfig(); 21 | channelConfig.nodelay(true,40,2,true); 22 | channelConfig.setSndwnd(1024); 23 | channelConfig.setRcvwnd(1024); 24 | channelConfig.setMtu(1400); 25 | //channelConfig.setFecDataShardCount(10); 26 | //channelConfig.setFecParityShardCount(3); 27 | //channelConfig.setAckNoDelay(true); 28 | //channelConfig.setCrc32Check(true); 29 | channelConfig.setUseConvChannel(true); 30 | channelConfig.setTimeoutMillis(10000); 31 | KcpServer kcpServer = new KcpServer(); 32 | kcpServer.init(kcpRttExampleServer, channelConfig, 10021); 33 | } 34 | 35 | 36 | @Override 37 | public void onConnected(Ukcp ukcp) { 38 | System.out.println("有连接进来" + Thread.currentThread().getName() + ukcp.user().getRemoteAddress()); 39 | } 40 | 41 | int i = 0; 42 | 43 | long start = System.currentTimeMillis(); 44 | 45 | @Override 46 | public void handleReceive(ByteBuf buf, Ukcp kcp) { 47 | i++; 48 | long now = System.currentTimeMillis(); 49 | if(now-start>1000){ 50 | System.out.println("收到消息 time: "+(now-start) +" message :" +i); 51 | start = now; 52 | i=0; 53 | } 54 | kcp.write(buf); 55 | } 56 | 57 | @Override 58 | public void handleException(Throwable ex, Ukcp kcp) { 59 | ex.printStackTrace(); 60 | } 61 | 62 | @Override 63 | public void handleClose(Ukcp kcp) { 64 | System.out.println(Snmp.snmp.toString()); 65 | Snmp.snmp= new Snmp(); 66 | System.out.println("连接断开了"); 67 | } 68 | } -------------------------------------------------------------------------------- /kcp-example/src/main/java/test/KcpRttExampleServer.java: -------------------------------------------------------------------------------- 1 | package test; 2 | 3 | import com.backblaze.erasure.FecAdapt; 4 | import com.backblaze.erasure.fec.Snmp; 5 | import io.netty.buffer.ByteBuf; 6 | import kcp.ChannelConfig; 7 | import kcp.KcpListener; 8 | import kcp.KcpServer; 9 | import kcp.Ukcp; 10 | 11 | /** 12 | * 测试延迟的例子 13 | * Created by JinMiao 14 | * 2018/11/2. 15 | */ 16 | public class KcpRttExampleServer implements KcpListener { 17 | 18 | public static void main(String[] args) { 19 | 20 | KcpRttExampleServer kcpRttExampleServer = new KcpRttExampleServer(); 21 | 22 | ChannelConfig channelConfig = new ChannelConfig(); 23 | channelConfig.nodelay(true,40,2,true); 24 | channelConfig.setSndwnd(512); 25 | channelConfig.setRcvwnd(512); 26 | channelConfig.setMtu(512); 27 | channelConfig.setFecAdapt(new FecAdapt(3,1)); 28 | channelConfig.setAckNoDelay(true); 29 | channelConfig.setTimeoutMillis(10000); 30 | channelConfig.setUseConvChannel(true); 31 | channelConfig.setCrc32Check(true); 32 | KcpServer kcpServer = new KcpServer(); 33 | kcpServer.init(kcpRttExampleServer,channelConfig,20003); 34 | } 35 | 36 | 37 | @Override 38 | public void onConnected(Ukcp ukcp) { 39 | System.out.println("有连接进来"+Thread.currentThread().getName()+ukcp.user().getRemoteAddress()); 40 | } 41 | 42 | @Override 43 | public void handleReceive(ByteBuf buf, Ukcp kcp) { 44 | short curCount = buf.getShort(buf.readerIndex()); 45 | System.out.println(Thread.currentThread().getName()+" 收到消息 "+curCount); 46 | kcp.write(buf); 47 | if (curCount == -1) { 48 | kcp.close(); 49 | } 50 | } 51 | 52 | @Override 53 | public void handleException(Throwable ex, Ukcp kcp) { 54 | ex.printStackTrace(); 55 | } 56 | 57 | @Override 58 | public void handleClose(Ukcp kcp) { 59 | System.out.println(Snmp.snmp.toString()); 60 | Snmp.snmp = new Snmp(); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /kcp-example/src/main/java/test/SpeedExampleClient.java: -------------------------------------------------------------------------------- 1 | package test; 2 | 3 | import com.backblaze.erasure.fec.Snmp; 4 | import io.netty.buffer.ByteBuf; 5 | import io.netty.buffer.ByteBufAllocator; 6 | import kcp.ChannelConfig; 7 | import kcp.KcpClient; 8 | import kcp.KcpListener; 9 | import kcp.Ukcp; 10 | import threadPool.disruptor.DisruptorExecutorPool; 11 | 12 | import java.net.InetSocketAddress; 13 | 14 | /** 15 | * Created by JinMiao 16 | * 2020/12/23. 17 | */ 18 | public class SpeedExampleClient implements KcpListener { 19 | 20 | 21 | public SpeedExampleClient() { 22 | } 23 | 24 | public static void main(String[] args) { 25 | ChannelConfig channelConfig = new ChannelConfig(); 26 | channelConfig.nodelay(true,30,2,true); 27 | channelConfig.setSndwnd(2048); 28 | channelConfig.setRcvwnd(2048); 29 | channelConfig.setMtu(1400); 30 | channelConfig.setAckNoDelay(true); 31 | channelConfig.setConv(55); 32 | channelConfig.setiMessageExecutorPool(new DisruptorExecutorPool(Runtime.getRuntime().availableProcessors()/2)); 33 | //channelConfig.setFecDataShardCount(10); 34 | //channelConfig.setFecParityShardCount(3); 35 | channelConfig.setCrc32Check(false); 36 | channelConfig.setWriteBufferSize(channelConfig.getMtu()*300000); 37 | KcpClient kcpClient = new KcpClient(); 38 | kcpClient.init(channelConfig); 39 | 40 | SpeedExampleClient speedExampleClient = new SpeedExampleClient(); 41 | kcpClient.connect(new InetSocketAddress("127.0.0.1",20004),channelConfig,speedExampleClient); 42 | 43 | } 44 | private static final int messageSize = 2048; 45 | private long start = System.currentTimeMillis(); 46 | 47 | @Override 48 | public void onConnected(Ukcp ukcp) { 49 | new Thread(() -> { 50 | for(;;){ 51 | long now =System.currentTimeMillis(); 52 | if(now-start>=1000){ 53 | System.out.println("耗时 :" +(now-start) +" 发送数据: " +(Snmp.snmp.OutBytes.doubleValue()/1024.0/1024.0)+"MB"+" 有效数据: "+Snmp.snmp.BytesSent.doubleValue()/1024.0/1024.0+" MB"); 54 | System.out.println(Snmp.snmp.toString()); 55 | Snmp.snmp = new Snmp(); 56 | start=now; 57 | } 58 | ByteBuf byteBuf = ByteBufAllocator.DEFAULT.buffer(messageSize); 59 | byteBuf.writeBytes(new byte[messageSize]); 60 | if(!ukcp.write(byteBuf)){ 61 | try { 62 | Thread.sleep(1); 63 | } catch (InterruptedException e) { 64 | e.printStackTrace(); 65 | } 66 | } 67 | byteBuf.release(); 68 | } 69 | }).start(); 70 | } 71 | 72 | @Override 73 | public void handleReceive(ByteBuf byteBuf, Ukcp ukcp) { 74 | } 75 | 76 | @Override 77 | public void handleException(Throwable ex, Ukcp kcp) 78 | { 79 | ex.printStackTrace(); 80 | } 81 | 82 | @Override 83 | public void handleClose(Ukcp kcp) { 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /kcp-example/src/main/java/test/SpeedExampleServer.java: -------------------------------------------------------------------------------- 1 | package test; 2 | 3 | import com.backblaze.erasure.fec.Snmp; 4 | import io.netty.buffer.ByteBuf; 5 | import kcp.ChannelConfig; 6 | import kcp.KcpListener; 7 | import kcp.KcpServer; 8 | import kcp.Ukcp; 9 | import threadPool.disruptor.DisruptorExecutorPool; 10 | 11 | /** 12 | * 测试吞吐量 13 | * Created by JinMiao 14 | * 2020/12/23. 15 | */ 16 | public class SpeedExampleServer implements KcpListener { 17 | public static void main(String[] args) { 18 | 19 | SpeedExampleServer speedExampleServer = new SpeedExampleServer(); 20 | ChannelConfig channelConfig = new ChannelConfig(); 21 | channelConfig.nodelay(true,30,2,true); 22 | channelConfig.setSndwnd(2048); 23 | channelConfig.setRcvwnd(2048); 24 | channelConfig.setMtu(1400); 25 | channelConfig.setiMessageExecutorPool(new DisruptorExecutorPool(Runtime.getRuntime().availableProcessors()/2)); 26 | //channelConfig.setFecDataShardCount(10); 27 | //channelConfig.setFecParityShardCount(3); 28 | channelConfig.setAckNoDelay(true); 29 | channelConfig.setTimeoutMillis(5000); 30 | channelConfig.setUseConvChannel(true); 31 | channelConfig.setCrc32Check(false); 32 | KcpServer kcpServer = new KcpServer(); 33 | kcpServer.init(speedExampleServer,channelConfig,20004); 34 | } 35 | 36 | long start = System.currentTimeMillis(); 37 | 38 | 39 | @Override 40 | public void onConnected(Ukcp ukcp) { 41 | System.out.println("有连接进来"+Thread.currentThread().getName()+ukcp.user().getRemoteAddress()); 42 | } 43 | 44 | long inBytes = 0; 45 | 46 | @Override 47 | public void handleReceive(ByteBuf buf, Ukcp kcp) { 48 | inBytes+=buf.readableBytes(); 49 | long now =System.currentTimeMillis(); 50 | if(now-start>=1000){ 51 | System.out.println("耗时 :" +(now-start) +" 接收数据: " +(Snmp.snmp.InBytes.doubleValue()/1024.0/1024.0)+"MB"+" 有效数据: "+inBytes/1024.0/1024.0+" MB"); 52 | System.out.println(Snmp.snmp.BytesReceived.doubleValue()/1024.0/1024.0); 53 | System.out.println(Snmp.snmp.toString()); 54 | inBytes=0; 55 | Snmp.snmp = new Snmp(); 56 | start=now; 57 | } 58 | } 59 | 60 | @Override 61 | public void handleException(Throwable ex, Ukcp kcp) { 62 | ex.printStackTrace(); 63 | } 64 | 65 | @Override 66 | public void handleClose(Ukcp kcp) { 67 | System.out.println(Snmp.snmp.toString()); 68 | Snmp.snmp = new Snmp(); 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /kcp-fec/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | java-Kcp 7 | com.github.l42111996 8 | 1.6.1 9 | 10 | 4.0.0 11 | 12 | kcp-fec 13 | 14 | 15 | io.netty 16 | netty-all 17 | ${netty.version} 18 | 19 | 20 | -------------------------------------------------------------------------------- /kcp-fec/src/main/java/com/backblaze/erasure/ByteInputOutputExpCodingLoop.java: -------------------------------------------------------------------------------- 1 | /** 2 | * One specific ordering/nesting of the coding loops. 3 | * 4 | * Copyright 2015, Backblaze, Inc. All rights reserved. 5 | */ 6 | 7 | package com.backblaze.erasure; 8 | 9 | public class ByteInputOutputExpCodingLoop extends CodingLoopBase { 10 | 11 | @Override 12 | public void codeSomeShards( 13 | byte[][] matrixRows, 14 | byte[][] inputs, int inputCount, 15 | byte[][] outputs, int outputCount, 16 | int offset, int byteCount) { 17 | 18 | for (int iByte = offset; iByte < offset + byteCount; iByte++) { 19 | { 20 | final int iInput = 0; 21 | final byte[] inputShard = inputs[iInput]; 22 | final byte inputByte = inputShard[iByte]; 23 | for (int iOutput = 0; iOutput < outputCount; iOutput++) { 24 | final byte[] outputShard = outputs[iOutput]; 25 | final byte[] matrixRow = matrixRows[iOutput]; 26 | outputShard[iByte] = Galois.multiply(matrixRow[iInput], inputByte); 27 | } 28 | } 29 | 30 | for (int iInput = 1; iInput < inputCount; iInput++) { 31 | final byte[] inputShard = inputs[iInput]; 32 | final byte inputByte = inputShard[iByte]; 33 | for (int iOutput = 0; iOutput < outputCount; iOutput++) { 34 | final byte[] outputShard = outputs[iOutput]; 35 | final byte[] matrixRow = matrixRows[iOutput]; 36 | outputShard[iByte] ^= Galois.multiply(matrixRow[iInput], inputByte); 37 | } 38 | } 39 | } 40 | } 41 | 42 | } 43 | -------------------------------------------------------------------------------- /kcp-fec/src/main/java/com/backblaze/erasure/ByteInputOutputTableCodingLoop.java: -------------------------------------------------------------------------------- 1 | /** 2 | * One specific ordering/nesting of the coding loops. 3 | * 4 | * Copyright 2015, Backblaze, Inc. All rights reserved. 5 | */ 6 | 7 | package com.backblaze.erasure; 8 | 9 | public class ByteInputOutputTableCodingLoop extends CodingLoopBase { 10 | 11 | @Override 12 | public void codeSomeShards( 13 | byte[][] matrixRows, 14 | byte[][] inputs, int inputCount, 15 | byte[][] outputs, int outputCount, 16 | int offset, int byteCount) { 17 | 18 | final byte[][] table = Galois.MULTIPLICATION_TABLE; 19 | 20 | for (int iByte = offset; iByte < offset + byteCount; iByte++) { 21 | { 22 | final int iInput = 0; 23 | final byte[] inputShard = inputs[iInput]; 24 | final byte inputByte = inputShard[iByte]; 25 | for (int iOutput = 0; iOutput < outputCount; iOutput++) { 26 | final byte[] outputShard = outputs[iOutput]; 27 | final byte[] matrixRow = matrixRows[iOutput]; 28 | final byte[] multTableRow = table[matrixRow[iInput] & 0xFF]; 29 | outputShard[iByte] = multTableRow[inputByte & 0xFF]; 30 | } 31 | } 32 | for (int iInput = 1; iInput < inputCount; iInput++) { 33 | final byte[] inputShard = inputs[iInput]; 34 | final byte inputByte = inputShard[iByte]; 35 | for (int iOutput = 0; iOutput < outputCount; iOutput++) { 36 | final byte[] outputShard = outputs[iOutput]; 37 | final byte[] matrixRow = matrixRows[iOutput]; 38 | final byte[] multTableRow = table[matrixRow[iInput] & 0xFF]; 39 | outputShard[iByte] ^= multTableRow[inputByte & 0xFF]; 40 | } 41 | } 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /kcp-fec/src/main/java/com/backblaze/erasure/ByteOutputInputExpCodingLoop.java: -------------------------------------------------------------------------------- 1 | /** 2 | * One specific ordering/nesting of the coding loops. 3 | * 4 | * Copyright 2015, Backblaze, Inc. All rights reserved. 5 | */ 6 | 7 | package com.backblaze.erasure; 8 | 9 | public class ByteOutputInputExpCodingLoop extends CodingLoopBase { 10 | 11 | @Override 12 | public void codeSomeShards( 13 | byte[][] matrixRows, 14 | byte[][] inputs, int inputCount, 15 | byte[][] outputs, int outputCount, 16 | int offset, int byteCount) { 17 | 18 | for (int iByte = offset; iByte < offset + byteCount; iByte++) { 19 | for (int iOutput = 0; iOutput < outputCount; iOutput++) { 20 | byte [] matrixRow = matrixRows[iOutput]; 21 | int value = 0; 22 | for (int iInput = 0; iInput < inputCount; iInput++) { 23 | value ^= Galois.multiply(matrixRow[iInput], inputs[iInput][iByte]); 24 | } 25 | outputs[iOutput][iByte] = (byte) value; 26 | } 27 | } 28 | } 29 | 30 | } 31 | -------------------------------------------------------------------------------- /kcp-fec/src/main/java/com/backblaze/erasure/ByteOutputInputTableCodingLoop.java: -------------------------------------------------------------------------------- 1 | /** 2 | * One specific ordering/nesting of the coding loops. 3 | * 4 | * Copyright 2015, Backblaze, Inc. All rights reserved. 5 | */ 6 | 7 | package com.backblaze.erasure; 8 | 9 | public class ByteOutputInputTableCodingLoop extends CodingLoopBase { 10 | 11 | @Override 12 | public void codeSomeShards( 13 | byte[][] matrixRows, 14 | byte[][] inputs, int inputCount, 15 | byte[][] outputs, int outputCount, 16 | int offset, int byteCount) { 17 | 18 | byte [] [] table = Galois.MULTIPLICATION_TABLE; 19 | for (int iByte = offset; iByte < offset + byteCount; iByte++) { 20 | for (int iOutput = 0; iOutput < outputCount; iOutput++) { 21 | byte [] matrixRow = matrixRows[iOutput]; 22 | int value = 0; 23 | for (int iInput = 0; iInput < inputCount; iInput++) { 24 | value ^= table[matrixRow[iInput] & 0xFF][inputs[iInput][iByte] & 0xFF]; 25 | } 26 | outputs[iOutput][iByte] = (byte) value; 27 | } 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /kcp-fec/src/main/java/com/backblaze/erasure/CodingLoopBase.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Common implementations for coding loops. 3 | * 4 | * Copyright 2015, Backblaze, Inc. All rights reserved. 5 | */ 6 | 7 | package com.backblaze.erasure; 8 | 9 | /** 10 | * Common implementations for coding loops. 11 | * 12 | * Many of the coding loops do not have custom checkSomeShards() methods. 13 | * The benchmark doesn't measure that method. 14 | */ 15 | public abstract class CodingLoopBase implements CodingLoop { 16 | 17 | @Override 18 | public boolean checkSomeShards( 19 | byte[][] matrixRows, 20 | byte[][] inputs, int inputCount, 21 | byte[][] toCheck, int checkCount, 22 | int offset, int byteCount, 23 | byte[] tempBuffer) { 24 | 25 | // This is the loop structure for ByteOutputInput, which does not 26 | // require temporary buffers for checking. 27 | byte [] [] table = Galois.MULTIPLICATION_TABLE; 28 | for (int iByte = offset; iByte < offset + byteCount; iByte++) { 29 | for (int iOutput = 0; iOutput < checkCount; iOutput++) { 30 | byte [] matrixRow = matrixRows[iOutput]; 31 | int value = 0; 32 | for (int iInput = 0; iInput < inputCount; iInput++) { 33 | value ^= table[matrixRow[iInput] & 0xFF][inputs[iInput][iByte] & 0xFF]; 34 | } 35 | if (toCheck[iOutput][iByte] != (byte) value) { 36 | return false; 37 | } 38 | } 39 | } 40 | return true; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /kcp-fec/src/main/java/com/backblaze/erasure/FecAdapt.java: -------------------------------------------------------------------------------- 1 | package com.backblaze.erasure; 2 | 3 | import com.backblaze.erasure.fecNative.FecDecode; 4 | import com.backblaze.erasure.fecNative.FecEncode; 5 | import com.backblaze.erasure.fecNative.ReedSolomonNative; 6 | import io.netty.util.internal.logging.InternalLogger; 7 | import io.netty.util.internal.logging.InternalLoggerFactory; 8 | 9 | /** 10 | * Created by JinMiao 11 | * 2021/2/2. 12 | */ 13 | public class FecAdapt { 14 | 15 | private static final InternalLogger log = InternalLoggerFactory.getInstance(FecAdapt.class); 16 | private ReedSolomonNative reedSolomonNative; 17 | private ReedSolomon reedSolomon; 18 | 19 | public FecAdapt(int dataShards, int parityShards){ 20 | if(ReedSolomonNative.isNativeSupport()){ 21 | reedSolomonNative = new ReedSolomonNative(dataShards,parityShards); 22 | log.info("fec use C native reedSolomon dataShards {} parityShards {}",dataShards,parityShards); 23 | }else{ 24 | reedSolomon = ReedSolomon.create(dataShards,parityShards); 25 | log.info("fec use jvm reedSolomon dataShards {} parityShards {}",dataShards,parityShards); 26 | } 27 | 28 | } 29 | 30 | public IFecEncode fecEncode(int headerOffset,int mtu){ 31 | IFecEncode iFecEncode; 32 | if(reedSolomonNative!=null){ 33 | iFecEncode = new FecEncode(headerOffset,this.reedSolomonNative,mtu); 34 | }else{ 35 | iFecEncode = new com.backblaze.erasure.fec.FecEncode(headerOffset,this.reedSolomon,mtu); 36 | } 37 | return iFecEncode; 38 | } 39 | 40 | 41 | public IFecDecode fecDecode(int mtu){ 42 | IFecDecode iFecDecode; 43 | if(reedSolomonNative!=null){ 44 | iFecDecode = new FecDecode(3*reedSolomonNative.getTotalShardCount(),this.reedSolomonNative,mtu); 45 | }else{ 46 | iFecDecode = new com.backblaze.erasure.fec.FecDecode(3*this.reedSolomon.getTotalShardCount(),this.reedSolomon,mtu); 47 | } 48 | return iFecDecode; 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /kcp-fec/src/main/java/com/backblaze/erasure/IFecDecode.java: -------------------------------------------------------------------------------- 1 | package com.backblaze.erasure; 2 | 3 | import com.backblaze.erasure.fec.FecPacket; 4 | import io.netty.buffer.ByteBuf; 5 | 6 | import java.util.List; 7 | 8 | /** 9 | * Created by JinMiao 10 | * 2021/2/2. 11 | */ 12 | public interface IFecDecode { 13 | 14 | List decode(final FecPacket pkt); 15 | void release(); 16 | } 17 | -------------------------------------------------------------------------------- /kcp-fec/src/main/java/com/backblaze/erasure/IFecEncode.java: -------------------------------------------------------------------------------- 1 | package com.backblaze.erasure; 2 | 3 | import io.netty.buffer.ByteBuf; 4 | 5 | /** 6 | * Created by JinMiao 7 | * 2021/2/2. 8 | */ 9 | public interface IFecEncode { 10 | ByteBuf[] encode(final ByteBuf byteBuf); 11 | void release(); 12 | } 13 | -------------------------------------------------------------------------------- /kcp-fec/src/main/java/com/backblaze/erasure/InputByteOutputExpCodingLoop.java: -------------------------------------------------------------------------------- 1 | /** 2 | * One specific ordering/nesting of the coding loops. 3 | * 4 | * Copyright 2015, Backblaze, Inc. All rights reserved. 5 | */ 6 | 7 | package com.backblaze.erasure; 8 | 9 | public class InputByteOutputExpCodingLoop extends CodingLoopBase { 10 | 11 | @Override 12 | public void codeSomeShards( 13 | byte[][] matrixRows, 14 | byte[][] inputs, int inputCount, 15 | byte[][] outputs, int outputCount, 16 | int offset, int byteCount) { 17 | 18 | { 19 | final int iInput = 0; 20 | final byte[] inputShard = inputs[iInput]; 21 | for (int iByte = offset; iByte < offset + byteCount; iByte++) { 22 | final byte inputByte = inputShard[iByte]; 23 | for (int iOutput = 0; iOutput < outputCount; iOutput++) { 24 | final byte[] outputShard = outputs[iOutput]; 25 | final byte[] matrixRow = matrixRows[iOutput]; 26 | outputShard[iByte] = Galois.multiply(matrixRow[iInput], inputByte); 27 | } 28 | } 29 | } 30 | 31 | for (int iInput = 1; iInput < inputCount; iInput++) { 32 | final byte[] inputShard = inputs[iInput]; 33 | for (int iByte = offset; iByte < offset + byteCount; iByte++) { 34 | final byte inputByte = inputShard[iByte]; 35 | for (int iOutput = 0; iOutput < outputCount; iOutput++) { 36 | final byte[] outputShard = outputs[iOutput]; 37 | final byte[] matrixRow = matrixRows[iOutput]; 38 | outputShard[iByte] ^= Galois.multiply(matrixRow[iInput], inputByte); 39 | } 40 | } 41 | } 42 | } 43 | 44 | } 45 | -------------------------------------------------------------------------------- /kcp-fec/src/main/java/com/backblaze/erasure/InputByteOutputTableCodingLoop.java: -------------------------------------------------------------------------------- 1 | /** 2 | * One specific ordering/nesting of the coding loops. 3 | * 4 | * Copyright 2015, Backblaze, Inc. All rights reserved. 5 | */ 6 | 7 | package com.backblaze.erasure; 8 | 9 | public class InputByteOutputTableCodingLoop extends CodingLoopBase { 10 | 11 | @Override 12 | public void codeSomeShards( 13 | byte[][] matrixRows, 14 | byte[][] inputs, int inputCount, 15 | byte[][] outputs, int outputCount, 16 | int offset, int byteCount) { 17 | 18 | final byte [] [] table = Galois.MULTIPLICATION_TABLE; 19 | 20 | { 21 | final int iInput = 0; 22 | final byte[] inputShard = inputs[iInput]; 23 | for (int iByte = offset; iByte < offset + byteCount; iByte++) { 24 | final byte inputByte = inputShard[iByte]; 25 | final byte [] multTableRow = table[inputByte & 0xFF]; 26 | for (int iOutput = 0; iOutput < outputCount; iOutput++) { 27 | final byte[] outputShard = outputs[iOutput]; 28 | final byte[] matrixRow = matrixRows[iOutput]; 29 | outputShard[iByte] = multTableRow[matrixRow[iInput] & 0xFF]; 30 | } 31 | } 32 | } 33 | 34 | for (int iInput = 1; iInput < inputCount; iInput++) { 35 | final byte[] inputShard = inputs[iInput]; 36 | for (int iByte = offset; iByte < offset + byteCount; iByte++) { 37 | final byte inputByte = inputShard[iByte]; 38 | final byte [] multTableRow = table[inputByte & 0xFF]; 39 | for (int iOutput = 0; iOutput < outputCount; iOutput++) { 40 | final byte[] outputShard = outputs[iOutput]; 41 | final byte[] matrixRow = matrixRows[iOutput]; 42 | outputShard[iByte] ^= multTableRow[matrixRow[iInput] & 0xFF]; 43 | } 44 | } 45 | } 46 | } 47 | 48 | } 49 | -------------------------------------------------------------------------------- /kcp-fec/src/main/java/com/backblaze/erasure/InputOutputByteExpCodingLoop.java: -------------------------------------------------------------------------------- 1 | /** 2 | * One specific ordering/nesting of the coding loops. 3 | * 4 | * Copyright 2015, Backblaze, Inc. All rights reserved. 5 | */ 6 | 7 | package com.backblaze.erasure; 8 | 9 | public class InputOutputByteExpCodingLoop extends CodingLoopBase { 10 | 11 | @Override 12 | public void codeSomeShards( 13 | byte[][] matrixRows, 14 | byte[][] inputs, int inputCount, 15 | byte[][] outputs, int outputCount, 16 | int offset, int byteCount) { 17 | 18 | { 19 | final int iInput = 0; 20 | final byte[] inputShard = inputs[iInput]; 21 | for (int iOutput = 0; iOutput < outputCount; iOutput++) { 22 | final byte[] outputShard = outputs[iOutput]; 23 | final byte[] matrixRow = matrixRows[iOutput]; 24 | final byte matrixByte = matrixRow[iInput]; 25 | for (int iByte = offset; iByte < offset + byteCount; iByte++) { 26 | outputShard[iByte] = Galois.multiply(matrixByte, inputShard[iByte]); 27 | } 28 | } 29 | } 30 | 31 | for (int iInput = 1; iInput < inputCount; iInput++) { 32 | final byte[] inputShard = inputs[iInput]; 33 | for (int iOutput = 0; iOutput < outputCount; iOutput++) { 34 | final byte[] outputShard = outputs[iOutput]; 35 | final byte[] matrixRow = matrixRows[iOutput]; 36 | final byte matrixByte = matrixRow[iInput]; 37 | for (int iByte = offset; iByte < offset + byteCount; iByte++) { 38 | outputShard[iByte] ^= Galois.multiply(matrixByte, inputShard[iByte]); 39 | } 40 | } 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /kcp-fec/src/main/java/com/backblaze/erasure/InputOutputByteTableCodingLoop.java: -------------------------------------------------------------------------------- 1 | /** 2 | * One specific ordering/nesting of the coding loops. 3 | * 4 | * Copyright 2015, Backblaze, Inc. All rights reserved. 5 | */ 6 | 7 | package com.backblaze.erasure; 8 | 9 | public class InputOutputByteTableCodingLoop extends CodingLoopBase { 10 | 11 | @Override 12 | public void codeSomeShards( 13 | byte[][] matrixRows, 14 | byte[][] inputs, int inputCount, 15 | byte[][] outputs, int outputCount, 16 | int offset, int byteCount) { 17 | 18 | final byte [] [] table = Galois.MULTIPLICATION_TABLE; 19 | 20 | { 21 | final int iInput = 0; 22 | final byte[] inputShard = inputs[iInput]; 23 | for (int iOutput = 0; iOutput < outputCount; iOutput++) { 24 | final byte[] outputShard = outputs[iOutput]; 25 | final byte[] matrixRow = matrixRows[iOutput]; 26 | final byte[] multTableRow = table[matrixRow[iInput] & 0xFF]; 27 | for (int iByte = offset; iByte < offset + byteCount; iByte++) { 28 | outputShard[iByte] = multTableRow[inputShard[iByte] & 0xFF]; 29 | } 30 | } 31 | } 32 | 33 | for (int iInput = 1; iInput < inputCount; iInput++) { 34 | final byte[] inputShard = inputs[iInput]; 35 | for (int iOutput = 0; iOutput < outputCount; iOutput++) { 36 | final byte[] outputShard = outputs[iOutput]; 37 | final byte[] matrixRow = matrixRows[iOutput]; 38 | final byte[] multTableRow = table[matrixRow[iInput] & 0xFF]; 39 | for (int iByte = offset; iByte < offset + byteCount; iByte++) { 40 | outputShard[iByte] ^= multTableRow[inputShard[iByte] & 0xFF]; 41 | } 42 | } 43 | } 44 | } 45 | 46 | @Override 47 | public boolean checkSomeShards( 48 | byte[][] matrixRows, 49 | byte[][] inputs, int inputCount, 50 | byte[][] toCheck, int checkCount, 51 | int offset, int byteCount, 52 | byte[] tempBuffer) { 53 | 54 | if (tempBuffer == null) { 55 | return super.checkSomeShards(matrixRows, inputs, inputCount, toCheck, checkCount, offset, byteCount, null); 56 | } 57 | 58 | // This is actually the code from OutputInputByteTableCodingLoop. 59 | // Using the loops from this class would require multiple temp 60 | // buffers. 61 | 62 | final byte [] [] table = Galois.MULTIPLICATION_TABLE; 63 | for (int iOutput = 0; iOutput < checkCount; iOutput++) { 64 | final byte [] outputShard = toCheck[iOutput]; 65 | final byte[] matrixRow = matrixRows[iOutput]; 66 | { 67 | final int iInput = 0; 68 | final byte [] inputShard = inputs[iInput]; 69 | final byte [] multTableRow = table[matrixRow[iInput] & 0xFF]; 70 | for (int iByte = offset; iByte < offset + byteCount; iByte++) { 71 | tempBuffer[iByte] = multTableRow[inputShard[iByte] & 0xFF]; 72 | } 73 | } 74 | for (int iInput = 1; iInput < inputCount; iInput++) { 75 | final byte [] inputShard = inputs[iInput]; 76 | final byte [] multTableRow = table[matrixRow[iInput] & 0xFF]; 77 | for (int iByte = offset; iByte < offset + byteCount; iByte++) { 78 | tempBuffer[iByte] ^= multTableRow[inputShard[iByte] & 0xFF]; 79 | } 80 | } 81 | for (int iByte = offset; iByte < offset + byteCount; iByte++) { 82 | if (tempBuffer[iByte] != outputShard[iByte]) { 83 | return false; 84 | } 85 | } 86 | } 87 | 88 | return true; 89 | } 90 | 91 | } 92 | -------------------------------------------------------------------------------- /kcp-fec/src/main/java/com/backblaze/erasure/OutputByteInputExpCodingLoop.java: -------------------------------------------------------------------------------- 1 | /** 2 | * One specific ordering/nesting of the coding loops. 3 | * 4 | * Copyright 2015, Backblaze, Inc. All rights reserved. 5 | */ 6 | 7 | package com.backblaze.erasure; 8 | 9 | public class OutputByteInputExpCodingLoop extends CodingLoopBase { 10 | 11 | @Override 12 | public void codeSomeShards( 13 | byte[][] matrixRows, 14 | byte[][] inputs, int inputCount, 15 | byte[][] outputs, int outputCount, 16 | int offset, int byteCount) { 17 | 18 | for (int iOutput = 0; iOutput < outputCount; iOutput++) { 19 | final byte[] outputShard = outputs[iOutput]; 20 | final byte[] matrixRow = matrixRows[iOutput]; 21 | for (int iByte = offset; iByte < offset + byteCount; iByte++) { 22 | int value = 0; 23 | for (int iInput = 0; iInput < inputCount; iInput++) { 24 | final byte[] inputShard = inputs[iInput]; 25 | value ^= Galois.multiply(matrixRow[iInput], inputShard[iByte]); 26 | } 27 | outputShard[iByte] = (byte) value; 28 | } 29 | } 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /kcp-fec/src/main/java/com/backblaze/erasure/OutputByteInputTableCodingLoop.java: -------------------------------------------------------------------------------- 1 | /** 2 | * One specific ordering/nesting of the coding loops. 3 | * 4 | * Copyright 2015, Backblaze, Inc. All rights reserved. 5 | */ 6 | 7 | package com.backblaze.erasure; 8 | 9 | public class OutputByteInputTableCodingLoop extends CodingLoopBase { 10 | 11 | @Override 12 | public void codeSomeShards( 13 | byte[][] matrixRows, 14 | byte[][] inputs, int inputCount, 15 | byte[][] outputs, int outputCount, 16 | int offset, int byteCount) { 17 | 18 | final byte [] [] table = Galois.MULTIPLICATION_TABLE; 19 | for (int iOutput = 0; iOutput < outputCount; iOutput++) { 20 | final byte[] outputShard = outputs[iOutput]; 21 | final byte[] matrixRow = matrixRows[iOutput]; 22 | for (int iByte = offset; iByte < offset + byteCount; iByte++) { 23 | int value = 0; 24 | for (int iInput = 0; iInput < inputCount; iInput++) { 25 | final byte[] inputShard = inputs[iInput]; 26 | final byte[] multTableRow = table[matrixRow[iInput] & 0xFF]; 27 | value ^= multTableRow[inputShard[iByte] & 0xFF]; 28 | } 29 | outputShard[iByte] = (byte) value; 30 | } 31 | } 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /kcp-fec/src/main/java/com/backblaze/erasure/OutputInputByteExpCodingLoop.java: -------------------------------------------------------------------------------- 1 | /** 2 | * One specific ordering/nesting of the coding loops. 3 | * 4 | * Copyright 2015, Backblaze, Inc. All rights reserved. 5 | */ 6 | 7 | package com.backblaze.erasure; 8 | 9 | public class OutputInputByteExpCodingLoop extends CodingLoopBase { 10 | 11 | @Override 12 | public void codeSomeShards( 13 | byte[][] matrixRows, 14 | byte[][] inputs, int inputCount, 15 | byte[][] outputs, int outputCount, 16 | int offset, int byteCount) { 17 | 18 | for (int iOutput = 0; iOutput < outputCount; iOutput++) { 19 | final byte [] outputShard = outputs[iOutput]; 20 | final byte [] matrixRow = matrixRows[iOutput]; 21 | { 22 | final int iInput = 0; 23 | final byte [] inputShard = inputs[iInput]; 24 | final byte matrixByte = matrixRow[iInput]; 25 | for (int iByte = offset; iByte < offset + byteCount; iByte++) { 26 | outputShard[iByte] = Galois.multiply(matrixByte, inputShard[iByte]); 27 | } 28 | } 29 | for (int iInput = 1; iInput < inputCount; iInput++) { 30 | final byte [] inputShard = inputs[iInput]; 31 | final byte matrixByte = matrixRow[iInput]; 32 | for (int iByte = offset; iByte < offset + byteCount; iByte++) { 33 | outputShard[iByte] ^= Galois.multiply(matrixByte, inputShard[iByte]); 34 | } 35 | } 36 | } 37 | } 38 | 39 | } 40 | -------------------------------------------------------------------------------- /kcp-fec/src/main/java/com/backblaze/erasure/OutputInputByteTableCodingLoop.java: -------------------------------------------------------------------------------- 1 | /** 2 | * One specific ordering/nesting of the coding loops. 3 | * 4 | * Copyright 2015, Backblaze, Inc. All rights reserved. 5 | */ 6 | 7 | package com.backblaze.erasure; 8 | 9 | public class OutputInputByteTableCodingLoop extends CodingLoopBase { 10 | 11 | @Override 12 | public void codeSomeShards( 13 | byte[][] matrixRows, 14 | byte[][] inputs, int inputCount, 15 | byte[][] outputs, int outputCount, 16 | int offset, int byteCount) { 17 | 18 | final byte [] [] table = Galois.MULTIPLICATION_TABLE; 19 | for (int iOutput = 0; iOutput < outputCount; iOutput++) { 20 | final byte [] outputShard = outputs[iOutput]; 21 | final byte[] matrixRow = matrixRows[iOutput]; 22 | { 23 | final int iInput = 0; 24 | final byte [] inputShard = inputs[iInput]; 25 | final byte [] multTableRow = table[matrixRow[iInput] & 0xFF]; 26 | for (int iByte = offset; iByte < offset + byteCount; iByte++) { 27 | outputShard[iByte] = multTableRow[inputShard[iByte] & 0xFF]; 28 | } 29 | } 30 | for (int iInput = 1; iInput < inputCount; iInput++) { 31 | final byte [] inputShard = inputs[iInput]; 32 | final byte [] multTableRow = table[matrixRow[iInput] & 0xFF]; 33 | for (int iByte = offset; iByte < offset + byteCount; iByte++) { 34 | outputShard[iByte] ^= multTableRow[inputShard[iByte] & 0xFF]; 35 | } 36 | } 37 | } 38 | } 39 | 40 | @Override 41 | public boolean checkSomeShards( 42 | byte[][] matrixRows, 43 | byte[][] inputs, int inputCount, 44 | byte[][] toCheck, int checkCount, 45 | int offset, int byteCount, 46 | byte[] tempBuffer) { 47 | 48 | if (tempBuffer == null) { 49 | return super.checkSomeShards(matrixRows, inputs, inputCount, toCheck, checkCount, offset, byteCount, null); 50 | } 51 | 52 | final byte [] [] table = Galois.MULTIPLICATION_TABLE; 53 | for (int iOutput = 0; iOutput < checkCount; iOutput++) { 54 | final byte [] outputShard = toCheck[iOutput]; 55 | final byte[] matrixRow = matrixRows[iOutput]; 56 | { 57 | final int iInput = 0; 58 | final byte [] inputShard = inputs[iInput]; 59 | final byte [] multTableRow = table[matrixRow[iInput] & 0xFF]; 60 | for (int iByte = offset; iByte < offset + byteCount; iByte++) { 61 | tempBuffer[iByte] = multTableRow[inputShard[iByte] & 0xFF]; 62 | } 63 | } 64 | for (int iInput = 1; iInput < inputCount; iInput++) { 65 | final byte [] inputShard = inputs[iInput]; 66 | final byte [] multTableRow = table[matrixRow[iInput] & 0xFF]; 67 | for (int iByte = offset; iByte < offset + byteCount; iByte++) { 68 | tempBuffer[iByte] ^= multTableRow[inputShard[iByte] & 0xFF]; 69 | } 70 | } 71 | for (int iByte = offset; iByte < offset + byteCount; iByte++) { 72 | if (tempBuffer[iByte] != outputShard[iByte]) { 73 | return false; 74 | } 75 | } 76 | } 77 | 78 | return true; 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /kcp-fec/src/main/java/com/backblaze/erasure/SampleEncoder.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Command-line program encodes one file using Reed-Solomon 4+2. 3 | * 4 | * Copyright 2015, Backblaze, Inc. 5 | */ 6 | 7 | package com.backblaze.erasure; 8 | 9 | import java.io.File; 10 | import java.io.FileInputStream; 11 | import java.io.FileOutputStream; 12 | import java.io.IOException; 13 | import java.io.InputStream; 14 | import java.io.OutputStream; 15 | import java.nio.ByteBuffer; 16 | 17 | /** 18 | * Command-line program encodes one file using Reed-Solomon 4+2. 19 | * 20 | * The one argument should be a file name, say "foo.txt". This program 21 | * will create six files in the same directory, breaking the input file 22 | * into four data shards, and two parity shards. The output files are 23 | * called "foo.txt.0", "foo.txt.1", ..., and "foo.txt.5". Numbers 4 24 | * and 5 are the parity shards. 25 | * 26 | * The data stored is the file size (four byte int), followed by the 27 | * contents of the file, and then padded to a multiple of four bytes 28 | * with zeros. The padding is because all four data shards must be 29 | * the same size. 30 | */ 31 | public class SampleEncoder { 32 | 33 | public static final int DATA_SHARDS = 4; 34 | public static final int PARITY_SHARDS = 2; 35 | public static final int TOTAL_SHARDS = 6; 36 | 37 | public static final int BYTES_IN_INT = 4; 38 | 39 | public static void main(String [] arguments) throws IOException { 40 | 41 | // Parse the command line 42 | if (arguments.length != 1) { 43 | System.out.println("Usage: SampleEncoder "); 44 | return; 45 | } 46 | final File inputFile = new File(arguments[0]); 47 | if (!inputFile.exists()) { 48 | System.out.println("Cannot read input file: " + inputFile); 49 | return; 50 | } 51 | 52 | // Get the size of the input file. (Files bigger that 53 | // Integer.MAX_VALUE will fail here!) 54 | final int fileSize = (int) inputFile.length(); 55 | 56 | // Figure out how big each shard will be. The total size stored 57 | // will be the file size (8 bytes) plus the file. 58 | final int storedSize = fileSize + BYTES_IN_INT; 59 | final int shardSize = (storedSize + DATA_SHARDS - 1) / DATA_SHARDS; 60 | 61 | // Create a buffer holding the file size, followed by 62 | // the contents of the file. 63 | final int bufferSize = shardSize * DATA_SHARDS; 64 | final byte [] allBytes = new byte[bufferSize]; 65 | ByteBuffer.wrap(allBytes).putInt(fileSize); 66 | InputStream in = new FileInputStream(inputFile); 67 | int bytesRead = in.read(allBytes, BYTES_IN_INT, fileSize); 68 | if (bytesRead != fileSize) { 69 | throw new IOException("not enough bytes read"); 70 | } 71 | in.close(); 72 | 73 | // Make the buffers to hold the shards. 74 | byte [] [] shards = new byte [TOTAL_SHARDS] [shardSize]; 75 | 76 | // Fill in the data shards 77 | for (int i = 0; i < DATA_SHARDS; i++) { 78 | System.arraycopy(allBytes, i * shardSize, shards[i], 0, shardSize); 79 | } 80 | 81 | // Use Reed-Solomon to calculate the parity. 82 | ReedSolomon reedSolomon = ReedSolomon.create(DATA_SHARDS, PARITY_SHARDS); 83 | reedSolomon.encodeParity(shards, 0, shardSize); 84 | 85 | // Write out the resulting files. 86 | for (int i = 0; i < TOTAL_SHARDS; i++) { 87 | File outputFile = new File( 88 | inputFile.getParentFile(), 89 | inputFile.getName() + "." + i); 90 | OutputStream out = new FileOutputStream(outputFile); 91 | out.write(shards[i]); 92 | out.close(); 93 | System.out.println("wrote " + outputFile); 94 | } 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /kcp-fec/src/main/java/com/backblaze/erasure/bytebuf/ByteBufCodingLoop.java: -------------------------------------------------------------------------------- 1 | package com.backblaze.erasure.bytebuf; 2 | 3 | import io.netty.buffer.ByteBuf; 4 | 5 | /** 6 | * Created by JinMiao 7 | * 2018/6/7. 8 | */ 9 | public interface ByteBufCodingLoop { 10 | 11 | /** 12 | * Multiplies a subset of rows from a coding matrix by a full set of 13 | * input shards to produce some output shards. 14 | * 15 | * @param matrixRows The rows from the matrix to use. 16 | * @param inputs An array of byte arrays, each of which is one input shard. 17 | * The inputs array may have extra buffers after the ones 18 | * that are used. They will be ignored. The number of 19 | * inputs used is determined by the length of the 20 | * each matrix row. 21 | * @param inputCount The number of input byte arrays. 22 | * @param outputs Byte arrays where the computed shards are stored. The 23 | * outputs array may also have extra, unused, elements 24 | * at the end. The number of outputs computed, and the 25 | * number of matrix rows used, is determined by 26 | * outputCount. 27 | * @param outputCount The number of outputs to compute. 28 | * @param offset The index in the inputs and output of the first byte 29 | * to process. 30 | * @param byteCount The number of bytes to process. 31 | */ 32 | void codeSomeShards(final byte [] [] matrixRows, 33 | final ByteBuf[] inputs, 34 | final int inputCount, 35 | final ByteBuf [] outputs, 36 | final int outputCount, 37 | final int offset, 38 | final int byteCount); 39 | 40 | /** 41 | * Multiplies a subset of rows from a coding matrix by a full set of 42 | * input shards to produce some output shards, and checks that the 43 | * the data is those shards matches what's expected. 44 | * 45 | * @param matrixRows The rows from the matrix to use. 46 | * @param inputs An array of byte arrays, each of which is one input shard. 47 | * The inputs array may have extra buffers after the ones 48 | * that are used. They will be ignored. The number of 49 | * inputs used is determined by the length of the 50 | * each matrix row. 51 | * @param inputCount THe number of input byte arrays. 52 | * @param toCheck Byte arrays where the computed shards are stored. The 53 | * outputs array may also have extra, unused, elements 54 | * at the end. The number of outputs computed, and the 55 | * number of matrix rows used, is determined by 56 | * outputCount. 57 | * @param checkCount The number of outputs to compute. 58 | * @param offset The index in the inputs and output of the first byte 59 | * to process. 60 | * @param byteCount The number of bytes to process. 61 | * @param tempBuffer A place to store temporary results. May be null. 62 | */ 63 | boolean checkSomeShards(final byte [] [] matrixRows, 64 | final ByteBuf [] inputs, 65 | final int inputCount, 66 | final byte [] [] toCheck, 67 | final int checkCount, 68 | final int offset, 69 | final int byteCount, 70 | final byte [] tempBuffer); 71 | } 72 | -------------------------------------------------------------------------------- /kcp-fec/src/main/java/com/backblaze/erasure/bytebuf/ByteBufCodingLoopBase.java: -------------------------------------------------------------------------------- 1 | package com.backblaze.erasure.bytebuf; 2 | 3 | import com.backblaze.erasure.Galois; 4 | import io.netty.buffer.ByteBuf; 5 | 6 | /** 7 | * Created by JinMiao 8 | * 2018/6/7. 9 | */ 10 | public abstract class ByteBufCodingLoopBase implements ByteBufCodingLoop { 11 | 12 | @Override 13 | public boolean checkSomeShards(byte[][] matrixRows, ByteBuf[] inputs, int inputCount, byte[][] toCheck, int checkCount, int offset, int byteCount, byte[] tempBuffer) 14 | { 15 | // This is the loop structure for ByteOutputInput, which does not 16 | // require temporary buffers for checking. 17 | byte [] [] table = Galois.MULTIPLICATION_TABLE; 18 | for (int iByte = offset; iByte < offset + byteCount; iByte++) { 19 | for (int iOutput = 0; iOutput < checkCount; iOutput++) { 20 | byte [] matrixRow = matrixRows[iOutput]; 21 | int value = 0; 22 | for (int iInput = 0; iInput < inputCount; iInput++) { 23 | value ^= table[matrixRow[iInput] & 0xFF][inputs[iInput].getByte(iByte) & 0xFF]; 24 | } 25 | if (toCheck[iOutput][iByte] != (byte) value) { 26 | return false; 27 | } 28 | } 29 | } 30 | return true; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /kcp-fec/src/main/java/com/backblaze/erasure/bytebuf/InputOutputByteBufTableCodingLoop.java: -------------------------------------------------------------------------------- 1 | package com.backblaze.erasure.bytebuf; 2 | 3 | import com.backblaze.erasure.Galois; 4 | import io.netty.buffer.ByteBuf; 5 | 6 | /** 7 | * Created by JinMiao 8 | * 2018/6/7. 9 | */ 10 | public class InputOutputByteBufTableCodingLoop extends ByteBufCodingLoopBase { 11 | @Override 12 | public void codeSomeShards(byte[][] matrixRows, ByteBuf[] inputs, int inputCount, ByteBuf[] outputs, int outputCount, int offset, int byteCount) { 13 | 14 | final byte [] [] table = Galois.MULTIPLICATION_TABLE; 15 | 16 | { 17 | final int iInput = 0; 18 | final ByteBuf inputShard = inputs[iInput]; 19 | for (int iOutput = 0; iOutput < outputCount; iOutput++) { 20 | final ByteBuf outputShard = outputs[iOutput]; 21 | final byte[] matrixRow = matrixRows[iOutput]; 22 | final byte[] multTableRow = table[matrixRow[iInput] & 0xFF]; 23 | for (int iByte = offset; iByte < offset + byteCount; iByte++) { 24 | outputShard.setByte(iByte,multTableRow[inputShard.getByte(iByte) & 0xFF]); 25 | //outputShard[iByte] = multTableRow[inputShard[iByte] & 0xFF]; 26 | } 27 | } 28 | } 29 | 30 | for (int iInput = 1; iInput < inputCount; iInput++) { 31 | final ByteBuf inputShard = inputs[iInput]; 32 | for (int iOutput = 0; iOutput < outputCount; iOutput++) { 33 | final ByteBuf outputShard = outputs[iOutput]; 34 | final byte[] matrixRow = matrixRows[iOutput]; 35 | final byte[] multTableRow = table[matrixRow[iInput] & 0xFF]; 36 | for (int iByte = offset; iByte < offset + byteCount; iByte++) { 37 | byte temp =outputShard.getByte(iByte); 38 | temp ^= multTableRow[inputShard.getByte(iByte) & 0xFF]; 39 | outputShard.setByte(iByte,temp); 40 | //outputShard[iByte] ^= multTableRow[inputShard[iByte] & 0xFF]; 41 | } 42 | } 43 | } 44 | } 45 | 46 | 47 | @Override 48 | public boolean checkSomeShards( 49 | byte[][] matrixRows, 50 | ByteBuf[] inputs, int inputCount, 51 | byte[][] toCheck, int checkCount, 52 | int offset, int byteCount, 53 | byte[] tempBuffer) { 54 | 55 | if (tempBuffer == null) { 56 | return super.checkSomeShards(matrixRows, inputs, inputCount, toCheck, checkCount, offset, byteCount, null); 57 | } 58 | 59 | // This is actually the code from OutputInputByteTableCodingLoop. 60 | // Using the loops from this class would require multiple temp 61 | // buffers. 62 | 63 | final byte [] [] table = Galois.MULTIPLICATION_TABLE; 64 | for (int iOutput = 0; iOutput < checkCount; iOutput++) { 65 | final byte [] outputShard = toCheck[iOutput]; 66 | final byte[] matrixRow = matrixRows[iOutput]; 67 | { 68 | final int iInput = 0; 69 | final ByteBuf inputShard = inputs[iInput]; 70 | final byte [] multTableRow = table[matrixRow[iInput] & 0xFF]; 71 | for (int iByte = offset; iByte < offset + byteCount; iByte++) { 72 | tempBuffer[iByte] = multTableRow[inputShard.getByte(iByte) & 0xFF]; 73 | } 74 | } 75 | for (int iInput = 1; iInput < inputCount; iInput++) { 76 | final ByteBuf inputShard = inputs[iInput]; 77 | final byte [] multTableRow = table[matrixRow[iInput] & 0xFF]; 78 | for (int iByte = offset; iByte < offset + byteCount; iByte++) { 79 | tempBuffer[iByte] ^= multTableRow[inputShard.getByte(iByte) & 0xFF]; 80 | } 81 | } 82 | for (int iByte = offset; iByte < offset + byteCount; iByte++) { 83 | if (tempBuffer[iByte] != outputShard[iByte]) { 84 | return false; 85 | } 86 | } 87 | } 88 | 89 | return true; 90 | } 91 | 92 | 93 | 94 | 95 | } 96 | -------------------------------------------------------------------------------- /kcp-fec/src/main/java/com/backblaze/erasure/fec/Fec.java: -------------------------------------------------------------------------------- 1 | package com.backblaze.erasure.fec; 2 | 3 | /** 4 | * Created by JinMiao 5 | * 2018/6/6. 6 | */ 7 | public class Fec { 8 | public static int 9 | fecHeaderSize = 6, 10 | fecDataSize = 2, 11 | fecHeaderSizePlus2 = fecHeaderSize + fecDataSize, // plus 2B data size 12 | typeData = 0xf1, 13 | typeParity = 0xf2; 14 | 15 | } 16 | -------------------------------------------------------------------------------- /kcp-fec/src/main/java/com/backblaze/erasure/fec/FecException.java: -------------------------------------------------------------------------------- 1 | package com.backblaze.erasure.fec; 2 | 3 | /** 4 | * Created by JinMiao 5 | * 2018/6/8. 6 | */ 7 | public class FecException extends RuntimeException{ 8 | public FecException(String message) { 9 | super(message); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /kcp-fec/src/main/java/com/backblaze/erasure/fec/FecPacket.java: -------------------------------------------------------------------------------- 1 | package com.backblaze.erasure.fec; 2 | 3 | import io.netty.buffer.ByteBuf; 4 | import io.netty.util.Recycler; 5 | 6 | /** 7 | * Created by JinMiao 8 | * 2018/6/26. 9 | */ 10 | public class FecPacket { 11 | private long seqid; 12 | private int flag; 13 | private ByteBuf data; 14 | private Recycler.Handle recyclerHandle; 15 | 16 | 17 | private static final Recycler FEC_PACKET_RECYCLER = new Recycler() { 18 | @Override 19 | protected FecPacket newObject(Handle handle) { 20 | return new FecPacket(handle); 21 | } 22 | }; 23 | 24 | public static FecPacket newFecPacket(ByteBuf byteBuf){ 25 | FecPacket pkt = FEC_PACKET_RECYCLER.get(); 26 | pkt.seqid =byteBuf.readUnsignedIntLE(); 27 | pkt.flag = byteBuf.readUnsignedShortLE(); 28 | pkt.data = byteBuf.retainedSlice(byteBuf.readerIndex(),byteBuf.capacity()-byteBuf.readerIndex()); 29 | pkt.data.writerIndex(byteBuf.readableBytes()); 30 | return pkt; 31 | } 32 | 33 | private FecPacket(Recycler.Handle recyclerHandle) { 34 | this.recyclerHandle = recyclerHandle; 35 | } 36 | 37 | public void release(){ 38 | this.seqid = 0; 39 | this.flag = 0; 40 | this.data.release(); 41 | this.data = null; 42 | recyclerHandle.recycle(this); 43 | } 44 | 45 | public long getSeqid() { 46 | return seqid; 47 | } 48 | 49 | public int getFlag() { 50 | return flag; 51 | } 52 | 53 | public void setFlag(int flag) { 54 | this.flag = flag; 55 | } 56 | 57 | public ByteBuf getData() { 58 | return data; 59 | } 60 | 61 | public void setData(ByteBuf data) { 62 | this.data = data; 63 | } 64 | 65 | @Override 66 | public String toString() { 67 | return "FecPacket{" + 68 | "seqid=" + seqid + 69 | ", flag=" + flag + 70 | '}'; 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /kcp-fec/src/main/java/com/backblaze/erasure/fec/MyArrayList.java: -------------------------------------------------------------------------------- 1 | package com.backblaze.erasure.fec; 2 | 3 | import java.util.ArrayList; 4 | 5 | /** 6 | * Created by JinMiao 7 | * 2020/7/2. 8 | */ 9 | public class MyArrayList extends ArrayList { 10 | 11 | 12 | public MyArrayList() { 13 | super(); 14 | } 15 | 16 | public MyArrayList(int initialCapacity) { 17 | super(initialCapacity); 18 | } 19 | 20 | public void removeRange(int fromIndex, int toIndex){ 21 | super.removeRange(fromIndex, toIndex); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /kcp-fec/src/main/java/com/backblaze/erasure/fecNative/ReedSolomonC.java: -------------------------------------------------------------------------------- 1 | package com.backblaze.erasure.fecNative; 2 | 3 | import java.io.*; 4 | 5 | /** 6 | * Created by JinMiao 7 | * 2018/8/27. 8 | */ 9 | public class ReedSolomonC { 10 | 11 | private static boolean nativeSupport = true; 12 | static { 13 | try { 14 | String path = System.getProperty("user.dir"); 15 | String libPath = new File(path, "kcp-fec/src/main/java/com/backblaze/erasure/fecNative/native/").toString(); 16 | String extension = System.mapLibraryName("jni"); 17 | libPath +=File.separator+ extension; 18 | System.load(libPath); 19 | init(); 20 | }catch (Throwable e){ 21 | nativeSupport = false; 22 | } 23 | } 24 | 25 | public static boolean isNativeSupport() { 26 | return nativeSupport; 27 | } 28 | 29 | protected static native void init(); 30 | 31 | protected native long rsNew(int data_shards, int parity_shards); 32 | 33 | protected native void rsRelease(long reedSolomonPtr); 34 | 35 | protected native void rsEncode(long reedSolomonPtr,long[] shards,int byteCount); 36 | 37 | protected native void rsReconstruct(long reedSolomonPtr,long[] shards,boolean[] shardPresent,int byteCount); 38 | } 39 | -------------------------------------------------------------------------------- /kcp-fec/src/main/java/com/backblaze/erasure/fecNative/ReedSolomonNative.java: -------------------------------------------------------------------------------- 1 | package com.backblaze.erasure.fecNative; 2 | 3 | /** 4 | * jni调用c版本fec 5 | * netty的directbuf内存直接传给c层进行fec 6 | * 完全没有gc 7 | * Created by JinMiao 8 | * 2021/1/29. 9 | */ 10 | public class ReedSolomonNative{ 11 | 12 | public static final ReedSolomonC REED_SOLOMON_C = new ReedSolomonC(); 13 | 14 | private long reedSolomonPtr; 15 | private int dataShards; 16 | private int parityShards; 17 | 18 | 19 | public int getTotalShardCount(){ 20 | return this.dataShards+this.parityShards; 21 | } 22 | 23 | 24 | public ReedSolomonNative(int dataShards, int parityShards){ 25 | long reedSolomonPtr = REED_SOLOMON_C.rsNew(dataShards,parityShards); 26 | this.reedSolomonPtr = reedSolomonPtr; 27 | this.dataShards = dataShards; 28 | this.parityShards = parityShards; 29 | } 30 | 31 | 32 | public static boolean isNativeSupport() { 33 | return ReedSolomonC.isNativeSupport(); 34 | } 35 | 36 | 37 | protected void rsRelease(){ 38 | this.REED_SOLOMON_C.rsRelease(this.reedSolomonPtr); 39 | } 40 | 41 | protected void rsEncode(long[] shards,int byteCount){ 42 | this.REED_SOLOMON_C.rsEncode(this.reedSolomonPtr,shards,byteCount); 43 | } 44 | 45 | protected void rsReconstruct(long[] shards,boolean[] shardPresent,int byteCount){ 46 | this.REED_SOLOMON_C.rsReconstruct(this.reedSolomonPtr,shards,shardPresent,byteCount); 47 | } 48 | 49 | public int getDataShards() { 50 | return dataShards; 51 | } 52 | 53 | public void setDataShards(int dataShards) { 54 | this.dataShards = dataShards; 55 | } 56 | 57 | public int getParityShards() { 58 | return parityShards; 59 | } 60 | 61 | public void setParityShards(int parityShards) { 62 | this.parityShards = parityShards; 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /kcp-fec/src/main/java/com/backblaze/erasure/fecNative/native/jni.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/l42111996/java-Kcp/HEAD/kcp-fec/src/main/java/com/backblaze/erasure/fecNative/native/jni.dll -------------------------------------------------------------------------------- /kcp-fec/src/main/java/com/backblaze/erasure/fecNative/native/libjni.dylib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/l42111996/java-Kcp/HEAD/kcp-fec/src/main/java/com/backblaze/erasure/fecNative/native/libjni.dylib -------------------------------------------------------------------------------- /kcp-fec/src/test/java/com/backblaze/erasure/BytebufBenchmark.java: -------------------------------------------------------------------------------- 1 | package com.backblaze.erasure; 2 | 3 | import com.backblaze.erasure.fec.*; 4 | import io.netty.buffer.ByteBuf; 5 | import io.netty.buffer.ByteBufAllocator; 6 | 7 | import java.util.List; 8 | import java.util.Random; 9 | 10 | /** 11 | * Created by JinMiao 12 | * 2020/12/25. 13 | */ 14 | public class BytebufBenchmark { 15 | 16 | private static final int DATA_COUNT = 17; 17 | private static final int PARITY_COUNT = 3; 18 | private static final int TOTAL_COUNT = DATA_COUNT + PARITY_COUNT; 19 | private static final int BUFFER_SIZE = 200 * 1000; 20 | private static final int PROCESSOR_CACHE_SIZE = 10 * 1024 * 1024; 21 | private static final Random RANDOM = new Random(); 22 | public static void main(String[] args) { 23 | ReedSolomon reedSolomon = ReedSolomon.create(DATA_COUNT,PARITY_COUNT); 24 | FecDecode fecDecode = new FecDecode(TOTAL_COUNT*3,reedSolomon,BUFFER_SIZE); 25 | FecEncode fecEncode = new FecEncode(0,reedSolomon,BUFFER_SIZE); 26 | 27 | ByteBuf[] byteBufs = new ByteBuf[DATA_COUNT]; 28 | for (int i = 0; i < DATA_COUNT; i++) { 29 | ByteBuf byteBuf = ByteBufAllocator.DEFAULT.buffer(BUFFER_SIZE); 30 | for (int i1 = 0; i1 < BUFFER_SIZE; i1++) { 31 | byteBuf.writeByte((byte) RANDOM.nextInt(256)); 32 | } 33 | byteBufs[i] = byteBuf; 34 | } 35 | 36 | long start = System.currentTimeMillis(); 37 | double size = 0; 38 | 39 | for(;;){ 40 | long now = System.currentTimeMillis(); 41 | if(now-start>=1000){ 42 | 43 | System.out.println("时间"+(now-start)+" "+((size)/1024.0/1024.0)+" MB"); 44 | start=now; 45 | size=0; 46 | } 47 | size+=(BUFFER_SIZE*TOTAL_COUNT); 48 | ByteBuf[] byteBufs1 = null; 49 | for (ByteBuf byteBuf : byteBufs) { 50 | byteBufs1 = fecEncode.encode(byteBuf); 51 | if(byteBufs1!=null){ 52 | break; 53 | } 54 | } 55 | int dropIndex = RANDOM.nextInt(20)+1; 56 | 57 | 58 | for (ByteBuf byteBuf : byteBufs1) { 59 | if(dropIndex>1&&dropIndex<4){ 60 | byteBuf.release(); 61 | continue; 62 | } 63 | 64 | 65 | FecPacket fecPacket = FecPacket.newFecPacket(byteBuf); 66 | byteBuf.release(); 67 | List byteBufList = fecDecode.decode(fecPacket); 68 | if(byteBufList!=null&&byteBufList.size()!=0){ 69 | System.out.println(); 70 | for (ByteBuf buf : byteBufList) { 71 | buf.release(); 72 | } 73 | } 74 | 75 | } 76 | //System.out.println(now-start); 77 | } 78 | 79 | 80 | 81 | 82 | 83 | 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /kcp-fec/src/test/java/com/backblaze/erasure/BytebufBenchmarkNative.java: -------------------------------------------------------------------------------- 1 | package com.backblaze.erasure; 2 | 3 | import com.backblaze.erasure.fec.FecPacket; 4 | import com.backblaze.erasure.fecNative.FecDecode; 5 | import com.backblaze.erasure.fecNative.FecEncode; 6 | import com.backblaze.erasure.fecNative.ReedSolomonNative; 7 | import io.netty.buffer.ByteBuf; 8 | import io.netty.buffer.ByteBufAllocator; 9 | 10 | import java.util.List; 11 | import java.util.Random; 12 | 13 | /** 14 | * Created by JinMiao 15 | * 2020/12/25. 16 | */ 17 | public class BytebufBenchmarkNative { 18 | 19 | private static final int DATA_COUNT = 17; 20 | private static final int PARITY_COUNT = 3; 21 | private static final int TOTAL_COUNT = DATA_COUNT + PARITY_COUNT; 22 | private static final int BUFFER_SIZE = 200 * 1000; 23 | private static final int PROCESSOR_CACHE_SIZE = 10 * 1024 * 1024; 24 | private static final Random RANDOM = new Random(); 25 | public static void main(String[] args) { 26 | ReedSolomonNative reedSolomon = new ReedSolomonNative(DATA_COUNT,PARITY_COUNT); 27 | FecDecode fecDecode = new FecDecode(TOTAL_COUNT*3,reedSolomon,BUFFER_SIZE); 28 | FecEncode fecEncode = new FecEncode(0,reedSolomon,BUFFER_SIZE); 29 | 30 | ByteBuf[] byteBufs = new ByteBuf[DATA_COUNT]; 31 | for (int i = 0; i < DATA_COUNT; i++) { 32 | ByteBuf byteBuf = ByteBufAllocator.DEFAULT.buffer(BUFFER_SIZE); 33 | for (int i1 = 0; i1 < BUFFER_SIZE; i1++) { 34 | byteBuf.writeByte((byte) RANDOM.nextInt(256)); 35 | } 36 | byteBufs[i] = byteBuf; 37 | } 38 | 39 | long start = System.currentTimeMillis(); 40 | double size = 0; 41 | 42 | for(;;){ 43 | long now = System.currentTimeMillis(); 44 | if(now-start>=1000){ 45 | 46 | System.out.println("时间"+(now-start)+" "+((size)/1024.0/1024.0)+" MB"); 47 | start=now; 48 | size=0; 49 | } 50 | size+=(BUFFER_SIZE*TOTAL_COUNT); 51 | ByteBuf[] byteBufs1 = null; 52 | for (ByteBuf byteBuf : byteBufs) { 53 | byteBufs1 = fecEncode.encode(byteBuf); 54 | if(byteBufs1!=null){ 55 | break; 56 | } 57 | } 58 | int dropIndex = RANDOM.nextInt(20)+1; 59 | 60 | 61 | for (ByteBuf byteBuf : byteBufs1) { 62 | if(dropIndex>1&&dropIndex<4){ 63 | byteBuf.release(); 64 | continue; 65 | } 66 | 67 | 68 | FecPacket fecPacket = FecPacket.newFecPacket(byteBuf); 69 | byteBuf.release(); 70 | List byteBufList = fecDecode.decode(fecPacket); 71 | if(byteBufList!=null&&byteBufList.size()!=0){ 72 | System.out.println(); 73 | for (ByteBuf buf : byteBufList) { 74 | buf.release(); 75 | } 76 | } 77 | 78 | } 79 | //System.out.println(now-start); 80 | } 81 | 82 | 83 | 84 | 85 | 86 | 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /kcp-fec/src/test/java/com/backblaze/erasure/FecDecodeBenchmark.java: -------------------------------------------------------------------------------- 1 | package com.backblaze.erasure; 2 | 3 | import com.backblaze.erasure.fec.MyArrayList; 4 | import org.openjdk.jmh.annotations.*; 5 | 6 | import java.util.concurrent.TimeUnit; 7 | 8 | /** 9 | * Created by JinMiao 10 | * 2020/7/2. 11 | */ 12 | @BenchmarkMode(Mode.Throughput) 13 | @OutputTimeUnit(TimeUnit.SECONDS) 14 | @Fork(1) 15 | @Warmup(iterations = 1) 16 | @Measurement(iterations = 3) 17 | //@State(Scope.Group) 18 | public class FecDecodeBenchmark { 19 | 20 | 21 | @Benchmark 22 | public void remove(){ 23 | buildFree(); 24 | } 25 | 26 | 27 | @Benchmark 28 | public void removeRange(){ 29 | buildRemoveRange(); 30 | } 31 | 32 | 33 | 34 | public void buildRemoveRange(){ 35 | int first = 10; 36 | int n=20; 37 | MyArrayList q = new MyArrayList<>(); 38 | for (int i = 0; i < 100; i++) { 39 | q.add(i); 40 | } 41 | q.removeRange(first,first+n); 42 | } 43 | 44 | public void buildFree(){ 45 | int first = 0; 46 | int n=20; 47 | MyArrayList q = new MyArrayList<>(); 48 | for (int i = 0; i < 100; i++) { 49 | q.add(i); 50 | } 51 | //q.removeRange(first,first+n); 52 | for (int i = first; i < q.size(); i++) { 53 | int index = i+n; 54 | if(index==q.size()) { 55 | break; 56 | } 57 | q.set(i,q.get(index)); 58 | } 59 | for (int i = 0; i < n; i++) { 60 | q.remove(q.size()-1); 61 | } 62 | } 63 | 64 | } 65 | -------------------------------------------------------------------------------- /kcp-fec/src/test/java/com/backblaze/erasure/MatrixTest.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Unit tests for Matrix 3 | * 4 | * Copyright 2015, Backblaze, Inc. All rights reserved. 5 | */ 6 | 7 | package com.backblaze.erasure; 8 | 9 | public class MatrixTest { 10 | 11 | //@Test 12 | //public void testIdentity() { 13 | // assertEquals( 14 | // "[[1, 0, 0], [0, 1, 0], [0, 0, 1]]", 15 | // Matrix.identity(3).toString() 16 | // ); 17 | //} 18 | // 19 | //@Test 20 | //public void testBigString() { 21 | // assertEquals("01 00 \n00 01 \n", Matrix.identity(2).toBigString()); 22 | //} 23 | // 24 | //@Test 25 | //public void testMultiply() { 26 | // Matrix m1 = new Matrix( 27 | // new byte [] [] { 28 | // new byte [] { 1, 2 }, 29 | // new byte [] { 3, 4 } 30 | // }); 31 | // Matrix m2 = new Matrix( 32 | // new byte [] [] { 33 | // new byte [] { 5, 6 }, 34 | // new byte [] { 7, 8 } 35 | // }); 36 | // Matrix actual = m1.times(m2); 37 | // // correct answer from java_tables.py 38 | // assertEquals("[[11, 22], [19, 42]]", actual.toString()); 39 | //} 40 | // 41 | //@Test 42 | //public void inverse() { 43 | // Matrix m = new Matrix( 44 | // new byte [] [] { 45 | // new byte [] { 56, 23, 98 }, 46 | // new byte [] { 3, 100, (byte)200 }, 47 | // new byte [] { 45, (byte)201, 123 } 48 | // }); 49 | // assertEquals( 50 | // "[[175, 133, 33], [130, 13, 245], [112, 35, 126]]", 51 | // m.invert().toString() 52 | // ); 53 | // assertEquals( 54 | // Matrix.identity(3).toString(), 55 | // m.times(m.invert()).toString() 56 | // ); 57 | //} 58 | // 59 | //@Test 60 | //public void inverse2() { 61 | // Matrix m = new Matrix( 62 | // new byte [] [] { 63 | // new byte [] { 1, 0, 0, 0, 0 }, 64 | // new byte [] { 0, 1, 0, 0, 0 }, 65 | // new byte [] { 0, 0, 0, 1, 0 }, 66 | // new byte [] { 0, 0, 0, 0, 1 }, 67 | // new byte [] { 7, 7, 6, 6, 1 } 68 | // } 69 | // ); 70 | // assertEquals( 71 | // "[[1, 0, 0, 0, 0]," + 72 | // " [0, 1, 0, 0, 0]," + 73 | // " [123, 123, 1, 122, 122]," + 74 | // " [0, 0, 1, 0, 0]," + 75 | // " [0, 0, 0, 1, 0]]", 76 | // m.invert().toString() 77 | // ); 78 | // assertEquals( 79 | // Matrix.identity(5).toString(), 80 | // m.times(m.invert()).toString() 81 | // ); 82 | //} 83 | } 84 | -------------------------------------------------------------------------------- /kcp-lockStepSynchronization/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | java-Kcp 7 | com.github.l42111996 8 | 1.6.1 9 | 10 | 4.0.0 11 | 12 | kcp-lockStepSynchronization 13 | 14 | 15 | 16 | com.github.l42111996 17 | kcp-base 18 | ${project.version} 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | -------------------------------------------------------------------------------- /kcp-lockStepSynchronization/src/main/java/test/IWriter.java: -------------------------------------------------------------------------------- 1 | package test; 2 | 3 | import io.netty.buffer.ByteBuf; 4 | 5 | /** 6 | * Created by JinMiao 7 | * 2020/7/17. 8 | */ 9 | public interface IWriter { 10 | void write(ByteBuf byteBuf); 11 | } 12 | -------------------------------------------------------------------------------- /kcp-lockStepSynchronization/src/main/java/test/KcpLockStepSynchronizationClient.java: -------------------------------------------------------------------------------- 1 | package test; 2 | 3 | import com.backblaze.erasure.fec.Snmp; 4 | import io.netty.buffer.ByteBuf; 5 | import io.netty.buffer.ByteBufAllocator; 6 | import kcp.ChannelConfig; 7 | import kcp.KcpClient; 8 | import kcp.KcpListener; 9 | import kcp.Ukcp; 10 | 11 | import java.net.InetSocketAddress; 12 | 13 | /** 14 | * 模拟帧同步 15 | * 50ms一帧 16 | * 20字节一个包 17 | * Created by JinMiao 18 | * 2019-06-25. 19 | */ 20 | public class KcpLockStepSynchronizationClient implements KcpListener 21 | { 22 | 23 | public static void main(String[] args) { 24 | String ip = "49.232.119.183"; 25 | if(args.length>0){ 26 | ip = args[0]; 27 | } 28 | int number= 1; 29 | if(args.length>1){ 30 | number = Integer.parseInt(args[1]); 31 | } 32 | 33 | KcpClient kcpClient = new KcpClient(); 34 | 35 | 36 | ChannelConfig channelConfig = new ChannelConfig(); 37 | channelConfig.nodelay(true,40,2,true); 38 | channelConfig.setSndwnd(300); 39 | channelConfig.setRcvwnd(300); 40 | channelConfig.setMtu(500); 41 | //channelConfig.setFecDataShardCount(10); 42 | //channelConfig.setFecParityShardCount(3); 43 | channelConfig.setAckNoDelay(false); 44 | channelConfig.setCrc32Check(true); 45 | channelConfig.setTimeoutMillis(10000); 46 | 47 | kcpClient.init(channelConfig); 48 | KcpLockStepSynchronizationClient lockStepSynchronizationClient = new KcpLockStepSynchronizationClient(); 49 | 50 | for (int i = 0; i < number; i++) { 51 | kcpClient.connect(new InetSocketAddress(ip, 10009), channelConfig, lockStepSynchronizationClient); 52 | } 53 | 54 | TimerThreadPool.scheduleWithFixedDelay(() -> { 55 | long inSegs = Snmp.snmp.InSegs.longValue(); 56 | if(inSegs==0){ 57 | inSegs = 1; 58 | } 59 | System.out.println("每秒收包"+ (Snmp.snmp.InBytes.longValue()/1024.0/1024.0*8.0)+" M"+" 丢包率 "+((double)Snmp.snmp.LostSegs.longValue()/inSegs)); 60 | System.out.println("每秒发包"+ (Snmp.snmp.OutBytes.longValue()/1024.0/1024.0*8.0)+" M"); 61 | System.out.println(Snmp.snmp.toString()); 62 | System.out.println(); 63 | 64 | Snmp.snmp = new Snmp(); 65 | },1000); 66 | 67 | } 68 | 69 | 70 | 71 | 72 | @Override 73 | public void onConnected(Ukcp ukcp) 74 | { 75 | //模拟按键事件 76 | TimerThreadPool.scheduleWithFixedDelay(() -> { 77 | ByteBuf byteBuf = ByteBufAllocator.DEFAULT.directBuffer(20); 78 | byteBuf.writeBytes(new byte[20]); 79 | ukcp.write(byteBuf); 80 | byteBuf.release(); 81 | },50); 82 | } 83 | 84 | @Override 85 | public void handleReceive(ByteBuf byteBuf, Ukcp ukcp) { 86 | 87 | } 88 | 89 | @Override 90 | public void handleException(Throwable ex, Ukcp ukcp) { 91 | ex.printStackTrace(); 92 | 93 | } 94 | 95 | @Override 96 | public void handleClose(Ukcp ukcp) { 97 | System.out.println("连接断开了"+ukcp.user().getRemoteAddress()); 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /kcp-lockStepSynchronization/src/main/java/test/KcpLockStepSynchronizationServer.java: -------------------------------------------------------------------------------- 1 | package test; 2 | 3 | import com.backblaze.erasure.fec.Snmp; 4 | import io.netty.buffer.ByteBuf; 5 | import io.netty.buffer.ByteBufAllocator; 6 | import kcp.ChannelConfig; 7 | import kcp.KcpListener; 8 | import kcp.KcpServer; 9 | import kcp.Ukcp; 10 | 11 | /** 12 | * 模拟帧同步测试吞吐和流量 13 | * 50ms一帧 14 | * Created by JinMiao 15 | * 2019-06-25. 16 | */ 17 | public class KcpLockStepSynchronizationServer implements KcpListener 18 | { 19 | 20 | private RoomManager roomManager; 21 | 22 | 23 | public static void main(String[] args) { 24 | KcpLockStepSynchronizationServer kcpLockStepSynchronizationServer = new KcpLockStepSynchronizationServer(); 25 | ChannelConfig channelConfig = new ChannelConfig(); 26 | channelConfig.nodelay(true,40,2,true); 27 | channelConfig.setSndwnd(300); 28 | channelConfig.setRcvwnd(300); 29 | channelConfig.setMtu(500); 30 | //channelConfig.setFecDataShardCount(10); 31 | //channelConfig.setFecParityShardCount(3); 32 | channelConfig.setAckNoDelay(false); 33 | channelConfig.setCrc32Check(true); 34 | channelConfig.setTimeoutMillis(10000); 35 | KcpServer kcpServer = new KcpServer(); 36 | kcpServer.init(kcpLockStepSynchronizationServer, channelConfig, 10009); 37 | 38 | kcpLockStepSynchronizationServer.roomManager = new RoomManager(); 39 | 40 | 41 | TimerThreadPool.scheduleWithFixedDelay(() -> { 42 | try { 43 | long inSegs = Snmp.snmp.InSegs.longValue(); 44 | if(inSegs==0){ 45 | inSegs = 1; 46 | } 47 | System.out.println("每秒收包"+ (Snmp.snmp.InBytes.longValue()/1024.0/1024.0*8.0)+" M"+" 丢包率 "+((double)Snmp.snmp.LostSegs.longValue()/inSegs)); 48 | System.out.println("每秒发包"+ (Snmp.snmp.OutBytes.longValue()/1024.0/1024.0*8.0)+" M"); 49 | System.out.println(Snmp.snmp.toString()); 50 | System.out.println(); 51 | Snmp.snmp = new Snmp(); 52 | 53 | }catch (Exception e){ 54 | e.printStackTrace(); 55 | } 56 | },1000); 57 | } 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | @Override 66 | public void onConnected(Ukcp ukcp) { 67 | System.out.println("有连接进来"+ukcp.user()); 68 | 69 | Player player = new Player(new IWriter() { 70 | @Override 71 | public void write(ByteBuf byteBuf) { 72 | ukcp.write(byteBuf); 73 | byteBuf.release(); 74 | } 75 | }); 76 | ukcp.user().setCache(player); 77 | roomManager.joinRoom(player); 78 | } 79 | 80 | @Override 81 | public void handleReceive(ByteBuf byteBuf, Ukcp ukcp) { 82 | //System.out.println("收到消息"+ukcp.user()); 83 | Player player = ukcp.user().getCache(); 84 | Room room = roomManager.getRoom(player.getId()); 85 | ByteBuf byteBufAllocator = ByteBufAllocator.DEFAULT.directBuffer(20); 86 | byteBuf.readBytes(byteBufAllocator); 87 | byteBufAllocator.readerIndex(0); 88 | byteBufAllocator.writerIndex(20); 89 | room.getiMessageExecutor().execute(() ->{ 90 | player.getMessages().add(byteBufAllocator); 91 | } 92 | ); 93 | } 94 | 95 | @Override 96 | public void handleException(Throwable ex, Ukcp ukcp) { 97 | ex.printStackTrace(); 98 | } 99 | 100 | @Override 101 | public void handleClose(Ukcp ukcp) { 102 | Player player = ukcp.user().getCache(); 103 | roomManager.remove(player.getId()); 104 | System.out.println("连接断开了"+ukcp.user().getRemoteAddress()); 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /kcp-lockStepSynchronization/src/main/java/test/Player.java: -------------------------------------------------------------------------------- 1 | package test; 2 | 3 | import io.netty.buffer.ByteBuf; 4 | import kcp.Ukcp; 5 | 6 | import java.util.ArrayList; 7 | import java.util.List; 8 | import java.util.concurrent.atomic.AtomicInteger; 9 | 10 | /** 11 | * Created by JinMiao 12 | * 2019-07-08. 13 | */ 14 | public class Player { 15 | 16 | private static final AtomicInteger idGen = new AtomicInteger(); 17 | private IWriter iWriter; 18 | 19 | private int id ; 20 | 21 | private List messages= new ArrayList<>(); 22 | 23 | 24 | public Player(IWriter iWriter) { 25 | this.iWriter = iWriter; 26 | this.id = idGen.incrementAndGet(); 27 | } 28 | 29 | 30 | public int getId() { 31 | return id; 32 | } 33 | 34 | public void setId(int id) { 35 | this.id = id; 36 | } 37 | 38 | public List getMessages() { 39 | return messages; 40 | } 41 | 42 | 43 | public void write(ByteBuf byteBuf){ 44 | iWriter.write(byteBuf); 45 | } 46 | 47 | 48 | 49 | 50 | 51 | } 52 | -------------------------------------------------------------------------------- /kcp-lockStepSynchronization/src/main/java/test/Room.java: -------------------------------------------------------------------------------- 1 | package test; 2 | 3 | import io.netty.buffer.ByteBuf; 4 | import io.netty.buffer.ByteBufAllocator; 5 | import threadPool.ITask; 6 | import threadPool.IMessageExecutor; 7 | 8 | import java.util.Map; 9 | import java.util.concurrent.ConcurrentHashMap; 10 | 11 | /** 12 | * Created by JinMiao 13 | * 2019-06-26. 14 | */ 15 | public class Room implements Runnable, ITask { 16 | Map players = new ConcurrentHashMap<>(); 17 | 18 | private IMessageExecutor iMessageExecutor; 19 | 20 | private volatile boolean executed; 21 | 22 | public Map getPlayers() { 23 | return players; 24 | } 25 | 26 | public void setPlayers(Map players) { 27 | this.players = players; 28 | } 29 | 30 | public IMessageExecutor getiMessageExecutor() { 31 | return iMessageExecutor; 32 | } 33 | 34 | public void setiMessageExecutor(IMessageExecutor iMessageExecutor) { 35 | this.iMessageExecutor = iMessageExecutor; 36 | } 37 | 38 | public void execute() { 39 | ByteBuf byteBuf = ByteBufAllocator.DEFAULT.directBuffer(2048); 40 | boolean needSend = false; 41 | for (Player player : players.values()) { 42 | for (ByteBuf message : player.getMessages()) { 43 | needSend = true; 44 | byteBuf.writeBytes(message); 45 | //message.release(); 46 | } 47 | player.getMessages().clear(); 48 | } 49 | if(!needSend){ 50 | byteBuf.release(); 51 | return; 52 | } 53 | //System.out.println("发送"+byteBuf.writerIndex()+"房间人数"+ players.size()); 54 | for (Player player : players.values()) { 55 | ByteBuf b = byteBuf.retain(); 56 | player.write(b); 57 | } 58 | byteBuf.release(); 59 | } 60 | 61 | @Override 62 | public void run() { 63 | iMessageExecutor.execute(this); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /kcp-lockStepSynchronization/src/main/java/test/RoomManager.java: -------------------------------------------------------------------------------- 1 | package test; 2 | 3 | import threadPool.disruptor.DisruptorExecutorPool; 4 | 5 | import java.util.Map; 6 | import java.util.concurrent.ConcurrentHashMap; 7 | 8 | /** 9 | * Created by JinMiao 10 | * 2020/7/17. 11 | */ 12 | public class RoomManager { 13 | Map playerRooms = new ConcurrentHashMap<>(); 14 | 15 | DisruptorExecutorPool disruptorExecutorPool = new DisruptorExecutorPool(1); 16 | 17 | public void remove(Integer playerId){ 18 | this.playerRooms.remove(playerId); 19 | 20 | } 21 | 22 | 23 | public Room getRoom(Integer playerId){ 24 | return playerRooms.get(playerId); 25 | } 26 | 27 | 28 | 29 | public synchronized void joinRoom(Player player){ 30 | Room room = null; 31 | for (Room value : playerRooms.values()) { 32 | if(value.getPlayers().size()==8) 33 | { 34 | continue; 35 | } 36 | if(room==null){ 37 | room = value; 38 | continue; 39 | } 40 | if(room.getPlayers().size()>value.getPlayers().size()){ 41 | room = value; 42 | } 43 | } 44 | if(room==null){ 45 | room = new Room(); 46 | room.setiMessageExecutor(disruptorExecutorPool.getIMessageExecutor()); 47 | TimerThreadPool.scheduleWithFixedDelay(room,50); 48 | } 49 | playerRooms.put(player.getId(),room); 50 | room.getPlayers().put(player.getId(),player); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /kcp-lockStepSynchronization/src/main/java/test/TcpLockStepSynchronizationClient.java: -------------------------------------------------------------------------------- 1 | package test; 2 | 3 | import io.netty.buffer.ByteBuf; 4 | import io.netty.buffer.ByteBufAllocator; 5 | import io.netty.channel.ChannelHandler; 6 | import io.netty.channel.ChannelHandlerContext; 7 | import io.netty.channel.SimpleChannelInboundHandler; 8 | import test.tcp.NetConnector; 9 | import test.tcp.TcpChannelInitializer; 10 | 11 | import java.net.InetSocketAddress; 12 | 13 | /** 14 | * Created by JinMiao 15 | * 2020/7/17. 16 | */ 17 | @ChannelHandler.Sharable 18 | public class TcpLockStepSynchronizationClient extends SimpleChannelInboundHandler { 19 | 20 | public static void main(String[] args) { 21 | TcpLockStepSynchronizationClient tcpLockStepSynchronizationClient = new TcpLockStepSynchronizationClient(); 22 | String ip = "127.0.0.1"; 23 | if(args.length>0){ 24 | ip = args[0]; 25 | } 26 | int number= 100; 27 | if(args.length>1){ 28 | number = Integer.parseInt(args[1]); 29 | } 30 | NetConnector netConnector = new NetConnector(new TcpChannelInitializer(tcpLockStepSynchronizationClient)); 31 | try { 32 | for (int i = 0; i < number; i++) { 33 | netConnector.connect(new InetSocketAddress(ip,11009)); 34 | } 35 | } catch (InterruptedException e) { 36 | e.printStackTrace(); 37 | } 38 | } 39 | 40 | 41 | @Override 42 | public void channelActive(ChannelHandlerContext ctx) throws Exception { 43 | //模拟按键事件 44 | TimerThreadPool.scheduleWithFixedDelay(() -> { 45 | ByteBuf byteBuf = ByteBufAllocator.DEFAULT.directBuffer(20); 46 | byteBuf.writeBytes(new byte[20]); 47 | ctx.channel().writeAndFlush(byteBuf); 48 | //byteBuf.release(); 49 | },50); 50 | 51 | } 52 | 53 | @Override 54 | public void channelInactive(ChannelHandlerContext ctx) throws Exception { 55 | System.out.println("连接断开了"+ctx.channel().remoteAddress()); 56 | } 57 | 58 | 59 | @Override 60 | public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { 61 | cause.printStackTrace(); 62 | } 63 | 64 | @Override 65 | protected void channelRead0(ChannelHandlerContext channelHandlerContext, ByteBuf byteBuf) throws Exception { 66 | 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /kcp-lockStepSynchronization/src/main/java/test/TcpLockStepSynchronizationServer.java: -------------------------------------------------------------------------------- 1 | package test; 2 | 3 | import io.netty.buffer.ByteBuf; 4 | import io.netty.buffer.ByteBufAllocator; 5 | import io.netty.channel.ChannelHandler; 6 | import io.netty.channel.ChannelHandlerContext; 7 | import io.netty.channel.SimpleChannelInboundHandler; 8 | import io.netty.util.Attribute; 9 | import io.netty.util.AttributeKey; 10 | import test.tcp.NetAcceptor; 11 | import test.tcp.TcpChannelInitializer; 12 | 13 | import java.net.InetSocketAddress; 14 | 15 | /** 16 | * Created by JinMiao 17 | * 2020/7/17. 18 | */ 19 | @ChannelHandler.Sharable 20 | public class TcpLockStepSynchronizationServer extends SimpleChannelInboundHandler { 21 | 22 | 23 | private static final RoomManager roomManager = new RoomManager(); 24 | public static void main(String[] args) { 25 | TcpLockStepSynchronizationServer tcpLockStepSynchronizationServer = new TcpLockStepSynchronizationServer(); 26 | TcpChannelInitializer tcpChannelInitializer = new TcpChannelInitializer(tcpLockStepSynchronizationServer); 27 | new NetAcceptor(tcpChannelInitializer,new InetSocketAddress(11009)); 28 | 29 | } 30 | 31 | public static final AttributeKey playerAttributeKey = AttributeKey.newInstance("player"); 32 | 33 | 34 | @Override 35 | public void channelActive(ChannelHandlerContext ctx) throws Exception { 36 | System.out.println("有连接进来"+ctx.channel().remoteAddress()); 37 | Player player = new Player(byteBuf -> ctx.channel().writeAndFlush(byteBuf)); 38 | Attribute playerAttribute = ctx.channel().attr(playerAttributeKey); 39 | playerAttribute.set(player); 40 | roomManager.joinRoom(player); 41 | } 42 | 43 | 44 | private Player getPlayer(ChannelHandlerContext ctx){ 45 | Attribute playerAttribute = ctx.channel().attr(playerAttributeKey); 46 | return playerAttribute.get(); 47 | } 48 | 49 | @Override 50 | public void channelInactive(ChannelHandlerContext ctx) throws Exception { 51 | Player player = getPlayer(ctx); 52 | roomManager.remove(player.getId()); 53 | System.out.println("连接断开了"+ctx.channel().remoteAddress()); 54 | 55 | } 56 | 57 | 58 | @Override 59 | public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { 60 | cause.printStackTrace(); 61 | 62 | } 63 | 64 | @Override 65 | protected void channelRead0(ChannelHandlerContext channelHandlerContext, ByteBuf byteBuf) throws Exception { 66 | Player player = getPlayer(channelHandlerContext); 67 | Room room = roomManager.getRoom(player.getId()); 68 | ByteBuf byteBuf1 = ByteBufAllocator.DEFAULT.directBuffer(20); 69 | byteBuf.readBytes(byteBuf1); 70 | byteBuf1.readerIndex(0); 71 | byteBuf1.writerIndex(20); 72 | room.getiMessageExecutor().execute(() ->{ 73 | player.getMessages().add(byteBuf1); 74 | } 75 | ); 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /kcp-lockStepSynchronization/src/main/java/test/TimerThreadPool.java: -------------------------------------------------------------------------------- 1 | package test; 2 | 3 | import io.netty.channel.DefaultEventLoop; 4 | import io.netty.util.HashedWheelTimer; 5 | import io.netty.util.TimerTask; 6 | 7 | import java.util.concurrent.ScheduledFuture; 8 | import java.util.concurrent.ThreadFactory; 9 | import java.util.concurrent.TimeUnit; 10 | import java.util.concurrent.atomic.AtomicInteger; 11 | 12 | /** 13 | * 内部定时器 14 | * Created by JinMiao 15 | * 2020/11/24. 16 | */ 17 | public class TimerThreadPool { 18 | /**定时线程池**/ 19 | //private static final ScheduledThreadPoolExecutor scheduled = new ScheduledThreadPoolExecutor(1,new TimerThreadFactory()); 20 | private static final DefaultEventLoop EVENT_EXECUTORS = new DefaultEventLoop(); 21 | 22 | 23 | 24 | public static ScheduledFuture scheduleWithFixedDelay(Runnable command, long milliseconds){ 25 | return EVENT_EXECUTORS.scheduleWithFixedDelay(command,milliseconds,milliseconds, TimeUnit.MILLISECONDS); 26 | } 27 | 28 | 29 | public void stop() 30 | { 31 | if(!EVENT_EXECUTORS.isShuttingDown()){ 32 | EVENT_EXECUTORS.shutdownGracefully(); 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /kcp-lockStepSynchronization/src/main/java/test/TpsTest/GameTestRoomManager.java: -------------------------------------------------------------------------------- 1 | package test.TpsTest; 2 | 3 | import kcp.Ukcp; 4 | import test.TimerThreadPool; 5 | 6 | import java.util.ArrayList; 7 | import java.util.List; 8 | import java.util.Map; 9 | import java.util.concurrent.ConcurrentHashMap; 10 | 11 | /** 12 | * Created by JinMiao 13 | * 2021/7/13. 14 | */ 15 | public class GameTestRoomManager { 16 | 17 | private Map rooms = new ConcurrentHashMap<>(); 18 | 19 | 20 | public synchronized void addClient(Ukcp ukcp){ 21 | boolean isAdd = false; 22 | for (TestRoom room : rooms.values()) { 23 | if(room.size()==8){ 24 | continue; 25 | } 26 | room.getUkcps().add(ukcp); 27 | isAdd = true; 28 | } 29 | 30 | if(!isAdd){ 31 | TestRoom testRoom = new TestRoom(); 32 | testRoom.getUkcps().add(ukcp); 33 | rooms.put(testRoom.getRoomId(),testRoom); 34 | TimerThreadPool.scheduleWithFixedDelay(testRoom,50); 35 | } 36 | } 37 | 38 | 39 | public Map getRooms() { 40 | return rooms; 41 | } 42 | 43 | public synchronized void remove(Ukcp ukcp){ 44 | for (TestRoom room : rooms.values()) { 45 | if(room.getUkcps().remove(ukcp)){ 46 | if(room.getUkcps().isEmpty()){ 47 | rooms.remove(room.getRoomId()); 48 | } 49 | return; 50 | } 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /kcp-lockStepSynchronization/src/main/java/test/TpsTest/KcpGameTestServer.java: -------------------------------------------------------------------------------- 1 | package test.TpsTest; 2 | 3 | import com.backblaze.erasure.fec.Snmp; 4 | import io.netty.buffer.ByteBuf; 5 | import io.netty.buffer.ByteBufAllocator; 6 | import kcp.ChannelConfig; 7 | import kcp.KcpListener; 8 | import kcp.KcpServer; 9 | import kcp.Ukcp; 10 | import test.*; 11 | 12 | /** 13 | * 14 | * Created by JinMiao 15 | * 2019-06-25. 16 | */ 17 | public class KcpGameTestServer implements KcpListener 18 | { 19 | 20 | private GameTestRoomManager roomManager; 21 | 22 | 23 | public static void main(String[] args) { 24 | KcpGameTestServer kcpGameTestServer = new KcpGameTestServer(); 25 | ChannelConfig channelConfig = new ChannelConfig(); 26 | channelConfig.nodelay(true,40,2,true); 27 | channelConfig.setSndwnd(300); 28 | channelConfig.setRcvwnd(300); 29 | channelConfig.setMtu(500); 30 | //channelConfig.setFecDataShardCount(10); 31 | //channelConfig.setFecParityShardCount(3); 32 | channelConfig.setAckNoDelay(false); 33 | channelConfig.setCrc32Check(true); 34 | channelConfig.setTimeoutMillis(10000); 35 | KcpServer kcpServer = new KcpServer(); 36 | kcpServer.init(kcpGameTestServer, channelConfig, 10019); 37 | 38 | kcpGameTestServer.roomManager = new GameTestRoomManager(); 39 | 40 | 41 | TimerThreadPool.scheduleWithFixedDelay(() -> { 42 | try { 43 | long inSegs = Snmp.snmp.InSegs.longValue(); 44 | if(inSegs==0){ 45 | inSegs = 1; 46 | } 47 | System.out.println("每秒收包"+ (Snmp.snmp.InBytes.longValue()/1024.0/1024.0*8.0)+" M"+" 丢包率 "+((double)Snmp.snmp.LostSegs.longValue()/inSegs)); 48 | System.out.println("每秒发包"+ (Snmp.snmp.OutBytes.longValue()/1024.0/1024.0*8.0)+" M"); 49 | System.out.println("房间数 "+kcpGameTestServer.roomManager.getRooms().size()); 50 | int playerCount = 0; 51 | for (TestRoom value : kcpGameTestServer.roomManager.getRooms().values()) { 52 | playerCount+=value.size(); 53 | } 54 | System.out.println("总人数数 "+playerCount); 55 | System.out.println(Snmp.snmp.toString()); 56 | System.out.println(); 57 | Snmp.snmp = new Snmp(); 58 | 59 | }catch (Exception e){ 60 | e.printStackTrace(); 61 | } 62 | },1000); 63 | } 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | @Override 72 | public void onConnected(Ukcp ukcp) { 73 | System.out.println("有连接进来"+ukcp.user()); 74 | TpsChannelServerCache tpsChannelServerCache = new TpsChannelServerCache(); 75 | ukcp.user().setCache(tpsChannelServerCache); 76 | roomManager.addClient(ukcp); 77 | } 78 | 79 | @Override 80 | public void handleReceive(ByteBuf byteBuf, Ukcp ukcp) { 81 | //System.out.println("收到消息"+ukcp.user()); 82 | TpsChannelServerCache tpsChannelServerCache = ukcp.user().getCache(); 83 | int packId = byteBuf.readInt(); 84 | tpsChannelServerCache.addPackId(packId); 85 | } 86 | 87 | @Override 88 | public void handleException(Throwable ex, Ukcp ukcp) { 89 | ex.printStackTrace(); 90 | } 91 | 92 | @Override 93 | public void handleClose(Ukcp ukcp) { 94 | roomManager.remove(ukcp); 95 | System.out.println("连接断开了"+ukcp.user().getRemoteAddress()); 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /kcp-lockStepSynchronization/src/main/java/test/TpsTest/TestRoom.java: -------------------------------------------------------------------------------- 1 | package test.TpsTest; 2 | 3 | import io.netty.buffer.ByteBuf; 4 | import io.netty.buffer.ByteBufAllocator; 5 | import kcp.Ukcp; 6 | 7 | import java.util.ArrayList; 8 | import java.util.List; 9 | import java.util.concurrent.ConcurrentSkipListSet; 10 | import java.util.concurrent.CopyOnWriteArrayList; 11 | import java.util.concurrent.atomic.AtomicInteger; 12 | 13 | /** 14 | * Created by JinMiao 15 | * 2021/7/13. 16 | */ 17 | public class TestRoom implements Runnable{ 18 | private static final AtomicInteger roomIdInc = new AtomicInteger(); 19 | 20 | private int roomId; 21 | 22 | public TestRoom() { 23 | this.roomId = roomIdInc.incrementAndGet(); 24 | } 25 | 26 | private CopyOnWriteArrayList ukcps = new CopyOnWriteArrayList<>(); 27 | 28 | public int size(){ 29 | return ukcps.size(); 30 | } 31 | 32 | public int getRoomId() { 33 | return roomId; 34 | } 35 | 36 | 37 | @Override 38 | public void run() { 39 | int packIdCount = 0; 40 | for (Ukcp ukcp : ukcps) { 41 | TpsChannelServerCache tpsChannelServerCache = ukcp.user().getCache(); 42 | packIdCount+=tpsChannelServerCache.size(); 43 | } 44 | packIdCount= packIdCount*44; 45 | 46 | for (Ukcp ukcp : ukcps) { 47 | TpsChannelServerCache tpsChannelServerCache = ukcp.user().getCache(); 48 | List packIds = tpsChannelServerCache.getSendPackIds(); 49 | int size = packIds.size(); 50 | ByteBuf byteBuf = ByteBufAllocator.DEFAULT.directBuffer(packIdCount+4*size+2); 51 | byteBuf.writeShort(size); 52 | for (Integer packId : packIds) { 53 | byteBuf.writeInt(packId); 54 | } 55 | byteBuf.writeBytes(new byte[packIdCount]); 56 | 57 | ukcp.write(byteBuf); 58 | byteBuf.release(); 59 | } 60 | } 61 | 62 | public CopyOnWriteArrayList getUkcps() { 63 | return ukcps; 64 | } 65 | 66 | public void setUkcps(CopyOnWriteArrayList ukcps) { 67 | this.ukcps = ukcps; 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /kcp-lockStepSynchronization/src/main/java/test/TpsTest/Tps.java: -------------------------------------------------------------------------------- 1 | package test.TpsTest; 2 | 3 | import javax.print.attribute.standard.PrinterURI; 4 | import java.util.concurrent.atomic.AtomicLong; 5 | import java.util.concurrent.atomic.LongAdder; 6 | 7 | /** 8 | * Created by JinMiao 9 | * 2021/7/13. 10 | */ 11 | public class Tps { 12 | 13 | private int cmd ; 14 | private LongAdder send; 15 | private LongAdder recieve; 16 | private float succRate; 17 | private AtomicLong maxDelay; 18 | private LongAdder delay; 19 | private AtomicLong minDelay; 20 | private LongAdder sumDelay; 21 | 22 | 23 | public Tps(int cmd, LongAdder send, LongAdder recieve, float succRate, AtomicLong maxDelay, LongAdder delay, AtomicLong minDelay, LongAdder sumDelay) { 24 | this.cmd = cmd; 25 | this.send = send; 26 | this.recieve = recieve; 27 | this.succRate = succRate; 28 | this.maxDelay = maxDelay; 29 | this.maxDelay.set(Integer.MIN_VALUE); 30 | this.delay = delay; 31 | this.minDelay = minDelay; 32 | this.minDelay.set(Integer.MAX_VALUE); 33 | this.sumDelay = sumDelay; 34 | } 35 | 36 | 37 | public int getCmd() { 38 | return cmd; 39 | } 40 | 41 | public void setCmd(int cmd) { 42 | this.cmd = cmd; 43 | } 44 | 45 | public LongAdder getSend() { 46 | return send; 47 | } 48 | 49 | public void setSend(LongAdder send) { 50 | this.send = send; 51 | } 52 | 53 | public LongAdder getRecieve() { 54 | return recieve; 55 | } 56 | 57 | public void setRecieve(LongAdder recieve) { 58 | this.recieve = recieve; 59 | } 60 | 61 | public float getSuccRate() { 62 | return succRate; 63 | } 64 | 65 | public void setSuccRate(float succRate) { 66 | this.succRate = succRate; 67 | } 68 | 69 | 70 | public AtomicLong getMaxDelay() { 71 | return maxDelay; 72 | } 73 | 74 | public void setMaxDelay(AtomicLong maxDelay) { 75 | this.maxDelay = maxDelay; 76 | } 77 | 78 | public LongAdder getDelay() { 79 | return delay; 80 | } 81 | 82 | public void setDelay(LongAdder delay) { 83 | this.delay = delay; 84 | } 85 | 86 | public AtomicLong getMinDelay() { 87 | return minDelay; 88 | } 89 | 90 | public void setMinDelay(AtomicLong minDelay) { 91 | this.minDelay = minDelay; 92 | } 93 | 94 | public LongAdder getSumDelay() { 95 | return sumDelay; 96 | } 97 | 98 | public void setSumDelay(LongAdder sumDelay) { 99 | this.sumDelay = sumDelay; 100 | } 101 | 102 | @Override 103 | public String toString() { 104 | return "Tps{" + 105 | "cmd=" + cmd + 106 | ", send=" + send + 107 | ", recieve=" + recieve + 108 | ", succRate=" + succRate + 109 | ", maxDelay=" + maxDelay + 110 | ", delay=" + delay + 111 | ", minDelay=" + minDelay + 112 | ", sumDelay=" + sumDelay + 113 | '}'; 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /kcp-lockStepSynchronization/src/main/java/test/TpsTest/TpsChannelClientCache.java: -------------------------------------------------------------------------------- 1 | package test.TpsTest; 2 | 3 | import java.util.HashMap; 4 | import java.util.Map; 5 | 6 | /** 7 | * Created by JinMiao 8 | * 2021/7/13. 9 | */ 10 | public class TpsChannelClientCache { 11 | 12 | private int index; 13 | private Map packetTime = new HashMap<>(); 14 | 15 | public int getIndex() { 16 | return index; 17 | } 18 | 19 | public void setIndex(int index) { 20 | this.index = index; 21 | } 22 | 23 | public Map getPacketTime() { 24 | return packetTime; 25 | } 26 | 27 | public void setPacketTime(Map packetTime) { 28 | this.packetTime = packetTime; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /kcp-lockStepSynchronization/src/main/java/test/TpsTest/TpsChannelServerCache.java: -------------------------------------------------------------------------------- 1 | package test.TpsTest; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | /** 7 | * Created by JinMiao 8 | * 2021/7/13. 9 | */ 10 | public class TpsChannelServerCache { 11 | private List sendPackIds = new ArrayList<>(); 12 | 13 | 14 | public synchronized void addPackId(int packId){ 15 | this.sendPackIds.add(packId); 16 | } 17 | public synchronized int size(){ 18 | return sendPackIds.size(); 19 | } 20 | 21 | public synchronized List getSendPackIds(){ 22 | List sendPackIds = this.sendPackIds; 23 | this.sendPackIds = new ArrayList<>(); 24 | return sendPackIds; 25 | } 26 | 27 | } 28 | -------------------------------------------------------------------------------- /kcp-lockStepSynchronization/src/main/java/test/TpsTest/TpsCounter.java: -------------------------------------------------------------------------------- 1 | package test.TpsTest; 2 | 3 | import org.slf4j.Logger; 4 | import org.slf4j.LoggerFactory; 5 | 6 | import java.util.Map; 7 | import java.util.concurrent.ConcurrentHashMap; 8 | import java.util.concurrent.atomic.AtomicLong; 9 | import java.util.concurrent.atomic.LongAdder; 10 | 11 | /** 12 | * Created by JinMiao 13 | * 2021/7/13. 14 | */ 15 | public class TpsCounter { 16 | private static final Logger logger = LoggerFactory.getLogger(TpsCounter.class); 17 | 18 | private Map cmds = new ConcurrentHashMap<>(); 19 | 20 | 21 | public void add(int cmd) { 22 | Tps tps = cmds.computeIfAbsent(cmd, integer -> new Tps(cmd, new LongAdder(), new LongAdder(), 0, new AtomicLong(), new LongAdder(), new AtomicLong(), new LongAdder())); 23 | tps.getSend().increment(); 24 | tps.setCmd(cmd); 25 | } 26 | 27 | 28 | 29 | public void set(int cmd,int delay){ 30 | Tps tps = cmds.get(cmd); 31 | tps.getRecieve().increment(); 32 | tps.getMaxDelay().getAndUpdate(operand -> Math.max(delay,operand)); 33 | tps.getSumDelay().add(delay); 34 | tps.getMinDelay().getAndUpdate(operand -> Math.min(delay,operand)); 35 | } 36 | 37 | 38 | 39 | public void count(){ 40 | for (Map.Entry tpsEntry : cmds.entrySet()) { 41 | int cmd = tpsEntry.getKey(); 42 | Tps tps = tpsEntry.getValue(); 43 | long receive = tps.getRecieve().sum(); 44 | long send = tps.getSend().sum(); 45 | if(receive>0){ 46 | long xDelay = tps.getSumDelay().sum() / receive; 47 | float rate = receive *1F/send; 48 | float tpss = 1000; 49 | if(xDelay!=0){ 50 | tpss = 1000/xDelay; 51 | } 52 | logger.error("Tps 命令号:{} 发包数:{} 收包数:{} 成功率:{} 延时(最大:{}ms、平均:{}ms、最小:{}ms)tps:{}",cmd,receive,send,rate,tps.getMaxDelay().get(),xDelay,tps.getMinDelay().get(),tpss); 53 | } 54 | //if(send==receive){ 55 | // cmds.remove(cmd); 56 | //}else{ 57 | //} 58 | cmds.put(cmd,new Tps(cmd, new LongAdder(), new LongAdder(), 0, new AtomicLong(), new LongAdder(), new AtomicLong(), new LongAdder())); 59 | } 60 | 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /kcp-lockStepSynchronization/src/main/java/test/tcp/NetAcceptor.java: -------------------------------------------------------------------------------- 1 | package test.tcp; 2 | 3 | import io.netty.bootstrap.ServerBootstrap; 4 | import io.netty.buffer.PooledByteBufAllocator; 5 | import io.netty.channel.Channel; 6 | import io.netty.channel.ChannelOption; 7 | import io.netty.channel.EventLoopGroup; 8 | import io.netty.channel.epoll.Epoll; 9 | import io.netty.channel.epoll.EpollChannelOption; 10 | import io.netty.channel.epoll.EpollEventLoopGroup; 11 | import io.netty.channel.epoll.EpollServerSocketChannel; 12 | import io.netty.channel.nio.NioEventLoopGroup; 13 | import io.netty.channel.socket.ServerSocketChannel; 14 | import io.netty.channel.socket.nio.NioServerSocketChannel; 15 | 16 | import java.net.InetSocketAddress; 17 | 18 | /** 19 | * 网络监听 20 | * @author King 21 | * 22 | */ 23 | public class NetAcceptor 24 | { 25 | private EventLoopGroup bossGroup; 26 | private EventLoopGroup workerGroup; 27 | private ServerBootstrap b; 28 | private Channel channel; 29 | 30 | public NetAcceptor(TcpChannelInitializer tcpChannelInitializer,InetSocketAddress address) 31 | { 32 | this.b = new ServerBootstrap(); 33 | boolean epoll = Epoll.isAvailable(); 34 | 35 | // handler 36 | this.bossGroup = epoll?new EpollEventLoopGroup():new NioEventLoopGroup(); 37 | this.workerGroup = epoll?new EpollEventLoopGroup():new NioEventLoopGroup(); 38 | 39 | b.childOption(ChannelOption.TCP_NODELAY, true); 40 | b.childOption(ChannelOption.SO_SNDBUF, 2048); 41 | b.childOption(ChannelOption.SO_RCVBUF, 8096); 42 | b.childOption(ChannelOption.SO_KEEPALIVE, true); 43 | b.childOption(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT); 44 | Class serverSocketChannelClass = epoll?EpollServerSocketChannel.class:NioServerSocketChannel.class; 45 | 46 | b.group(bossGroup, workerGroup).channel(serverSocketChannelClass) 47 | .childHandler(tcpChannelInitializer); 48 | try { 49 | this.channel = b.bind(address).sync().channel(); 50 | } catch (Exception e) { 51 | e.printStackTrace(); 52 | this.workerGroup.shutdownGracefully(); 53 | this.bossGroup.shutdownGracefully(); 54 | throw new RuntimeException("error",e); 55 | } 56 | } 57 | 58 | /** 59 | * 关闭服务器用 60 | */ 61 | public void shutdown() 62 | { 63 | this.channel.close(); 64 | this.workerGroup.shutdownGracefully(); 65 | this.bossGroup.shutdownGracefully(); 66 | } 67 | 68 | public Channel getChannel() { 69 | return channel; 70 | } 71 | 72 | } 73 | -------------------------------------------------------------------------------- /kcp-lockStepSynchronization/src/main/java/test/tcp/NetConnector.java: -------------------------------------------------------------------------------- 1 | package test.tcp; 2 | 3 | import io.netty.bootstrap.Bootstrap; 4 | import io.netty.buffer.PooledByteBufAllocator; 5 | import io.netty.channel.Channel; 6 | import io.netty.channel.ChannelFuture; 7 | import io.netty.channel.ChannelOption; 8 | import io.netty.channel.EventLoopGroup; 9 | import io.netty.channel.nio.NioEventLoopGroup; 10 | import io.netty.channel.socket.nio.NioSocketChannel; 11 | 12 | import java.net.InetSocketAddress; 13 | 14 | /** 15 | * 网络连接器 16 | * 17 | * @author King 18 | * 19 | */ 20 | public class NetConnector 21 | { 22 | private EventLoopGroup group; 23 | private Bootstrap b; 24 | private Channel channel; 25 | 26 | 27 | public NetConnector(TcpChannelInitializer tcpChannelInitializer) 28 | { 29 | // netty connector初始化 30 | this.group = new NioEventLoopGroup(); 31 | this.b = new Bootstrap(); 32 | this.b.group(group).channel(NioSocketChannel.class).handler(tcpChannelInitializer); 33 | b.option(ChannelOption.TCP_NODELAY, true); 34 | b.option(ChannelOption.SO_SNDBUF, 2048); 35 | b.option(ChannelOption.SO_RCVBUF, 8096); 36 | b.option(ChannelOption.SO_KEEPALIVE, true); 37 | b.option(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT); 38 | } 39 | 40 | 41 | public Channel connect(InetSocketAddress... address) throws InterruptedException 42 | { 43 | for(InetSocketAddress addr:address) 44 | { 45 | ChannelFuture future = b.connect(addr); 46 | future.sync(); 47 | return future.channel(); 48 | } 49 | return null; 50 | } 51 | 52 | 53 | /** 54 | * 关闭 55 | */ 56 | public void shutdown() { 57 | if(this.channel!=null) 58 | this.channel.close(); 59 | if(this.group!=null) 60 | this.group.shutdownGracefully(); 61 | } 62 | 63 | public Channel getChannel() { 64 | return channel; 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /kcp-lockStepSynchronization/src/main/java/test/tcp/TcpChannelInitializer.java: -------------------------------------------------------------------------------- 1 | package test.tcp; 2 | 3 | import io.netty.channel.Channel; 4 | import io.netty.channel.ChannelInitializer; 5 | import io.netty.channel.ChannelPipeline; 6 | import io.netty.channel.SimpleChannelInboundHandler; 7 | import io.netty.handler.codec.LengthFieldBasedFrameDecoder; 8 | import io.netty.handler.codec.LengthFieldPrepender; 9 | 10 | /** 11 | * Created by JinMiao 12 | * 2020/7/17. 13 | */ 14 | public class TcpChannelInitializer extends ChannelInitializer { 15 | 16 | private SimpleChannelInboundHandler simpleChannelInboundHandler; 17 | 18 | public TcpChannelInitializer(SimpleChannelInboundHandler simpleChannelInboundHandler) { 19 | this.simpleChannelInboundHandler = simpleChannelInboundHandler; 20 | } 21 | 22 | @Override 23 | protected void initChannel(Channel channel) throws Exception { 24 | ChannelPipeline pipeline = channel.pipeline(); 25 | pipeline.addLast("frameDecoder",new LengthFieldBasedFrameDecoder(Integer.MAX_VALUE, 0, 4)); 26 | pipeline.addLast("frameEncoder",new LengthFieldPrepender(4,false)); 27 | pipeline.addLast("handler",simpleChannelInboundHandler); 28 | } 29 | } 30 | --------------------------------------------------------------------------------