├── .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 extends ServerSocketChannel> 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 |
--------------------------------------------------------------------------------