├── .gitignore
├── .idea
├── .gitignore
└── .idea.csharp-kcp
│ ├── .idea
│ ├── $CACHE_FILE$
│ ├── .gitignore
│ ├── contentModel.xml
│ ├── indexLayout.xml
│ ├── inspectionProfiles
│ │ ├── Project_Default.xml
│ │ └── profiles_settings.xml
│ ├── misc.xml
│ ├── modules.xml
│ ├── projectSettingsUpdater.xml
│ └── vcs.xml
│ └── riderModule.iml
├── LICENSE
├── README.md
├── base-kcp
├── DelayPacket.cs
├── Kcp.cs
├── KcpOutput.cs
├── KcpUntils.cs
├── LatencySimulator.cs
├── Program.cs
├── Segment.cs
├── base-kcp.csproj
├── bin
│ ├── Debug
│ │ └── netcoreapp2.2
│ │ │ ├── fec.dll
│ │ │ └── fec.pdb
│ └── Release
│ │ └── netcoreapp2.2
│ │ └── mono_crash.0.1.json
├── fec
│ ├── ByteBufCodingLoop.cs
│ ├── ByteBufCodingLoopBase.cs
│ ├── Fec.cs
│ ├── FecDecode.cs
│ ├── FecEncode.cs
│ ├── FecExpansion.cs
│ ├── FecPacket.cs
│ ├── InputOutputByteBufTableCodingLoop.cs
│ ├── Snmp.cs
│ └── reedsolomon_csharp.dll
└── obj
│ ├── Debug
│ └── netcoreapp2.2
│ │ ├── base-kcp.assets.cache
│ │ ├── base-kcp.csproj.CoreCompileInputs.cache
│ │ └── base-kcp.csproj.FileListAbsolute.txt
│ ├── base-kcp.csproj.nuget.dgspec.json
│ ├── base-kcp.csproj.nuget.g.props
│ ├── base-kcp.csproj.nuget.g.targets
│ ├── project.assets.json
│ └── project.packagespec.json
├── csharp-kcp.sln
├── dotNetty-kcp
├── ChannelConfig.cs
├── ClientChannelHandler.cs
├── ClientConvChannelManager.cs
├── ClientEndPointChannelManager.cs
├── CloseTask.cs
├── CodecOutputList.cs
├── ConnectTask.cs
├── Crc32.cs
├── Crc32OutPut.cs
├── FecOutPut.cs
├── IChannelManager.cs
├── IScheduleTask.cs
├── KcpClient.cs
├── KcpListener.cs
├── KcpOutPutImp.cs
├── KcpServer.cs
├── ReadTask.cs
├── ScheduleTask.cs
├── ServerChannelHandler.cs
├── ServerConvChannelManager.cs
├── ServerEndPointChannelManager.cs
├── Ukcp.cs
├── User.cs
├── WriteTask.cs
├── dotNetty-kcp.csproj
├── obj
│ ├── Debug
│ │ └── netcoreapp2.2
│ │ │ ├── dotNetty-kcp.AssemblyInfo.cs
│ │ │ ├── dotNetty-kcp.AssemblyInfoInputs.cache
│ │ │ ├── dotNetty-kcp.assets.cache
│ │ │ ├── dotNetty-kcp.csproj.FileListAbsolute.txt
│ │ │ └── dotNetty-kcp.csprojAssemblyReference.cache
│ ├── dotNetty-kcp.csproj.nuget.dgspec.json
│ ├── dotNetty-kcp.csproj.nuget.g.props
│ ├── dotNetty-kcp.csproj.nuget.g.targets
│ ├── project.assets.json
│ └── project.packagespec.json
├── queue
│ ├── ConcurrentCircularArrayQueue.cs
│ ├── MpscArrayQueue.cs
│ └── RefArrayAccessUtil.cs
└── thread
│ ├── AbstratcMessageExecutor.cs
│ ├── AtomicBoolean.cs
│ ├── DistuptorMessageExecutor.cs
│ ├── EventLoopScheduleThread.cs
│ ├── ExecutorPool.cs
│ ├── HashedWheelScheduleThread.cs
│ ├── IExecutorPool.cs
│ ├── IMessageExecutor.cs
│ ├── IScheduleThread.cs
│ ├── ITask.cs
│ ├── MessageExecutorTest.cs
│ ├── RingBuffer.cs
│ └── ThreadMessageExecutor.cs
├── example-Kcp
├── KcpRttExampleClient.cs
├── KcpRttExampleServer.cs
├── Program.cs
├── example-Kcp.csproj
└── obj
│ ├── Debug
│ └── netcoreapp2.2
│ │ ├── example-Kcp.AssemblyInfo.cs
│ │ ├── example-Kcp.AssemblyInfoInputs.cache
│ │ ├── example-Kcp.assets.cache
│ │ └── example-Kcp.csproj.FileListAbsolute.txt
│ ├── example-Kcp.csproj.nuget.dgspec.json
│ ├── example-Kcp.csproj.nuget.g.props
│ ├── example-Kcp.csproj.nuget.g.targets
│ ├── project.assets.json
│ └── project.packagespec.json
└── kcp4game
├── GameChannel.cs
├── GameClientService.cs
├── GameConfig.cs
├── GameService.cs
├── GameUntils.cs
├── IGameHandler.cs
├── KcpService.cs
├── Message.cs
├── Program.cs
├── TcpClient.cs
├── TcpServer.cs
├── code
├── ClientChannelInitializer.cs
├── IDistributeManager.cs
├── IHandler.cs
├── IMessageManager.cs
├── IProtoDecodeEncode.cs
├── TcpChannelHandler.cs
├── TcpProtoDecode.cs
└── TcpProtoEncode.cs
├── kcp4game.csproj
└── obj
├── Debug
└── netcoreapp2.2
│ ├── kcp4game.AssemblyInfo.cs
│ ├── kcp4game.AssemblyInfoInputs.cache
│ ├── kcp4game.assets.cache
│ └── kcp4game.csproj.FileListAbsolute.txt
├── kcp4game.csproj.nuget.dgspec.json
├── kcp4game.csproj.nuget.g.props
├── kcp4game.csproj.nuget.g.targets
├── project.assets.json
└── project.packagespec.json
/.gitignore:
--------------------------------------------------------------------------------
1 | example-Kcp/bin
2 | base-kcp/obj
3 | dotNetty-kcp/bin
4 | dotNetty-kcp/obj
5 | example-Kcp/obj
6 | kcp4game/bin
7 | kcp4game/obj
8 | base-kcp/bin
9 | .idea
10 |
--------------------------------------------------------------------------------
/.idea/.gitignore:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/l42111996/csharp-kcp/cc7e42dbc2979c09e87625f5328e466e31fde744/.idea/.gitignore
--------------------------------------------------------------------------------
/.idea/.idea.csharp-kcp/.idea/$CACHE_FILE$:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 | Android
14 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/.idea/.idea.csharp-kcp/.idea/.gitignore:
--------------------------------------------------------------------------------
1 | # Default ignored files
2 | /workspace.xml
--------------------------------------------------------------------------------
/.idea/.idea.csharp-kcp/.idea/contentModel.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
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 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
130 |
131 |
132 |
133 |
134 |
135 |
136 |
137 |
138 |
139 |
140 |
141 |
142 |
143 |
--------------------------------------------------------------------------------
/.idea/.idea.csharp-kcp/.idea/indexLayout.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/.idea/.idea.csharp-kcp/.idea/inspectionProfiles/Project_Default.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/.idea/.idea.csharp-kcp/.idea/inspectionProfiles/profiles_settings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/.idea/.idea.csharp-kcp/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/.idea/.idea.csharp-kcp/.idea/modules.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/.idea/.idea.csharp-kcp/.idea/projectSettingsUpdater.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.idea/.idea.csharp-kcp/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.idea/.idea.csharp-kcp/riderModule.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # csharp-kcp
2 |
3 | 基于DotNetty版本实现的kcp(包含fec功能的实现)
4 |
5 | KCP是一个基于udp的快速可靠协议(rudp),能以比 TCP浪费10%-20%的带宽的代价,换取平均延迟降低 30%-40%,且最大延迟降低三倍的传输效果
6 |
7 | 主要用于构建unity客户端网络层
8 |
9 |
10 | java服务端可使用 https://github.com/l42111996/java-Kcp 完美兼容
11 |
12 | [相关参考资料](https://github.com/l42111996/java-Kcp/blob/master/README.md)
13 |
14 |
15 | # 相关资料
16 |
17 | 1. https://github.com/skywind3000/kcp 原版c版本的kcp
18 | 2. https://github.com/l42111996/java-Kcp 基于netty的java版本kcp,完美兼容,快速构建游戏网络层
19 |
20 | #交流
21 |
22 | QQ:526167774
23 |
--------------------------------------------------------------------------------
/base-kcp/DelayPacket.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using DotNetty.Buffers;
3 |
4 | namespace base_kcp
5 | {
6 | public class DelayPacket
7 | {
8 | private long ts;
9 | private IByteBuffer ptr;
10 |
11 |
12 | public void init(IByteBuffer src)
13 | {
14 | this.ptr = src.RetainedSlice();
15 | }
16 |
17 |
18 | public long getTs()
19 | {
20 | return ts;
21 | }
22 |
23 | public void setTs(long ts)
24 | {
25 | this.ts = ts;
26 | }
27 |
28 | public IByteBuffer getPtr()
29 | {
30 | return ptr;
31 | }
32 |
33 | public void setPtr(IByteBuffer ptr)
34 | {
35 | this.ptr = ptr;
36 | }
37 |
38 | public void Release(){
39 | ptr.Release();
40 | }
41 | }
42 | }
--------------------------------------------------------------------------------
/base-kcp/KcpOutput.cs:
--------------------------------------------------------------------------------
1 | using DotNetty.Buffers;
2 |
3 | namespace base_kcp
4 | {
5 | public interface KcpOutput
6 | {
7 | void outPut(IByteBuffer data, Kcp kcp);
8 | }
9 | }
--------------------------------------------------------------------------------
/base-kcp/KcpUntils.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using DotNetty.Common.Utilities;
3 |
4 | namespace base_kcp
5 | {
6 | public class KcpUntils
7 | {
8 | public static long currentMs()
9 | {
10 | return DateTime.Now.Ticks/TimeSpan.TicksPerMillisecond;
11 | }
12 | }
13 | }
--------------------------------------------------------------------------------
/base-kcp/Program.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Runtime.CompilerServices;
3 | using System.Threading;
4 | using DotNetty.Buffers;
5 |
6 | namespace base_kcp
7 | {
8 | class Program
9 | {
10 |
11 |
12 | // static void Main(string[] args)
13 | // {
14 | // Kcp kcp = new Kcp(1,null);
15 | // Console.WriteLine(kcp.currentMs());
16 | // Thread.Sleep(5000);
17 | // Console.WriteLine((uint)kcp.currentMs());
18 | //// var byteBuffer = UnpooledByteBufferAllocator.Default.DirectBuffer(10);
19 | //// byteBuffer.WriteInt(55);
20 | //// Console.WriteLine(byteBuffer.ReadInt());
21 | //
22 | //
23 | // }
24 | }
25 | }
--------------------------------------------------------------------------------
/base-kcp/Segment.cs:
--------------------------------------------------------------------------------
1 | using DotNetty.Buffers;
2 | using DotNetty.Common;
3 |
4 | namespace base_kcp
5 | {
6 | public class Segment
7 | {
8 | private readonly ThreadLocalPool.Handle recyclerHandle;
9 |
10 | /**会话id**/
11 |
12 | /**命令**/
13 | private byte cmd;
14 |
15 | /**message中的segment分片ID(在message中的索引,由大到小,0表示最后一个分片)**/
16 | private short frg;
17 |
18 | /**剩余接收窗口大小(接收窗口大小-接收队列大小)**/
19 | private int wnd;
20 |
21 | /**message发送时刻的时间戳**/
22 | private long ts;
23 |
24 | /**message分片segment的序号**/
25 | private long sn;
26 |
27 | /**待接收消息序号(接收滑动窗口左端)**/
28 | private long una;
29 |
30 | /**下次超时重传的时间戳**/
31 | private long resendts;
32 |
33 | /**该分片的超时重传等待时间**/
34 | private int rto;
35 |
36 | /**收到ack时计算的该分片被跳过的累计次数,即该分片后的包都被对方收到了,达到一定次数,重传当前分片**/
37 | private int fastack;
38 |
39 | /***发送分片的次数,每发送一次加一**/
40 | private int xmit;
41 |
42 | private long ackMask;
43 |
44 | private IByteBuffer data;
45 |
46 | private int ackMaskSize;
47 |
48 | private static readonly ThreadLocalPool RECYCLER =
49 | new ThreadLocalPool(handle =>
50 | {
51 | return new Segment(handle);
52 | });
53 |
54 | private Segment(ThreadLocalPool.Handle recyclerHandle)
55 | {
56 | this.recyclerHandle =recyclerHandle;
57 | }
58 |
59 | public void recycle(bool releaseBuf) {
60 | Conv = 0;
61 | cmd = 0;
62 | frg = 0;
63 | wnd = 0;
64 | ts = 0;
65 | sn = 0;
66 | una = 0;
67 | resendts = 0;
68 | rto = 0;
69 | fastack = 0;
70 | xmit = 0;
71 | ackMask=0;
72 | if (releaseBuf) {
73 | data.Release();
74 | }
75 | data = null;
76 | recyclerHandle.Release(this);
77 | }
78 |
79 | public static Segment createSegment(IByteBufferAllocator byteBufAllocator, int size) {
80 | Segment seg = RECYCLER.Take();
81 | if (size == 0) {
82 | seg.data = byteBufAllocator.DirectBuffer(0, 0);
83 | } else {
84 | seg.data = byteBufAllocator.DirectBuffer(size);
85 | }
86 | return seg;
87 | }
88 |
89 |
90 | public static Segment createSegment(IByteBuffer buf) {
91 | Segment seg = RECYCLER.Take();
92 | seg.data = buf;
93 | return seg;
94 | }
95 |
96 |
97 | public int Conv { get; set; }
98 |
99 | public byte Cmd
100 | {
101 | get => cmd;
102 | set => cmd = value;
103 | }
104 |
105 | public short Frg
106 | {
107 | get => frg;
108 | set => frg = value;
109 | }
110 |
111 | public int Wnd
112 | {
113 | get => wnd;
114 | set => wnd = value;
115 | }
116 |
117 | public long Ts
118 | {
119 | get => ts;
120 | set => ts = value;
121 | }
122 |
123 | public long Sn
124 | {
125 | get => sn;
126 | set => sn = value;
127 | }
128 |
129 | public long Una
130 | {
131 | get => una;
132 | set => una = value;
133 | }
134 |
135 | public long Resendts
136 | {
137 | get => resendts;
138 | set => resendts = value;
139 | }
140 |
141 | public int Rto
142 | {
143 | get => rto;
144 | set => rto = value;
145 | }
146 |
147 | public int Fastack
148 | {
149 | get => fastack;
150 | set => fastack = value;
151 | }
152 |
153 | public int Xmit
154 | {
155 | get => xmit;
156 | set => xmit = value;
157 | }
158 |
159 | public long AckMask
160 | {
161 | get => ackMask;
162 | set => ackMask = value;
163 | }
164 |
165 | public IByteBuffer Data
166 | {
167 | get => data;
168 | set => data = value;
169 | }
170 |
171 | public int AckMaskSize
172 | {
173 | get => ackMaskSize;
174 | set => ackMaskSize = value;
175 | }
176 | }
177 | }
--------------------------------------------------------------------------------
/base-kcp/base-kcp.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Exe
5 | netstandard2.0
6 | base_kcp
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 | fec\reedsolomon_csharp.dll
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/base-kcp/bin/Debug/netcoreapp2.2/fec.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/l42111996/csharp-kcp/cc7e42dbc2979c09e87625f5328e466e31fde744/base-kcp/bin/Debug/netcoreapp2.2/fec.dll
--------------------------------------------------------------------------------
/base-kcp/bin/Debug/netcoreapp2.2/fec.pdb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/l42111996/csharp-kcp/cc7e42dbc2979c09e87625f5328e466e31fde744/base-kcp/bin/Debug/netcoreapp2.2/fec.pdb
--------------------------------------------------------------------------------
/base-kcp/fec/ByteBufCodingLoop.cs:
--------------------------------------------------------------------------------
1 | using DotNetty.Buffers;
2 |
3 | namespace fec
4 | {
5 | public interface ByteBufCodingLoop
6 | {
7 | /**
8 | * Multiplies a subset of rows from a coding matrix by a full set of
9 | * input shards to produce some output shards.
10 | *
11 | * @param matrixRows The rows from the matrix to use.
12 | * @param inputs An array of byte arrays, each of which is one input shard.
13 | * The inputs array may have extra buffers after the ones
14 | * that are used. They will be ignored. The number of
15 | * inputs used is determined by the length of the
16 | * each matrix row.
17 | * @param inputCount The number of input byte arrays.
18 | * @param outputs Byte arrays where the computed shards are stored. The
19 | * outputs array may also have extra, unused, elements
20 | * at the end. The number of outputs computed, and the
21 | * number of matrix rows used, is determined by
22 | * outputCount.
23 | * @param outputCount The number of outputs to compute.
24 | * @param offset The index in the inputs and output of the first byte
25 | * to process.
26 | * @param byteCount The number of bytes to process.
27 | */
28 | void codeSomeShards(byte[][] matrixRows,
29 | IByteBuffer[] inputs,
30 | int inputCount,
31 | IByteBuffer[] outputs,
32 | int outputCount,
33 | int offset,
34 | int byteCount);
35 |
36 | /**
37 | * Multiplies a subset of rows from a coding matrix by a full set of
38 | * input shards to produce some output shards, and checks that the
39 | * the data is those shards matches what's expected.
40 | *
41 | * @param matrixRows The rows from the matrix to use.
42 | * @param inputs An array of byte arrays, each of which is one input shard.
43 | * The inputs array may have extra buffers after the ones
44 | * that are used. They will be ignored. The number of
45 | * inputs used is determined by the length of the
46 | * each matrix row.
47 | * @param inputCount THe number of input byte arrays.
48 | * @param toCheck Byte arrays where the computed shards are stored. The
49 | * outputs array may also have extra, unused, elements
50 | * at the end. The number of outputs computed, and the
51 | * number of matrix rows used, is determined by
52 | * outputCount.
53 | * @param checkCount The number of outputs to compute.
54 | * @param offset The index in the inputs and output of the first byte
55 | * to process.
56 | * @param byteCount The number of bytes to process.
57 | * @param tempBuffer A place to store temporary results. May be null.
58 | */
59 | bool checkSomeShards(byte[][] matrixRows,
60 | IByteBuffer[] inputs,
61 | int inputCount,
62 | byte[][] toCheck,
63 | int checkCount,
64 | int offset,
65 | int byteCount,
66 | byte[] tempBuffer);
67 | }
68 | }
--------------------------------------------------------------------------------
/base-kcp/fec/ByteBufCodingLoopBase.cs:
--------------------------------------------------------------------------------
1 | using DotNetty.Buffers;
2 |
3 | namespace fec
4 | {
5 | public class ByteBufCodingLoopBase : ByteBufCodingLoop
6 | {
7 | public virtual void codeSomeShards(byte[][] matrixRows, IByteBuffer[] inputs, int inputCount,
8 | IByteBuffer[] outputs, int outputCount,
9 | int offset, int byteCount)
10 | {
11 | }
12 |
13 | public virtual bool checkSomeShards(byte[][] matrixRows, IByteBuffer[] inputs, int inputCount, byte[][] toCheck,
14 | int checkCount,
15 | int offset, int byteCount, byte[] tempBuffer)
16 | {
17 | byte[][] table = Galois.MULTIPLICATION_TABLE;
18 | for (int iByte = offset; iByte < offset + byteCount; iByte++)
19 | {
20 | for (int iOutput = 0; iOutput < checkCount; iOutput++)
21 | {
22 | byte[] matrixRow = matrixRows[iOutput];
23 | int value = 0;
24 | for (int iInput = 0; iInput < inputCount; iInput++)
25 | {
26 | value ^= table[matrixRow[iInput] & 0xFF][inputs[iInput].GetByte(iByte) & 0xFF];
27 | }
28 |
29 | if (toCheck[iOutput][iByte] != (byte) value)
30 | {
31 | return false;
32 | }
33 | }
34 | }
35 |
36 | return true;
37 | }
38 | }
39 | }
--------------------------------------------------------------------------------
/base-kcp/fec/Fec.cs:
--------------------------------------------------------------------------------
1 | namespace fec.fec
2 | {
3 | public class Fec
4 | {
5 | public const int fecHeaderSize = 6,
6 | fecDataSize = 2,
7 | fecHeaderSizePlus2 = fecHeaderSize + fecDataSize, // plus 2B data size
8 | typeData = 0xf1,
9 | typeParity = 0xf2;
10 | }
11 | }
--------------------------------------------------------------------------------
/base-kcp/fec/FecDecode.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using DotNetty.Buffers;
5 | using fec;
6 |
7 | namespace fec.fec
8 | {
9 | public class FecDecode
10 | {
11 | // queue size limit
12 | private readonly int rxlimit;
13 | private readonly int dataShards;
14 |
15 | private int parityShards;
16 |
17 | /** dataShards+parityShards **/
18 | private readonly int shardSize;
19 |
20 | // ordered receive queue
21 | private readonly List rx;
22 |
23 | private readonly IByteBuffer[] decodeCache;
24 |
25 | /**标记是否已经缓存了**/
26 | private readonly bool[] flagCache;
27 |
28 | private readonly ReedSolomon codec;
29 |
30 | private readonly IByteBuffer zeros;
31 |
32 |
33 | public FecDecode(int rxlimit, ReedSolomon codec, int mtu)
34 | {
35 | this.rxlimit = rxlimit;
36 | this.dataShards = codec.getDataShardCount();
37 | this.parityShards = codec.getParityShardCount();
38 | this.shardSize = dataShards + parityShards;
39 |
40 | if (dataShards <= 0 || parityShards <= 0)
41 | {
42 | throw new Exception("dataShards and parityShards can not less than 0");
43 | }
44 |
45 | if (rxlimit < dataShards + parityShards)
46 | {
47 | throw new Exception("");
48 | }
49 |
50 | this.codec = codec;
51 | this.decodeCache = new IByteBuffer[this.shardSize];
52 | this.flagCache = new bool[this.shardSize];
53 | this.rx = new List(rxlimit);
54 |
55 | zeros = PooledByteBufferAllocator.Default.DirectBuffer(mtu);
56 | zeros.WriteBytes(new byte[mtu]);
57 | }
58 |
59 |
60 | public List decode(FecPacket pkt)
61 | {
62 | if (pkt.Flag == Fec.typeParity)
63 | {
64 | Snmp.snmp.FECParityShards++;
65 | }
66 | else
67 | {
68 | Snmp.snmp.FECDataShards++;
69 | }
70 |
71 | int n = rx.Count - 1;
72 | int insertIdx = 0;
73 | for (int i = n; i >= 0; i--)
74 | {
75 | //去重
76 | if (pkt.Seqid == rx[i].Seqid)
77 | {
78 | Snmp.snmp.FECRepeatDataShards++;
79 | pkt.release();
80 | return null;
81 | }
82 |
83 | if (pkt.Seqid > rx[i].Seqid)
84 | {
85 | // insertion
86 | insertIdx = i + 1;
87 | break;
88 | }
89 | }
90 |
91 | //插入 rx中
92 | if (insertIdx == n + 1)
93 | {
94 | this.rx.Add(pkt);
95 | }
96 | else
97 | {
98 | rx.Insert(insertIdx, pkt);
99 | }
100 |
101 | //所有消息列表中的第一个包
102 | // shard range for current packet
103 | long shardBegin = pkt.Seqid - pkt.Seqid % shardSize;
104 | long shardEnd = shardBegin + shardSize - 1;
105 |
106 | //rx数组中的第一个包
107 | // max search range in ordered queue for current shard
108 | int searchBegin = (int) (insertIdx - pkt.Seqid % shardSize);
109 | if (searchBegin < 0)
110 | {
111 | searchBegin = 0;
112 | }
113 |
114 | int searchEnd = searchBegin + shardSize - 1;
115 | if (searchEnd >= rx.Count)
116 | {
117 | searchEnd = rx.Count - 1;
118 | }
119 |
120 | List result = null;
121 | if (searchEnd - searchBegin + 1 >= dataShards)
122 | {
123 | //当前包组的已收到的包数量
124 | int numshard = 0;
125 | //当前包组中属于数据包的数量
126 | int numDataShard = 0;
127 | //搜到第一个包在搜索行中的位置
128 | int first = 0;
129 | //收到的最大包的字节长度
130 | int maxlen = 0;
131 |
132 | // zero cache
133 | IByteBuffer[] shards = decodeCache;
134 | bool[] shardsflag = flagCache;
135 | for (int i = 0; i < shards.Length; i++)
136 | {
137 | shards[i] = null;
138 | shardsflag[i] = false;
139 | }
140 |
141 | for (int i = searchBegin; i <= searchEnd; i++)
142 | {
143 | FecPacket fecPacket = rx[i];
144 | long seqid = fecPacket.Seqid;
145 | if (seqid > shardEnd)
146 | break;
147 | if (seqid < shardBegin)
148 | continue;
149 | shards[(int) (seqid % shardSize)] = fecPacket.Data;
150 | shardsflag[(int) (seqid % shardSize)] = true;
151 | numshard++;
152 | if (fecPacket.Flag == Fec.typeData)
153 | {
154 | numDataShard++;
155 | }
156 |
157 | if (numshard == 1)
158 | {
159 | first = i;
160 | }
161 |
162 | if (fecPacket.Data.ReadableBytes> maxlen)
163 | {
164 | maxlen = fecPacket.Data.ReadableBytes;
165 | }
166 | }
167 |
168 | if (numDataShard == dataShards)
169 | {
170 | freeRange(first, numshard, rx);
171 | }
172 | else if (numshard >= dataShards)
173 | {
174 | for (int i = 0; i < shards.Length; i++)
175 | {
176 | IByteBuffer shard = shards[i];
177 | //如果数据不存在 用0填充起来
178 | if (shard == null)
179 | {
180 | shards[i] = zeros.Copy(0, maxlen);
181 | shards[i].SetWriterIndex(maxlen);
182 | continue;
183 | }
184 |
185 | int left = maxlen - shard.ReadableBytes;
186 | if (left > 0)
187 | {
188 | shard.WriteBytes(this.zeros, left);
189 | zeros.ResetReaderIndex();
190 | // zeros.resetReaderIndex();
191 | }
192 | }
193 |
194 | codec.decodeMissing(shards, shardsflag, 0, maxlen);
195 | result = new List(this.dataShards);
196 | for (int i = 0; i < shardSize; i++)
197 | {
198 | if (shardsflag[i])
199 | {
200 | continue;
201 | }
202 |
203 | IByteBuffer byteBufs = shards[i];
204 | //释放构建的parityShards内存
205 | if (i >= dataShards)
206 | {
207 | byteBufs.Release();
208 | continue;
209 | }
210 |
211 | int packageSize = byteBufs.ReadShort();
212 | if (byteBufs.ReadableBytes < packageSize)
213 | {
214 | //// System.out.println("bytebuf长度: " + byteBufs.writerIndex() + " 读出长度" + packageSize);
215 | // byte[] bytes = new byte[byteBufs.writerIndex()];
216 | // byteBufs.getBytes(0, bytes);
217 | // for (byte aByte :
218 | // bytes) {
219 | // System.out.print("[" + aByte + "] ");
220 | // }
221 | Snmp.snmp.FECErrs++;
222 | }
223 | else
224 | {
225 | Snmp.snmp.FECRecovered++;
226 | }
227 |
228 | //去除fec头标记的消息体长度2字段
229 | byteBufs = byteBufs.Slice(Fec.fecDataSize, packageSize);
230 | //int packageSize =byteBufs.readUnsignedShort();
231 | //byteBufs = byteBufs.slice(0,packageSize);
232 | result.Add(byteBufs);
233 | Snmp.snmp.FECRecovered++;
234 | //int packageSize =byteBufs.getUnsignedShort(0);
235 | ////判断长度
236 | //if(byteBufs.writerIndex()-Fec.fecHeaderSizePlus2>=packageSize&&packageSize>0)
237 | //{
238 | // byteBufs = byteBufs.slice(Fec.fecHeaderSizePlus2,packageSize);
239 | // result.add(byteBufs);
240 | // Snmp.snmp.FECRecovered.incrementAndGet();
241 | //}else{
242 | // System.out.println("bytebuf长度: "+byteBufs.writerIndex()+" 读出长度"+packageSize);
243 | // byte[] bytes = new byte[byteBufs.writerIndex()];
244 | // byteBufs.getBytes(0,bytes);
245 | // for (byte aByte : bytes) {
246 | // System.out.print("["+aByte+"] ");
247 | // }
248 | // Snmp.snmp.FECErrs.incrementAndGet();
249 | //}
250 | }
251 |
252 | freeRange(first, numshard, rx);
253 | }
254 | }
255 |
256 | if (rx.Count> rxlimit)
257 | {
258 | if (rx[0].Flag == Fec.typeData)
259 | {
260 | Snmp.snmp.FECShortShards++;
261 | }
262 |
263 | freeRange(0, 1, rx);
264 | }
265 |
266 | return result;
267 | }
268 |
269 |
270 | public void release(){
271 | this.parityShards=0;
272 | foreach (var fecPacket in this.rx)
273 | {
274 | fecPacket?.release();
275 | }
276 | this.zeros.Release();
277 | }
278 | /**
279 | * 1,回收first后n个bytebuf
280 | * 2,将q的first到first+n之间的数据移除掉
281 | * 3,将尾部的n个数据的data清空
282 | * 4,返回开头到尾部n个数组的对象
283 | *
284 | * @param first
285 | * @param n
286 | * @param q
287 | */
288 | private static void freeRange(int first,int n,List q){
289 | for (int i = first; i < first + n; i++) {
290 | q[i].release();
291 | }
292 | //copy(q[first:], q[first+n:])
293 | for (int i = first; i < q.Count; i++) {
294 | int index = i+n;
295 | if(index==q.Count)
296 | break;
297 | q[i]=q[index];
298 | }
299 | //for (int i = 0; i < n; i++) {
300 | // q.get(q.size()-1-i).setData(null);
301 | //}
302 | for (int i = 0; i < n; i++) {
303 | q.RemoveAt(q.Count-1);
304 | }
305 | }
306 | }
307 | }
--------------------------------------------------------------------------------
/base-kcp/fec/FecEncode.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using DotNetty.Buffers;
3 | using fec;
4 |
5 | namespace fec.fec
6 | {
7 | public class FecEncode
8 | {
9 | /**消息包长度**/
10 | private readonly int dataShards;
11 |
12 | /**冗余包长度**/
13 | private readonly int parityShards;
14 |
15 | /** dataShards+parityShards **/
16 | private int shardSize;
17 |
18 | //Protect Against Wrapped Sequence numbers
19 | private readonly long paws;
20 |
21 | // next seqid
22 | private long next;
23 |
24 | //count the number of datashards collected
25 | private int shardCount;
26 |
27 | // record maximum data length in datashard
28 | private int maxSize;
29 |
30 | // FEC header offset
31 | private readonly int headerOffset;
32 |
33 | // FEC payload offset
34 | private readonly int payloadOffset;
35 |
36 | //用完需要手动release
37 | private readonly IByteBuffer[] shardCache;
38 | private readonly IByteBuffer[] encodeCache;
39 |
40 | private readonly IByteBuffer zeros;
41 |
42 | private readonly ReedSolomon codec;
43 |
44 | public FecEncode(int headerOffset, ReedSolomon codec, int mtu)
45 | {
46 | this.dataShards = codec.getDataShardCount();
47 | this.parityShards = codec.getParityShardCount();
48 | this.shardSize = this.dataShards + this.parityShards;
49 | //this.paws = (Integer.MAX_VALUE/shardSize - 1) * shardSize;
50 | this.paws = 0xffffffffL / shardSize * shardSize;
51 | this.headerOffset = headerOffset;
52 | this.payloadOffset = headerOffset + Fec.fecHeaderSize;
53 | this.codec = codec;
54 | this.shardCache = new IByteBuffer[shardSize];
55 | this.encodeCache = new IByteBuffer[parityShards];
56 | zeros = PooledByteBufferAllocator.Default.DirectBuffer(mtu);
57 | zeros.WriteBytes(new byte[mtu]);
58 | }
59 |
60 | /**
61 | *
62 | * 使用方法:
63 | * 1,入bytebuf后 把bytebuf发送出去,并释放bytebuf
64 | * 2,判断返回值是否为null,如果不为null发送出去并释放它
65 | *
66 | * headerOffset +6字节fectHead + 2字节bodylenth(lenth-headerOffset-6)
67 | *
68 | * 1,对数据写入头标记为数据类型 markData
69 | * 2,写入消息长度
70 | * 3,获得缓存数据中最大长度,其他的缓存进行扩容到同样长度
71 | * 4,去掉头长度,进行fec编码
72 | * 5,对冗余字节数组进行标记为fec makefec
73 | * 6,返回完整长度
74 | *
75 | * 注意: 传入的bytebuf如果需要释放在传入后手动释放。
76 | * 返回的bytebuf 也需要自己释放
77 | * @param byteBuf
78 | * @return
79 | */
80 | public IByteBuffer[] encode(IByteBuffer byteBuf)
81 | {
82 | markData(byteBuf, headerOffset);
83 | int sz = byteBuf.WriterIndex;
84 | byteBuf.SetShort(payloadOffset, sz - headerOffset - Fec.fecHeaderSizePlus2);
85 | this.shardCache[shardCount] = byteBuf.RetainedDuplicate();
86 | this.shardCount++;
87 | if (sz > this.maxSize)
88 | {
89 | this.maxSize = sz;
90 | }
91 |
92 | if (shardCount != dataShards)
93 | {
94 | return null;
95 | }
96 |
97 | //填充parityShards
98 | for (int i = 0; i < parityShards; i++)
99 | {
100 | IByteBuffer parityByte = PooledByteBufferAllocator.Default.DirectBuffer(this.maxSize);
101 | shardCache[i + dataShards] = parityByte;
102 | encodeCache[i] = parityByte;
103 | markParity(parityByte, headerOffset);
104 | parityByte.SetWriterIndex(this.maxSize);
105 | }
106 |
107 | //按着最大长度不足补充0
108 | for (var i = 0; i < this.dataShards; i++)
109 | {
110 | var shard = shardCache[i];
111 | var left = this.maxSize - shard.WriterIndex;
112 | if (left <= 0)
113 | continue;
114 | //是否需要扩容 会出现吗??
115 | //if(shard.capacity()=this.paws) {
173 | // this.next++;
174 | // //this.next = (this.next + 1) % this.paws;
175 | //}
176 | }
177 | }
178 | }
--------------------------------------------------------------------------------
/base-kcp/fec/FecExpansion.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using DotNetty.Buffers;
3 |
4 | namespace fec
5 | {
6 | public static class FecExpansion
7 | {
8 | private static readonly InputOutputByteBufTableCodingLoop LOOP = new InputOutputByteBufTableCodingLoop();
9 |
10 |
11 | public static void encodeParity(this ReedSolomon reedSolomon, IByteBuffer[] shards, int offset, int byteCount)
12 | {
13 | // Check arguments.
14 | reedSolomon.checkBuffersAndSizes(shards, offset, byteCount);
15 |
16 | // Build the array of output buffers.
17 | IByteBuffer[] outputs = new IByteBuffer [reedSolomon.ParityShardCount];
18 | Array.Copy(shards, reedSolomon.DataShardCount, outputs, 0, reedSolomon.ParityShardCount);
19 | // System.arraycopy(shards, dataShardCount, outputs, 0, parityShardCount);
20 |
21 | // Do the coding.
22 | LOOP.codeSomeShards(
23 | reedSolomon.ParityRows,
24 | shards, reedSolomon.DataShardCount,
25 | outputs, reedSolomon.ParityShardCount,
26 | offset, byteCount);
27 | }
28 |
29 |
30 | /**
31 | * Checks the consistency of arguments passed to public methods.
32 | */
33 | private static void checkBuffersAndSizes(this ReedSolomon reedSolomon, IByteBuffer[] shards, int offset,
34 | int byteCount)
35 | {
36 | // The number of buffers should be equal to the number of
37 | // data shards plus the number of parity shards.
38 | if (shards.Length != reedSolomon.TotalShardCount)
39 | {
40 | throw new Exception("wrong number of shards: " + shards.Length);
41 | }
42 |
43 | // All of the shard buffers should be the same length.
44 | int shardLength = shards[0].ReadableBytes;
45 | for (int i = 1; i < shards.Length; i++)
46 | {
47 | if (shards[i].ReadableBytes != shardLength)
48 | {
49 | throw new Exception("Shards are different sizes");
50 | }
51 | }
52 |
53 | // The offset and byteCount must be non-negative and fit in the buffers.
54 | if (offset < 0)
55 | {
56 | throw new Exception("offset is negative: " + offset);
57 | }
58 |
59 | if (byteCount < 0)
60 | {
61 | throw new Exception("byteCount is negative: " + byteCount);
62 | }
63 |
64 | if (shardLength < offset + byteCount)
65 | {
66 | throw new Exception("buffers to small: " + byteCount + offset);
67 | }
68 | }
69 |
70 |
71 | public static void decodeMissing(this ReedSolomon reedSolomon, IByteBuffer[] shards,
72 | bool[] shardPresent,
73 | int offset,
74 | int byteCount)
75 | {
76 | // Check arguments.
77 | reedSolomon.checkBuffersAndSizes(shards, offset, byteCount);
78 |
79 | // Quick check: are all of the shards present? If so, there's
80 | // nothing to do.
81 | int numberPresent = 0;
82 | for (int i = 0; i < reedSolomon.TotalShardCount; i++)
83 | {
84 | if (shardPresent[i])
85 | {
86 | numberPresent += 1;
87 | }
88 | }
89 |
90 | if (numberPresent == reedSolomon.TotalShardCount)
91 | {
92 | // Cool. All of the shards data data. We don't
93 | // need to do anything.
94 | return;
95 | }
96 |
97 | // More complete sanity check
98 | if (numberPresent < reedSolomon.DataShardCount)
99 | {
100 | throw new Exception("Not enough shards present");
101 | }
102 |
103 | // Pull out the rows of the matrix that correspond to the
104 | // shards that we have and build a square matrix. This
105 | // matrix could be used to generate the shards that we have
106 | // from the original data.
107 | //
108 | // Also, pull out an array holding just the shards that
109 | // correspond to the rows of the submatrix. These shards
110 | // will be the input to the decoding process that re-creates
111 | // the missing data shards.
112 | Matrix subMatrix = new Matrix(reedSolomon.DataShardCount, reedSolomon.DataShardCount);
113 | IByteBuffer[] subShards = new IByteBuffer[reedSolomon.DataShardCount];
114 | {
115 | int subMatrixRow = 0;
116 | for (int matrixRow = 0;
117 | matrixRow < reedSolomon.TotalShardCount && subMatrixRow < reedSolomon.DataShardCount;
118 | matrixRow++)
119 | {
120 | if (shardPresent[matrixRow])
121 | {
122 | for (int c = 0; c < reedSolomon.DataShardCount; c++)
123 | {
124 | subMatrix.set(subMatrixRow, c, reedSolomon.Matrix.get(matrixRow, c));
125 | }
126 |
127 | subShards[subMatrixRow] = shards[matrixRow];
128 | subMatrixRow += 1;
129 | }
130 | }
131 | }
132 |
133 | // Invert the matrix, so we can go from the encoded shards
134 | // back to the original data. Then pull out the row that
135 | // generates the shard that we want to decode. Note that
136 | // since this matrix maps back to the orginal data, it can
137 | // be used to create a data shard, but not a parity shard.
138 | Matrix dataDecodeMatrix = subMatrix.invert();
139 |
140 | // Re-create any data shards that were missing.
141 | //
142 | // The input to the coding is all of the shards we actually
143 | // have, and the output is the missing data shards. The computation
144 | // is done using the special decode matrix we just built.
145 | IByteBuffer[] outputs = new IByteBuffer[reedSolomon.ParityShardCount];
146 | byte[][] matrixRows = new byte [reedSolomon.ParityShardCount][];
147 | int outputCount = 0;
148 | for (int iShard = 0; iShard < reedSolomon.DataShardCount; iShard++)
149 | {
150 | if (!shardPresent[iShard])
151 | {
152 | outputs[outputCount] = shards[iShard];
153 | matrixRows[outputCount] = dataDecodeMatrix.getRow(iShard);
154 | outputCount += 1;
155 | }
156 | }
157 |
158 | LOOP.codeSomeShards(
159 | matrixRows,
160 | subShards, reedSolomon.DataShardCount,
161 | outputs, outputCount,
162 | offset, byteCount);
163 |
164 | // Now that we have all of the data shards intact, we can
165 | // compute any of the parity that is missing.
166 | //
167 | // The input to the coding is ALL of the data shards, including
168 | // any that we just calculated. The output is whichever of the
169 | // data shards were missing.
170 | outputCount = 0;
171 | for (int iShard = reedSolomon.DataShardCount; iShard < reedSolomon.TotalShardCount; iShard++)
172 | {
173 | if (!shardPresent[iShard])
174 | {
175 | outputs[outputCount] = shards[iShard];
176 | matrixRows[outputCount] = reedSolomon.ParityRows[iShard - reedSolomon.DataShardCount];
177 | outputCount += 1;
178 | }
179 | }
180 |
181 | LOOP.codeSomeShards(
182 | matrixRows,
183 | shards, reedSolomon.DataShardCount,
184 | outputs, outputCount,
185 | offset, byteCount);
186 | }
187 | }
188 | }
--------------------------------------------------------------------------------
/base-kcp/fec/FecPacket.cs:
--------------------------------------------------------------------------------
1 | using DotNetty.Buffers;
2 | using DotNetty.Common;
3 |
4 | namespace fec.fec
5 | {
6 | public class FecPacket
7 | {
8 | private long seqid;
9 | private int flag;
10 | private IByteBuffer data;
11 |
12 | private readonly ThreadLocalPool.Handle recyclerHandle;
13 |
14 |
15 | private static readonly ThreadLocalPool fecPacketRecycler =
16 | new ThreadLocalPool(handle => new FecPacket(handle));
17 |
18 | private FecPacket(ThreadLocalPool.Handle recyclerHandle)
19 | {
20 | this.recyclerHandle = recyclerHandle;
21 | }
22 |
23 |
24 | public static FecPacket newFecPacket(IByteBuffer byteBuf)
25 | {
26 | FecPacket pkt = fecPacketRecycler.Take();
27 | pkt.seqid = byteBuf.ReadUnsignedIntLE();
28 | pkt.flag = byteBuf.ReadUnsignedShortLE();
29 | pkt.data = byteBuf.RetainedSlice(byteBuf.ReaderIndex, byteBuf.Capacity - byteBuf.ReaderIndex);
30 | pkt.data.SetWriterIndex(byteBuf.ReadableBytes);
31 | return pkt;
32 | }
33 |
34 |
35 | public void release()
36 | {
37 | this.seqid = 0;
38 | this.flag = 0;
39 | this.data.Release();
40 | this.data = null;
41 | recyclerHandle.Release(this);
42 | }
43 |
44 | public long Seqid
45 | {
46 | get => seqid;
47 | }
48 |
49 | public int Flag
50 | {
51 | get => flag;
52 | }
53 |
54 | public IByteBuffer Data
55 | {
56 | get => data;
57 | }
58 | }
59 | }
--------------------------------------------------------------------------------
/base-kcp/fec/InputOutputByteBufTableCodingLoop.cs:
--------------------------------------------------------------------------------
1 | using DotNetty.Buffers;
2 |
3 | namespace fec
4 | {
5 | public class InputOutputByteBufTableCodingLoop : ByteBufCodingLoopBase
6 | {
7 | public void codeSomeShards(byte[][] matrixRows, IByteBuffer[] inputs, int inputCount, IByteBuffer[] outputs,
8 | int outputCount, int offset, int byteCount)
9 | {
10 | byte[][] table = Galois.MULTIPLICATION_TABLE;
11 |
12 | {
13 | int iInput = 0;
14 | IByteBuffer inputShard = inputs[iInput];
15 | for (int iOutput = 0; iOutput < outputCount; iOutput++)
16 | {
17 | IByteBuffer outputShard = outputs[iOutput];
18 | byte[] matrixRow = matrixRows[iOutput];
19 | byte[] multTableRow = table[matrixRow[iInput] & 0xFF];
20 | for (int iByte = offset; iByte < offset + byteCount; iByte++)
21 | {
22 | outputShard.SetByte(iByte, multTableRow[inputShard.GetByte(iByte) & 0xFF]);
23 | //outputShard[iByte] = multTableRow[inputShard[iByte] & 0xFF];
24 | }
25 | }
26 | }
27 |
28 | for (int iInput = 1; iInput < inputCount; iInput++)
29 | {
30 | IByteBuffer inputShard = inputs[iInput];
31 | for (int iOutput = 0; iOutput < outputCount; iOutput++)
32 | {
33 | IByteBuffer outputShard = outputs[iOutput];
34 | byte[] matrixRow = matrixRows[iOutput];
35 | byte[] multTableRow = table[matrixRow[iInput] & 0xFF];
36 | for (int iByte = offset; iByte < offset + byteCount; iByte++)
37 | {
38 | byte temp = outputShard.GetByte(iByte);
39 | temp ^= multTableRow[inputShard.GetByte(iByte) & 0xFF];
40 | outputShard.SetByte(iByte, temp);
41 | //outputShard[iByte] ^= multTableRow[inputShard[iByte] & 0xFF];
42 | }
43 | }
44 | }
45 | }
46 |
47 |
48 | public bool checkSomeShards(
49 | byte[][] matrixRows,
50 | IByteBuffer[] inputs, int inputCount,
51 | byte[][] toCheck, int checkCount,
52 | int offset, int byteCount,
53 | byte[] tempBuffer)
54 | {
55 | if (tempBuffer == null)
56 | {
57 | return base.checkSomeShards(matrixRows, inputs, inputCount, toCheck, checkCount, offset, byteCount,
58 | null);
59 | }
60 |
61 | // This is actually the code from OutputInputByteTableCodingLoop.
62 | // Using the loops from this class would require multiple temp
63 | // buffers.
64 |
65 | byte[][] table = Galois.MULTIPLICATION_TABLE;
66 | for (int iOutput = 0; iOutput < checkCount; iOutput++)
67 | {
68 | byte[] outputShard = toCheck[iOutput];
69 | byte[] matrixRow = matrixRows[iOutput];
70 | {
71 | int iInput = 0;
72 | IByteBuffer inputShard = inputs[iInput];
73 | byte[] multTableRow = table[matrixRow[iInput] & 0xFF];
74 | for (int iByte = offset; iByte < offset + byteCount; iByte++)
75 | {
76 | tempBuffer[iByte] = multTableRow[inputShard.GetByte(iByte) & 0xFF];
77 | }
78 | }
79 | for (int iInput = 1; iInput < inputCount; iInput++)
80 | {
81 | IByteBuffer inputShard = inputs[iInput];
82 | byte[] multTableRow = table[matrixRow[iInput] & 0xFF];
83 | for (int iByte = offset; iByte < offset + byteCount; iByte++)
84 | {
85 | tempBuffer[iByte] ^= multTableRow[inputShard.GetByte(iByte) & 0xFF];
86 | }
87 | }
88 |
89 | for (int iByte = offset; iByte < offset + byteCount; iByte++)
90 | {
91 | if (tempBuffer[iByte] != outputShard[iByte])
92 | {
93 | return false;
94 | }
95 | }
96 | }
97 |
98 | return true;
99 | }
100 | }
101 | }
--------------------------------------------------------------------------------
/base-kcp/fec/Snmp.cs:
--------------------------------------------------------------------------------
1 | namespace fec
2 | {
3 | public class Snmp
4 | {
5 | // bytes sent from upper level
6 | public int BytesSent;
7 |
8 | // bytes received to upper level
9 | public int BytesReceived;
10 |
11 | // max number of connections ever reached
12 | public int MaxConn;
13 |
14 | // accumulated active open connections
15 | public int ActiveOpens;
16 |
17 | // accumulated passive open connections
18 | public int PassiveOpens;
19 |
20 | // current number of established connections
21 | public int CurrEstab;
22 |
23 | // UDP read errors reported from net.PacketConn
24 | public int InErrs;
25 |
26 | // checksum errors from CRC32
27 | public int InCsumErrors;
28 |
29 | // packet iput errors reported from KCP
30 | public int KCPInErrors;
31 |
32 | // incoming packets count
33 | public int InPkts;
34 |
35 | // outgoing packets count
36 | public int OutPkts;
37 |
38 | // incoming KCP segments
39 | public int InSegs;
40 |
41 | // outgoing KCP segments
42 | public int OutSegs;
43 |
44 | // UDP bytes received
45 | public int InBytes;
46 |
47 | // UDP bytes sent
48 | public int OutBytes;
49 |
50 | // accmulated retransmited segments
51 | public int RetransSegs;
52 |
53 | // accmulated fast retransmitted segments
54 | public int FastRetransSegs;
55 |
56 | // accmulated early retransmitted segments
57 | public int EarlyRetransSegs;
58 |
59 | // number of segs infered as lost
60 | public int LostSegs;
61 |
62 | // number of segs duplicated
63 | public int RepeatSegs;
64 |
65 | // correct packets recovered from FEC
66 | public int FECRecovered;
67 |
68 | // incorrect packets recovered from FEC
69 | public int FECErrs;
70 |
71 | // 收到的 Data数量
72 | public int FECDataShards;
73 |
74 | // 收到的 Parity数量
75 | public int FECParityShards;
76 |
77 | // number of data shards that's not enough for recovery
78 | public int FECShortShards;
79 |
80 | // number of data shards that's not enough for recovery
81 | public int FECRepeatDataShards;
82 |
83 | public static volatile Snmp snmp = new Snmp();
84 |
85 | public override string ToString() {
86 | return "Snmp{" +
87 | "BytesSent=" + BytesSent +
88 | ", BytesReceived=" + BytesReceived +
89 | ", MaxConn=" + MaxConn +
90 | ", ActiveOpens=" + ActiveOpens +
91 | ", PassiveOpens=" + PassiveOpens +
92 | ", CurrEstab=" + CurrEstab +
93 | ", InErrs=" + InErrs +
94 | ", InCsumErrors=" + InCsumErrors +
95 | ", KCPInErrors=" + KCPInErrors +
96 | ", 收到包=" + InPkts +
97 | ", 发送包=" + OutPkts +
98 | ", InSegs=" + InSegs +
99 | ", OutSegs=" + OutSegs +
100 | ", 收到字节=" + InBytes +
101 | ", 发送字节=" + OutBytes +
102 | ", 总共重发数=" + RetransSegs +
103 | ", 快速重发数=" + FastRetransSegs +
104 | ", 空闲快速重发数=" + EarlyRetransSegs +
105 | ", 超时重发数=" + LostSegs +
106 | ", 收到重复包数量=" + RepeatSegs +
107 | ", fec恢复数=" + FECRecovered +
108 | ", fec恢复错误数=" + FECErrs +
109 | ", 收到fecData数=" + FECDataShards +
110 | ", 收到fecParity数=" + FECParityShards +
111 | ", fec缓存冗余淘汰data包数=" + FECShortShards +
112 | ", fec收到重复的数据包=" + FECRepeatDataShards +
113 | '}';
114 | }
115 | }
116 | }
--------------------------------------------------------------------------------
/base-kcp/fec/reedsolomon_csharp.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/l42111996/csharp-kcp/cc7e42dbc2979c09e87625f5328e466e31fde744/base-kcp/fec/reedsolomon_csharp.dll
--------------------------------------------------------------------------------
/base-kcp/obj/Debug/netcoreapp2.2/base-kcp.assets.cache:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/l42111996/csharp-kcp/cc7e42dbc2979c09e87625f5328e466e31fde744/base-kcp/obj/Debug/netcoreapp2.2/base-kcp.assets.cache
--------------------------------------------------------------------------------
/base-kcp/obj/Debug/netcoreapp2.2/base-kcp.csproj.CoreCompileInputs.cache:
--------------------------------------------------------------------------------
1 | 3579eff3e8258f415eba741ea387349efc7e6b11
2 |
--------------------------------------------------------------------------------
/base-kcp/obj/Debug/netcoreapp2.2/base-kcp.csproj.FileListAbsolute.txt:
--------------------------------------------------------------------------------
1 | /Users/king/Documents/workspace/oschinaworkspace/csharp-kcp/base-kcp/bin/Debug/netcoreapp2.2/base-kcp.deps.json
2 | /Users/king/Documents/workspace/oschinaworkspace/csharp-kcp/base-kcp/bin/Debug/netcoreapp2.2/base-kcp.runtimeconfig.json
3 | /Users/king/Documents/workspace/oschinaworkspace/csharp-kcp/base-kcp/bin/Debug/netcoreapp2.2/base-kcp.runtimeconfig.dev.json
4 | /Users/king/Documents/workspace/oschinaworkspace/csharp-kcp/base-kcp/bin/Debug/netcoreapp2.2/base-kcp.dll
5 | /Users/king/Documents/workspace/oschinaworkspace/csharp-kcp/base-kcp/bin/Debug/netcoreapp2.2/base-kcp.pdb
6 | /Users/king/Documents/workspace/oschinaworkspace/csharp-kcp/base-kcp/obj/Debug/netcoreapp2.2/base-kcp.csprojAssemblyReference.cache
7 | /Users/king/Documents/workspace/oschinaworkspace/csharp-kcp/base-kcp/obj/Debug/netcoreapp2.2/base-kcp.csproj.CoreCompileInputs.cache
8 | /Users/king/Documents/workspace/oschinaworkspace/csharp-kcp/base-kcp/obj/Debug/netcoreapp2.2/base-kcp.AssemblyInfoInputs.cache
9 | /Users/king/Documents/workspace/oschinaworkspace/csharp-kcp/base-kcp/obj/Debug/netcoreapp2.2/base-kcp.AssemblyInfo.cs
10 | /Users/king/Documents/workspace/oschinaworkspace/csharp-kcp/base-kcp/obj/Debug/netcoreapp2.2/base-kcp.dll
11 | /Users/king/Documents/workspace/oschinaworkspace/csharp-kcp/base-kcp/obj/Debug/netcoreapp2.2/base-kcp.pdb
12 | /Users/king/Documents/workspace/oschinaworkspace/csharp-kcp/base-kcp/bin/Debug/netcoreapp2.2/fec.dll
13 | /Users/king/Documents/workspace/oschinaworkspace/csharp-kcp/base-kcp/bin/Debug/netcoreapp2.2/fec.pdb
14 | /Users/king/Documents/workspace/oschinaworkspace/csharp-kcp/base-kcp/obj/Debug/netcoreapp2.2/base-kcp.csproj.CopyComplete
15 |
--------------------------------------------------------------------------------
/base-kcp/obj/base-kcp.csproj.nuget.dgspec.json:
--------------------------------------------------------------------------------
1 | {
2 | "format": 1,
3 | "restore": {
4 | "/Users/king/Documents/workspace/github/csharp-kcp/base-kcp/base-kcp.csproj": {}
5 | },
6 | "projects": {
7 | "/Users/king/Documents/workspace/github/csharp-kcp/base-kcp/base-kcp.csproj": {
8 | "version": "1.0.0",
9 | "restore": {
10 | "projectUniqueName": "/Users/king/Documents/workspace/github/csharp-kcp/base-kcp/base-kcp.csproj",
11 | "projectName": "base-kcp",
12 | "projectPath": "/Users/king/Documents/workspace/github/csharp-kcp/base-kcp/base-kcp.csproj",
13 | "packagesPath": "/Users/king/.nuget/packages/",
14 | "outputPath": "/Users/king/Documents/workspace/github/csharp-kcp/base-kcp/obj/",
15 | "projectStyle": "PackageReference",
16 | "configFilePaths": [
17 | "/Users/king/.config/NuGet/NuGet.Config"
18 | ],
19 | "originalTargetFrameworks": [
20 | "netcoreapp2.2"
21 | ],
22 | "sources": {
23 | "https://api.nuget.org/v3/index.json": {}
24 | },
25 | "frameworks": {
26 | "netcoreapp2.2": {
27 | "projectReferences": {}
28 | }
29 | },
30 | "warningProperties": {
31 | "warnAsError": [
32 | "NU1605"
33 | ]
34 | }
35 | },
36 | "frameworks": {
37 | "netcoreapp2.2": {
38 | "dependencies": {
39 | "DotNetty.Buffers": {
40 | "target": "Package",
41 | "version": "[0.6.0, )"
42 | },
43 | "Microsoft.NETCore.App": {
44 | "suppressParent": "All",
45 | "target": "Package",
46 | "version": "[2.2.0, )",
47 | "autoReferenced": true
48 | }
49 | },
50 | "imports": [
51 | "net461",
52 | "net462",
53 | "net47",
54 | "net471",
55 | "net472",
56 | "net48"
57 | ],
58 | "assetTargetFallback": true,
59 | "warn": true,
60 | "runtimeIdentifierGraphPath": "/usr/local/share/dotnet/sdk/3.1.102/RuntimeIdentifierGraph.json"
61 | }
62 | }
63 | }
64 | }
65 | }
--------------------------------------------------------------------------------
/base-kcp/obj/base-kcp.csproj.nuget.g.props:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | True
5 | NuGet
6 | $(MSBuildThisFileDirectory)project.assets.json
7 | /Users/king/.nuget/packages/
8 | /Users/king/.nuget/packages/
9 | PackageReference
10 | 5.3.0
11 |
12 |
13 | $(MSBuildAllProjects);$(MSBuildThisFileFullPath)
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/base-kcp/obj/base-kcp.csproj.nuget.g.targets:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | $(MSBuildAllProjects);$(MSBuildThisFileFullPath)
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/base-kcp/obj/project.packagespec.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": "1.0.0",
3 | "restore": {
4 | "projectUniqueName": "/Users/king/Documents/workspace/github/csharp-kcp/base-kcp/base-kcp.csproj",
5 | "projectName": "base-kcp",
6 | "projectPath": "/Users/king/Documents/workspace/github/csharp-kcp/base-kcp/base-kcp.csproj",
7 | "outputPath": "/Users/king/Documents/workspace/github/csharp-kcp/base-kcp/obj/",
8 | "projectStyle": "PackageReference",
9 | "originalTargetFrameworks": [
10 | "netcoreapp2.2"
11 | ],
12 | "sources": {
13 | "https://api.nuget.org/v3/index.json": {}
14 | },
15 | "frameworks": {
16 | "netcoreapp2.2": {
17 | "projectReferences": {}
18 | }
19 | },
20 | "warningProperties": {
21 | "warnAsError": [
22 | "NU1605"
23 | ]
24 | }
25 | },
26 | "frameworks": {
27 | "netcoreapp2.2": {
28 | "dependencies": {
29 | "DotNetty.Buffers": {
30 | "target": "Package",
31 | "version": "[0.6.0, )"
32 | },
33 | "Microsoft.NETCore.App": {
34 | "suppressParent": "All",
35 | "target": "Package",
36 | "version": "[2.2.0, )",
37 | "autoReferenced": true
38 | }
39 | },
40 | "imports": [
41 | "net461",
42 | "net462",
43 | "net47",
44 | "net471",
45 | "net472",
46 | "net48"
47 | ],
48 | "assetTargetFallback": true,
49 | "warn": true,
50 | "runtimeIdentifierGraphPath": "/usr/local/share/dotnet/sdk/3.1.102/RuntimeIdentifierGraph.json"
51 | }
52 | }
53 | }
--------------------------------------------------------------------------------
/csharp-kcp.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "base-kcp", "base-kcp\base-kcp.csproj", "{4F470D6E-FE8E-4BEE-8DAA-5FEBB7EF1BD7}"
4 | EndProject
5 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "dotNetty-kcp", "dotNetty-kcp\dotNetty-kcp.csproj", "{C47F2191-35B9-49F7-9953-2D79DBD409EC}"
6 | EndProject
7 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "example-Kcp", "example-Kcp\example-Kcp.csproj", "{56068F6F-48AE-44F7-BF82-D3FF4770DE40}"
8 | EndProject
9 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "kcp4game", "kcp4game\kcp4game.csproj", "{0960065F-10DE-4C3D-9FAD-CFCA272CD7D8}"
10 | EndProject
11 | Global
12 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
13 | Debug|Any CPU = Debug|Any CPU
14 | Release|Any CPU = Release|Any CPU
15 | EndGlobalSection
16 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
17 | {4F470D6E-FE8E-4BEE-8DAA-5FEBB7EF1BD7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
18 | {4F470D6E-FE8E-4BEE-8DAA-5FEBB7EF1BD7}.Debug|Any CPU.Build.0 = Debug|Any CPU
19 | {4F470D6E-FE8E-4BEE-8DAA-5FEBB7EF1BD7}.Release|Any CPU.ActiveCfg = Release|Any CPU
20 | {4F470D6E-FE8E-4BEE-8DAA-5FEBB7EF1BD7}.Release|Any CPU.Build.0 = Release|Any CPU
21 | {4F470D6E-FE8E-4BEE-8DAA-5FEBB7EF1BD7}.Debug|Any CPU.Deploy.0 = Debug|Any CPU
22 | {4F470D6E-FE8E-4BEE-8DAA-5FEBB7EF1BD7}.Release|Any CPU.Deploy.0 = Release|Any CPU
23 | {C47F2191-35B9-49F7-9953-2D79DBD409EC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
24 | {C47F2191-35B9-49F7-9953-2D79DBD409EC}.Debug|Any CPU.Build.0 = Debug|Any CPU
25 | {C47F2191-35B9-49F7-9953-2D79DBD409EC}.Release|Any CPU.ActiveCfg = Release|Any CPU
26 | {C47F2191-35B9-49F7-9953-2D79DBD409EC}.Release|Any CPU.Build.0 = Release|Any CPU
27 | {C47F2191-35B9-49F7-9953-2D79DBD409EC}.Release|Any CPU.Deploy.0 = Release|Any CPU
28 | {56068F6F-48AE-44F7-BF82-D3FF4770DE40}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
29 | {56068F6F-48AE-44F7-BF82-D3FF4770DE40}.Debug|Any CPU.Build.0 = Debug|Any CPU
30 | {56068F6F-48AE-44F7-BF82-D3FF4770DE40}.Release|Any CPU.ActiveCfg = Release|Any CPU
31 | {56068F6F-48AE-44F7-BF82-D3FF4770DE40}.Release|Any CPU.Build.0 = Release|Any CPU
32 | {0960065F-10DE-4C3D-9FAD-CFCA272CD7D8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
33 | {0960065F-10DE-4C3D-9FAD-CFCA272CD7D8}.Debug|Any CPU.Build.0 = Debug|Any CPU
34 | {0960065F-10DE-4C3D-9FAD-CFCA272CD7D8}.Release|Any CPU.ActiveCfg = Release|Any CPU
35 | {0960065F-10DE-4C3D-9FAD-CFCA272CD7D8}.Release|Any CPU.Build.0 = Release|Any CPU
36 | EndGlobalSection
37 | EndGlobal
38 |
--------------------------------------------------------------------------------
/dotNetty-kcp/ChannelConfig.cs:
--------------------------------------------------------------------------------
1 | using base_kcp;
2 | using fec.fec;
3 |
4 | namespace dotNetty_kcp
5 | {
6 | public class ChannelConfig
7 | {
8 | private bool nodelay;
9 | private int interval = Kcp.IKCP_INTERVAL;
10 | private int fastresend;
11 | private bool nocwnd;
12 | private int sndwnd = Kcp.IKCP_WND_SND;
13 | private int rcvwnd = Kcp.IKCP_WND_RCV;
14 | private int mtu = Kcp.IKCP_MTU_DEF;
15 | private int minRto = Kcp.IKCP_RTO_MIN;
16 | //超时时间 超过一段时间没收到消息断开连接
17 | private long timeoutMillis;
18 | //TODO 可能有bug还未测试
19 | private bool stream;
20 |
21 | //下面为新增参数
22 | private int fecDataShardCount;
23 | private int fecParityShardCount;
24 | //收到包立刻回传ack包
25 | private bool ackNoDelay = false;
26 | //发送包立即调用flush 延迟低一些 cpu增加 如果interval值很小 建议关闭该参数
27 | private bool fastFlush = true;
28 | //crc32校验
29 | private bool crc32Check = false;
30 |
31 | //增加ack包回复成功率 填 /8/16/32
32 | private int ackMaskSize = 0;
33 | /**使用conv确定一个channel 还是使用 socketAddress确定一个channel**/
34 | private bool useConvChannel=false;
35 | /**预留长度**/
36 | private int reserved;
37 |
38 |
39 | public void initNodelay(bool nodelay, int interval, int resend, bool nc){
40 | this.nodelay = nodelay;
41 | this.interval = interval;
42 | this.fastresend = resend;
43 | this.nocwnd=nc;
44 | }
45 |
46 |
47 | public int Conv { get; set; }
48 |
49 | public bool Nodelay
50 | {
51 | get => nodelay;
52 | set => nodelay = value;
53 | }
54 |
55 | public int Interval
56 | {
57 | get => interval;
58 | set => interval = value;
59 | }
60 |
61 | public int Fastresend
62 | {
63 | get => fastresend;
64 | set => fastresend = value;
65 | }
66 |
67 | public bool Nocwnd
68 | {
69 | get => nocwnd;
70 | set => nocwnd = value;
71 | }
72 |
73 | public int Sndwnd
74 | {
75 | get => sndwnd;
76 | set => sndwnd = value;
77 | }
78 |
79 | public int Rcvwnd
80 | {
81 | get => rcvwnd;
82 | set => rcvwnd = value;
83 | }
84 |
85 | public int Mtu
86 | {
87 | get => mtu;
88 | set => mtu = value;
89 | }
90 |
91 | public int MinRto
92 | {
93 | get => minRto;
94 | set => minRto = value;
95 | }
96 |
97 | public long TimeoutMillis
98 | {
99 | get => timeoutMillis;
100 | set => timeoutMillis = value;
101 | }
102 |
103 | public bool Stream
104 | {
105 | get => stream;
106 | set => stream = value;
107 | }
108 |
109 | public int FecDataShardCount
110 | {
111 | get => fecDataShardCount;
112 | set
113 | {
114 | if (value > 0)
115 | {
116 | reserved += Fec.fecHeaderSizePlus2;
117 | }
118 | fecDataShardCount = value;
119 | }
120 | }
121 |
122 | public int FecParityShardCount
123 | {
124 | get => fecParityShardCount;
125 | set => fecParityShardCount = value;
126 | }
127 |
128 | public bool AckNoDelay
129 | {
130 | get => ackNoDelay;
131 | set => ackNoDelay = value;
132 | }
133 |
134 | public bool FastFlush
135 | {
136 | get => fastFlush;
137 | set => fastFlush = value;
138 | }
139 |
140 | public bool Crc32Check
141 | {
142 | get => crc32Check;
143 | set
144 | {
145 | if (value)
146 | {
147 | reserved += Ukcp.HEADER_CRC;
148 | }
149 | crc32Check = value;
150 | }
151 | }
152 |
153 | public int AckMaskSize
154 | {
155 | get => ackMaskSize;
156 | set => ackMaskSize = value;
157 | }
158 |
159 |
160 | public int Reserved
161 | {
162 | get => reserved;
163 | }
164 |
165 | public bool UseConvChannel
166 | {
167 | get => useConvChannel;
168 | set => useConvChannel = value;
169 | }
170 | }
171 | }
--------------------------------------------------------------------------------
/dotNetty-kcp/ClientChannelHandler.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using DotNetty.Transport.Channels;
3 | using DotNetty.Transport.Channels.Sockets;
4 | using dotNetty_kcp.thread;
5 |
6 | namespace dotNetty_kcp
7 | {
8 | public class ClientChannelHandler:ChannelHandlerAdapter
9 | {
10 | private readonly IChannelManager _channelManager;
11 |
12 | private readonly ChannelConfig _channelConfig;
13 |
14 |
15 | public ClientChannelHandler(IChannelManager channelManager,ChannelConfig channelConfig)
16 | {
17 | this._channelManager = channelManager;
18 | this._channelConfig = channelConfig;
19 | }
20 |
21 | public override void ExceptionCaught(IChannelHandlerContext context, Exception exception)
22 | {
23 | Console.WriteLine(exception);
24 | }
25 |
26 | public override void ChannelRead(IChannelHandlerContext context, object message)
27 | {
28 | var msg = (DatagramPacket) message;
29 | var ukcp = _channelManager.get(msg);
30 | ukcp.read(msg.Content);
31 | }
32 | }
33 | }
--------------------------------------------------------------------------------
/dotNetty-kcp/ClientConvChannelManager.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Concurrent;
3 | using System.Collections.Generic;
4 | using System.Collections.ObjectModel;
5 | using System.Net;
6 | using System.Xml.Linq;
7 | using DotNetty.Transport.Channels;
8 | using DotNetty.Transport.Channels.Sockets;
9 |
10 | namespace dotNetty_kcp
11 | {
12 | /**
13 | * 根据conv确定一个session
14 | */
15 | public class ClientConvChannelManager : IChannelManager
16 | {
17 |
18 | private readonly ConcurrentDictionary _ukcps = new ConcurrentDictionary();
19 |
20 | private readonly int convIndex;
21 |
22 | public ClientConvChannelManager(int convIndex)
23 | {
24 | this.convIndex = convIndex;
25 | }
26 |
27 | public Ukcp get(DatagramPacket msg)
28 | {
29 | var conv = getConv(msg);
30 | _ukcps.TryGetValue(conv, out var ukcp);
31 | return ukcp;
32 | }
33 |
34 | private int getConv(DatagramPacket msg) {
35 | var bytebuffer = msg.Content;
36 | return bytebuffer.GetIntLE(convIndex);;
37 | }
38 |
39 |
40 |
41 | public void New(EndPoint endPoint, Ukcp ukcp,DatagramPacket msg)
42 | {
43 | var conv = ukcp.getConv();
44 | if (msg != null) {
45 | conv = getConv(msg);
46 | ukcp.setConv(conv);
47 | }
48 | _ukcps.TryAdd(conv, ukcp);
49 | }
50 |
51 | public void del(Ukcp ukcp)
52 | {
53 | _ukcps.TryRemove(ukcp.getConv(), out var temp);
54 | if (temp == null)
55 | {
56 | Console.WriteLine("ukcp session is not exist conv: " + ukcp.getConv());
57 | }
58 | ukcp.user().Channel.CloseAsync();
59 | }
60 |
61 | public ICollection getAll()
62 | {
63 | return this._ukcps.Values;
64 | }
65 | }
66 | }
--------------------------------------------------------------------------------
/dotNetty-kcp/ClientEndPointChannelManager.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Concurrent;
3 | using System.Collections.Generic;
4 | using System.Net;
5 | using DotNetty.Transport.Channels.Sockets;
6 |
7 | namespace dotNetty_kcp
8 | {
9 | public class ClientEndPointChannelManager : IChannelManager
10 | {
11 | private readonly ConcurrentDictionary _ukcps = new ConcurrentDictionary();
12 |
13 | public Ukcp get(DatagramPacket msg)
14 | {
15 | _ukcps.TryGetValue(msg.Recipient, out var ukcp);
16 | return ukcp;
17 | }
18 |
19 | public void New(EndPoint endPoint, Ukcp ukcp, DatagramPacket msg)
20 | {
21 | _ukcps[endPoint] = ukcp;
22 | }
23 |
24 | public void del(Ukcp ukcp)
25 | {
26 | _ukcps.TryRemove(ukcp.user().LocalAddress, out var temp);
27 | if (temp == null)
28 | {
29 | Console.WriteLine("ukcp session is not exist RemoteAddress: " + ukcp.user().RemoteAddress);
30 | }
31 | ukcp.user().Channel.CloseAsync();
32 | }
33 |
34 | public ICollection getAll()
35 | {
36 | return _ukcps.Values;
37 | }
38 | }
39 | }
--------------------------------------------------------------------------------
/dotNetty-kcp/CloseTask.cs:
--------------------------------------------------------------------------------
1 | using dotNetty_kcp.thread;
2 |
3 | namespace dotNetty_kcp
4 | {
5 | public class CloseTask:ITask
6 | {
7 | private Ukcp _ukcp;
8 |
9 | public CloseTask(Ukcp ukcp)
10 | {
11 | _ukcp = ukcp;
12 | }
13 |
14 | public void execute()
15 | {
16 | _ukcp.internalClose();
17 | }
18 | }
19 | }
--------------------------------------------------------------------------------
/dotNetty-kcp/CodecOutputList.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using DotNetty.Common;
3 |
4 | namespace dotNetty_kcp
5 | {
6 | public class CodecOutputList:List
7 | {
8 |
9 | const int DefaultInitialCapacity =16;
10 |
11 | static readonly ThreadLocalPool> Pool = new ThreadLocalPool>(handle => new CodecOutputList(handle));
12 |
13 | readonly ThreadLocalPool.Handle returnHandle;
14 |
15 | CodecOutputList(ThreadLocalPool.Handle returnHandle)
16 | {
17 | this.returnHandle = returnHandle;
18 | }
19 |
20 | public static CodecOutputList NewInstance() => NewInstance(DefaultInitialCapacity);
21 |
22 | public static CodecOutputList NewInstance(int minCapacity)
23 | {
24 | CodecOutputList ret = Pool.Take();
25 | if (ret.Capacity < minCapacity)
26 | {
27 | ret.Capacity = minCapacity;
28 | }
29 | return ret;
30 |
31 | }
32 |
33 | public void Return()
34 | {
35 | this.Clear();
36 | this.returnHandle.Release(this);
37 | }
38 | }
39 | }
--------------------------------------------------------------------------------
/dotNetty-kcp/ConnectTask.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using dotNetty_kcp.thread;
3 |
4 | namespace dotNetty_kcp
5 | {
6 | public class ConnectTask:ITask
7 | {
8 | private readonly Ukcp _ukcp;
9 |
10 | private readonly KcpListener _listener;
11 |
12 | public ConnectTask(Ukcp ukcp, KcpListener listener)
13 | {
14 | _ukcp = ukcp;
15 | _listener = listener;
16 | }
17 |
18 |
19 | public void execute()
20 | {
21 | try
22 | {
23 | _listener.onConnected(_ukcp);
24 | }
25 | catch (Exception e)
26 | {
27 | _listener.handleException(e,_ukcp);
28 | }
29 | }
30 | }
31 | }
--------------------------------------------------------------------------------
/dotNetty-kcp/Crc32.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Text;
3 | using DotNetty.Buffers;
4 |
5 | namespace dotNetty_kcp
6 | {
7 | ///
8 | /// Computes a CRC32 checksum.
9 | ///
10 | /// Based on
11 | public static class Crc32
12 | {
13 | readonly static uint[] Table = CreateTable();
14 |
15 | static Crc32()
16 | {
17 | }
18 |
19 | // ///
20 | // /// Compute the checksum of a UTF8 text.
21 | // ///
22 | // /// Text to calculate
23 | // /// Checksum
24 | // public static int ComputeChecksum(string text)
25 | // {
26 | // return ComputeChecksum(text, Encoding.UTF8);
27 | // }
28 | //
29 | // ///
30 | // /// Compute the checksum of a text using a specific encoding.
31 | // ///
32 | // /// Text to calculate
33 | // /// Text encoding
34 | // /// Checksum
35 | // public static int ComputeChecksum(string text, Encoding encoding)
36 | // {
37 | // if (string.IsNullOrEmpty(text)) return 0;
38 | // byte[] bytes = encoding.GetBytes(text);
39 | // return ComputeChecksum(bytes);
40 | // }
41 |
42 | ///
43 | /// Compute the checksum of a binary buffer.
44 | ///
45 | /// Buffer to calculate
46 | ///
47 | public static int ComputeChecksum(sbyte[] bytes)
48 | {
49 | uint crc = 0xffffffff;
50 | for (int i = 0; i < bytes.Length; i++)
51 | {
52 | byte index = (byte) (((crc) & 0xff) ^ bytes[i]);
53 | crc = (crc >> 8) ^ Table[index];
54 | }
55 |
56 | return unchecked((int) ~crc);
57 | }
58 |
59 |
60 |
61 | ///
62 | /// Compute the checksum of a binary buffer.
63 | ///
64 | /// Buffer to calculate
65 | ///
66 | public static uint ComputeChecksum(IByteBuffer byteBuffer,int offset,int lenth)
67 | {
68 | var crc = 0xffffffff;
69 | lenth += offset;
70 | for (var i = offset; i < lenth; i++)
71 | {
72 | var index = (byte) (((crc) & 0xff) ^ byteBuffer.GetByte(i));
73 | crc = (crc >> 8) ^ Table[index];
74 | }
75 | return ~crc;
76 | }
77 |
78 | static uint[] CreateTable()
79 | {
80 | const uint poly = 0xedb88320;
81 | var table = new uint[256];
82 | uint temp = 0;
83 | for (uint i = 0; i < table.Length; ++i)
84 | {
85 | temp = i;
86 | for (int j = 8; j > 0; --j)
87 | {
88 | if ((temp & 1) == 1)
89 | {
90 | temp = (uint) ((temp >> 1) ^ poly);
91 | }
92 | else
93 | {
94 | temp >>= 1;
95 | }
96 | }
97 |
98 | table[i] = temp;
99 | }
100 |
101 | return table;
102 | }
103 | }
104 | }
--------------------------------------------------------------------------------
/dotNetty-kcp/Crc32OutPut.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using base_kcp;
3 | using DotNetty.Buffers;
4 |
5 | namespace dotNetty_kcp
6 | {
7 | public class Crc32OutPut:KcpOutput
8 | {
9 | private readonly KcpOutput _output;
10 | private readonly int _headerOffset;
11 |
12 | public Crc32OutPut(KcpOutput output,int headerOffset) {
13 | _output = output;
14 | _headerOffset = headerOffset;
15 | }
16 |
17 | public void outPut(IByteBuffer data, Kcp kcp)
18 | {
19 | var checksum =Crc32.ComputeChecksum(data, _headerOffset + Ukcp.HEADER_CRC,
20 | data.ReadableBytes - _headerOffset - Ukcp.HEADER_CRC);
21 | data.SetUnsignedIntLE(_headerOffset, checksum);
22 | _output.outPut(data,kcp);
23 | }
24 | }
25 | }
--------------------------------------------------------------------------------
/dotNetty-kcp/FecOutPut.cs:
--------------------------------------------------------------------------------
1 | using base_kcp;
2 | using DotNetty.Buffers;
3 | using fec.fec;
4 |
5 | namespace dotNetty_kcp
6 | {
7 | public class FecOutPut :KcpOutput
8 | {
9 | private readonly KcpOutput output;
10 |
11 | private readonly FecEncode fecEncode;
12 |
13 | public FecOutPut(KcpOutput output, FecEncode fecEncode)
14 | {
15 | this.output = output;
16 | this.fecEncode = fecEncode;
17 | }
18 |
19 | public void outPut(IByteBuffer data, Kcp kcp)
20 | {
21 | var byteBufs = fecEncode.encode(data);
22 | //out之后会自动释放你内存
23 | output.outPut(data,kcp);
24 | if(byteBufs==null)
25 | return;
26 | foreach (var parityByteBuf in byteBufs)
27 | {
28 | output.outPut(parityByteBuf,kcp);
29 | }
30 | }
31 | }
32 | }
--------------------------------------------------------------------------------
/dotNetty-kcp/IChannelManager.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.Collections.ObjectModel;
3 | using System.Net;
4 | using DotNetty.Transport.Channels;
5 | using DotNetty.Transport.Channels.Sockets;
6 |
7 | namespace dotNetty_kcp
8 | {
9 | public interface IChannelManager
10 | {
11 | Ukcp get(DatagramPacket msg);
12 |
13 | void New(EndPoint endPoint, Ukcp ukcp, DatagramPacket msg);
14 |
15 | void del(Ukcp ukcp);
16 |
17 | ICollection getAll();
18 | }
19 | }
--------------------------------------------------------------------------------
/dotNetty-kcp/IScheduleTask.cs:
--------------------------------------------------------------------------------
1 | using dotNetty_kcp.thread;
2 | using DotNetty.Common.Concurrency;
3 | using DotNetty.Common.Utilities;
4 |
5 | namespace dotNetty_kcp
6 | {
7 | public interface IScheduleTask:ITimerTask,IRunnable
8 | {
9 |
10 | }
11 | }
--------------------------------------------------------------------------------
/dotNetty-kcp/KcpClient.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Net;
3 | using System.Net.Sockets;
4 | using base_kcp;
5 | using DotNetty.Transport.Bootstrapping;
6 | using DotNetty.Transport.Channels;
7 | using DotNetty.Transport.Channels.Sockets;
8 | using dotNetty_kcp.thread;
9 | using fec;
10 | using fec.fec;
11 |
12 | namespace dotNetty_kcp
13 | {
14 | /**
15 | * kcp客户端
16 | * 客户端使用方式:
17 | * 1,与服务器tcp通讯得到conv
18 | * 2,kcp通过conv标识与服务器通讯
19 | * 3,客户端发现网络断开重连之后必须通过kcp发送一个心跳包出去 用于服务器确定客户端的出口地址
20 | * 4,客户端需要最少每60秒发送一个心跳数据包服务端收到后回复客户端用于 路由表记录映射信息
21 | */
22 | public class KcpClient
23 | {
24 | private Bootstrap bootstrap;
25 |
26 | private IExecutorPool _executorPool;
27 |
28 | private IChannelManager _channelManager;
29 |
30 | private IEventLoopGroup _eventLoopGroup;
31 |
32 | private IScheduleThread _scheduleThread;
33 |
34 |
35 | private static IChannel bindLocal(Bootstrap bootstrap,EndPoint localAddress = null)
36 | {
37 | if (localAddress == null)
38 | {
39 | localAddress = new IPEndPoint(IPAddress.Any, 0);
40 | }
41 | return bootstrap.BindAsync(localAddress).Result;
42 | }
43 |
44 | public void init(ChannelConfig channelConfig,ExecutorPool executorPool,IEventLoopGroup eventLoopGroup)
45 | {
46 | if(channelConfig.UseConvChannel){
47 | var convIndex = 0;
48 | if(channelConfig.Crc32Check){
49 | convIndex+=Ukcp.HEADER_CRC;
50 | }
51 | if(channelConfig.FecDataShardCount!=0&&channelConfig.FecParityShardCount!=0){
52 | convIndex+= Fec.fecHeaderSizePlus2;
53 | }
54 | _channelManager = new ClientConvChannelManager(convIndex);
55 | }else{
56 | _channelManager = new ClientEndPointChannelManager();
57 | }
58 |
59 | //初始化线程池 创建一个线程就够了
60 | _executorPool = executorPool;
61 | _executorPool.CreateMessageExecutor();
62 | _eventLoopGroup = eventLoopGroup;
63 |
64 | _scheduleThread = new EventLoopScheduleThread();
65 |
66 | bootstrap = new Bootstrap();
67 | bootstrap.Group(_eventLoopGroup);
68 | bootstrap.ChannelFactory(() => new SocketDatagramChannel(AddressFamily.InterNetwork));
69 | bootstrap.Handler(new ActionChannelInitializer(channel =>
70 | {
71 | var pipeline = channel.Pipeline;
72 | pipeline.AddLast(new ClientChannelHandler(_channelManager,channelConfig));
73 | }));
74 | }
75 |
76 |
77 |
78 | public void init(ChannelConfig channelConfig)
79 | {
80 | var executorPool = new ExecutorPool();
81 | executorPool.CreateMessageExecutor();
82 | init(channelConfig,executorPool,new MultithreadEventLoopGroup());
83 | }
84 |
85 |
86 | /**
87 | * 重连接口
88 | * 使用旧的kcp对象,出口ip和端口替换为新的
89 | * 在4G切换为wifi等场景使用
90 | * @param ukcp
91 | */
92 | public void reconnect(Ukcp ukcp){
93 | if (!(_channelManager is ServerConvChannelManager))
94 | {
95 | throw new Exception("reconnect can only be used in convChannel");
96 | }
97 | ukcp.IMessageExecutor.execute(new ReconnectTask(ukcp,bootstrap));
98 | }
99 |
100 | private class ReconnectTask : ITask
101 | {
102 | private readonly Ukcp _ukcp;
103 | private readonly Bootstrap _bootstrap;
104 |
105 | public ReconnectTask(Ukcp ukcp, Bootstrap bootstrap)
106 | {
107 | _ukcp = ukcp;
108 | _bootstrap = bootstrap;
109 | }
110 |
111 | public void execute()
112 | {
113 | _ukcp.user().Channel.CloseAsync();
114 | var iChannel = bindLocal(_bootstrap);
115 | _ukcp.user().Channel = iChannel;
116 | }
117 | }
118 |
119 |
120 | public Ukcp connect(IChannel localChannel,EndPoint remoteAddress, ChannelConfig channelConfig, KcpListener kcpListener)
121 | {
122 |
123 | KcpOutput kcpOutput = new KcpOutPutImp();
124 | ReedSolomon reedSolomon = null;
125 | if (channelConfig.FecDataShardCount != 0 && channelConfig.FecParityShardCount != 0)
126 | {
127 | reedSolomon = ReedSolomon.create(channelConfig.FecDataShardCount, channelConfig.FecParityShardCount);
128 | }
129 |
130 | var _messageExecutor = _executorPool.GetAutoMessageExecutor();
131 |
132 | var ukcp = new Ukcp(kcpOutput, kcpListener, _messageExecutor, reedSolomon, channelConfig);
133 |
134 | var user = new User(localChannel, remoteAddress, localChannel.LocalAddress);
135 | ukcp.user(user);
136 |
137 | _channelManager.New(localChannel.LocalAddress, ukcp,null);
138 |
139 | _messageExecutor.execute(new ConnectTask(ukcp, kcpListener));
140 |
141 | var scheduleTask = new ScheduleTask( _channelManager, ukcp,_scheduleThread);
142 |
143 | _scheduleThread.schedule(scheduleTask,TimeSpan.FromMilliseconds(ukcp.getInterval()));
144 | return ukcp;
145 | }
146 |
147 | /**
148 | * 连接一个服务器
149 | */
150 | public Ukcp connect(EndPoint localAddress,EndPoint remoteAddress, ChannelConfig channelConfig, KcpListener kcpListener)
151 | {
152 | var channel = bindLocal(bootstrap,localAddress);
153 | return connect(channel, remoteAddress, channelConfig, kcpListener);
154 | }
155 |
156 | /**
157 | * 连接一个服务器
158 | */
159 | public Ukcp connect(EndPoint remoteAddress, ChannelConfig channelConfig, KcpListener kcpListener)
160 | {
161 | var channel = bindLocal(bootstrap);
162 | return connect(channel, remoteAddress, channelConfig, kcpListener);
163 | }
164 |
165 |
166 | public void stop()
167 | {
168 | foreach (var ukcp in _channelManager.getAll())
169 | {
170 | ukcp.close();
171 | }
172 | _executorPool?.stop(false);
173 | if (_eventLoopGroup != null&&!_eventLoopGroup.IsShuttingDown)
174 | {
175 | _eventLoopGroup?.ShutdownGracefullyAsync().Wait();
176 | }
177 | _scheduleThread.stop();
178 | }
179 | }
180 | }
--------------------------------------------------------------------------------
/dotNetty-kcp/KcpListener.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using DotNetty.Buffers;
3 |
4 | namespace dotNetty_kcp
5 | {
6 | public interface KcpListener
7 | {
8 |
9 | /**
10 | * 连接之后
11 | * @param ukcp
12 | */
13 | void onConnected(Ukcp ukcp);
14 |
15 | /**
16 | * kcp message
17 | *
18 | * @param byteBuf the data
19 | * @param ukcp
20 | */
21 | void handleReceive(IByteBuffer byteBuf, Ukcp ukcp);
22 |
23 | /**
24 | *
25 | * kcp异常,之后此kcp就会被关闭
26 | *
27 | * @param ex 异常
28 | * @param ukcp 发生异常的kcp,null表示非kcp错误
29 | */
30 | void handleException(Exception ex, Ukcp ukcp);
31 |
32 | /**
33 | * 关闭
34 | *
35 | * @param ukcp
36 | */
37 | void handleClose(Ukcp ukcp);
38 | }
39 | }
--------------------------------------------------------------------------------
/dotNetty-kcp/KcpOutPutImp.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using base_kcp;
3 | using DotNetty.Buffers;
4 | using DotNetty.Transport.Channels.Sockets;
5 | using fec;
6 |
7 | namespace dotNetty_kcp
8 | {
9 | public class KcpOutPutImp:KcpOutput
10 | {
11 | public void outPut(IByteBuffer data, Kcp kcp)
12 | {
13 | Snmp.snmp.OutPkts++;
14 | Snmp.snmp.OutBytes+=data.WriterIndex;
15 | var user = (User) kcp.User;
16 | var temp = new DatagramPacket(data,user.LocalAddress, user.RemoteAddress);
17 | user.Channel.WriteAndFlushAsync(temp);
18 | }
19 | }
20 | }
--------------------------------------------------------------------------------
/dotNetty-kcp/KcpServer.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Net.Sockets;
4 | using System.Numerics;
5 | using System.Threading;
6 | using DotNetty.Transport.Bootstrapping;
7 | using DotNetty.Transport.Channels;
8 | using DotNetty.Transport.Channels.Sockets;
9 | using dotNetty_kcp.thread;
10 | using fec.fec;
11 |
12 | namespace dotNetty_kcp
13 | {
14 | public class KcpServer
15 | {
16 |
17 | private IExecutorPool _executorPool;
18 |
19 | private Bootstrap _bootstrap;
20 |
21 | private IEventLoopGroup _eventLoopGroup;
22 |
23 | private readonly List _localAddress = new List();
24 |
25 | private IChannelManager _channelManager;
26 |
27 | private IScheduleThread _scheduleThread;
28 |
29 |
30 | public void init(int workSize, KcpListener kcpListener, ChannelConfig channelConfig, params int[] ports)
31 | {
32 | _executorPool = new ExecutorPool();
33 | for (int i = 0; i < workSize; i++)
34 | {
35 | _executorPool.CreateMessageExecutor();
36 | }
37 | init(_executorPool, kcpListener, channelConfig, ports);
38 | }
39 |
40 |
41 | public void init(IExecutorPool executorPool, KcpListener kcpListener, ChannelConfig channelConfig, params int[] ports) {
42 | if(channelConfig.UseConvChannel){
43 | int convIndex = 0;
44 | if(channelConfig.Crc32Check){
45 | convIndex+=Ukcp.HEADER_CRC;
46 | }
47 | if(channelConfig.FecDataShardCount!=0&&channelConfig.FecParityShardCount!=0){
48 | convIndex+= Fec.fecHeaderSizePlus2;
49 | }
50 | _channelManager = new ServerConvChannelManager(convIndex);
51 | }else{
52 | _channelManager = new ServerEndPointChannelManager();
53 | }
54 |
55 | int cpuNum = Environment.ProcessorCount;
56 | int bindTimes = cpuNum;
57 |
58 | _eventLoopGroup = new MultithreadEventLoopGroup(cpuNum);
59 | _scheduleThread = new HashedWheelScheduleThread();
60 |
61 | _bootstrap = new Bootstrap();
62 | //TODO epoll模型 服务器端怎么支持?得试试成功没有
63 | _bootstrap.Option(ChannelOption.SoReuseport, true);
64 | // _bootstrap.Option(ChannelOption.SoReuseaddr, true);
65 | _bootstrap.Group(_eventLoopGroup);
66 | _bootstrap.ChannelFactory(() => new SocketDatagramChannel(AddressFamily.InterNetwork));
67 | _bootstrap.Handler(new ActionChannelInitializer(channel =>
68 | {
69 | var pipeline = channel.Pipeline;
70 | pipeline.AddLast(new ServerChannelHandler(_channelManager,channelConfig,executorPool,kcpListener,_scheduleThread));
71 | }));
72 |
73 | foreach (var port in ports)
74 | {
75 | // for (int i = 0; i < bindTimes; i++) {
76 | var task = _bootstrap.BindAsync(port);
77 | var channel = task.Result;
78 | _localAddress.Add(channel);
79 | // }
80 | }
81 |
82 | //TODO 如何启动关闭进程的钩子??
83 | }
84 |
85 |
86 | /**
87 | * 同步关闭服务器
88 | */
89 | public void stop() {
90 | foreach (var channel in _localAddress)
91 | {
92 | channel.CloseAsync().Wait();
93 | }
94 | foreach (var ukcp in _channelManager.getAll())
95 | {
96 | ukcp.close();
97 | }
98 | _eventLoopGroup?.ShutdownGracefullyAsync();
99 | _executorPool?.stop(false);
100 | _scheduleThread.stop();
101 | }
102 | }
103 | }
--------------------------------------------------------------------------------
/dotNetty-kcp/ReadTask.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using DotNetty.Buffers;
3 | using DotNetty.Common;
4 | using dotNetty_kcp.thread;
5 |
6 | namespace dotNetty_kcp
7 | {
8 | public class ReadTask : ITask
9 | {
10 | private Ukcp kcp;
11 |
12 | private static readonly ThreadLocalPool RECYCLER =
13 | new ThreadLocalPool(handle => new ReadTask(handle));
14 |
15 | private readonly ThreadLocalPool.Handle recyclerHandle;
16 |
17 | private ReadTask(ThreadLocalPool.Handle recyclerHandle)
18 | {
19 | this.recyclerHandle = recyclerHandle;
20 | }
21 |
22 | public static ReadTask New(Ukcp kcp)
23 | {
24 | ReadTask readTask = RECYCLER.Take();
25 | readTask.kcp = kcp;
26 | return readTask;
27 | }
28 |
29 | public void execute()
30 | {
31 | CodecOutputList bufList = null;
32 | try {
33 | //Thread.sleep(1000);
34 | //查看连接状态
35 | if (!kcp.isActive()) {
36 | return;
37 | }
38 | bool hasKcpMessage = false;
39 | long current = kcp.currentMs();
40 | var readQueue = kcp.ReadQueue;
41 | IByteBuffer byteBuf = null;
42 | for (;;)
43 | {
44 | if (!readQueue.TryDequeue(out byteBuf))
45 | {
46 | break;
47 | }
48 | hasKcpMessage = true;
49 | kcp.input(byteBuf, current);
50 | byteBuf.Release();
51 | }
52 | if (!hasKcpMessage) {
53 | return;
54 | }
55 | if (kcp.isStream()) {
56 | while (kcp.canRecv()) {
57 | if (bufList == null) {
58 | bufList = CodecOutputList.NewInstance();
59 | }
60 | kcp.receive(bufList);
61 | }
62 | int size = bufList.Count;
63 | for (int i = 0; i < size; i++)
64 | {
65 | byteBuf = bufList[i];
66 | readBytebuf(byteBuf,current);
67 | }
68 | } else {
69 | while (kcp.canRecv()) {
70 | IByteBuffer recvBuf = kcp.mergeReceive();
71 | readBytebuf(recvBuf,current);
72 | }
73 | }
74 | //判断写事件
75 | if (!kcp.WriteQueue.IsEmpty&&kcp.canSend(false)) {
76 | kcp.notifyWriteEvent();
77 | }
78 | } catch (Exception e) {
79 | kcp.KcpListener.handleException(e,kcp);
80 | Console.WriteLine(e);
81 | } finally {
82 | release();
83 | bufList?.Return();
84 | }
85 | }
86 |
87 |
88 | private void readBytebuf(IByteBuffer buf,long current)
89 | {
90 | kcp.LastRecieveTime = current;
91 | try
92 | {
93 | kcp.getKcpListener().handleReceive(buf, kcp);
94 | }
95 | catch (Exception throwable)
96 | {
97 | kcp.getKcpListener().handleException(throwable, kcp);
98 | }
99 | finally
100 | {
101 | buf.Release();
102 | }
103 | }
104 |
105 | private void release()
106 | {
107 | kcp.ReadProcessing.Set(false);
108 | kcp = null;
109 | recyclerHandle.Release(this);
110 | }
111 | }
112 | }
--------------------------------------------------------------------------------
/dotNetty-kcp/ScheduleTask.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using base_kcp;
3 | using DotNetty.Common.Utilities;
4 | using dotNetty_kcp.thread;
5 |
6 | namespace dotNetty_kcp
7 | {
8 | public class ScheduleTask:IScheduleTask,ITask
9 | {
10 | private readonly IMessageExecutor _imessageExecutor;
11 |
12 | private readonly IChannelManager _channelManager;
13 |
14 | private readonly Ukcp _ukcp;
15 |
16 | private readonly IScheduleThread _scheduleThread;
17 |
18 |
19 | public ScheduleTask(IChannelManager channelManager, Ukcp ukcp, IScheduleThread scheduleThread)
20 | {
21 | _imessageExecutor = ukcp.IMessageExecutor;
22 | _channelManager = channelManager;
23 | _ukcp = ukcp;
24 | _scheduleThread = scheduleThread;
25 | }
26 |
27 | public void execute()
28 | {
29 | try
30 | {
31 | long now = _ukcp.currentMs();
32 | //判断连接是否关闭
33 | if (_ukcp.TimeoutMillis != 0 && now - _ukcp.TimeoutMillis > _ukcp.LastRecieveTime) {
34 | _ukcp.internalClose();
35 | }
36 | if(!_ukcp.isActive()){
37 | var user = _ukcp.user();
38 | //抛回网络线程处理连接删除
39 | user.Channel.EventLoop.Execute(() => {_channelManager.del(_ukcp);});
40 | _ukcp.release();
41 | return;
42 | }
43 | long timeLeft = _ukcp.getTsUpdate()-now;
44 | //判断执行时间是否到了
45 | if(timeLeft>0){
46 | //System.err.println(timeLeft);
47 | _scheduleThread.schedule(this, TimeSpan.FromMilliseconds(timeLeft));
48 | return;
49 | }
50 |
51 | //long start = System.currentTimeMillis();
52 | long next = _ukcp.flush(now);
53 | //System.err.println(next);
54 | //System.out.println("耗时 "+(System.currentTimeMillis()-start));
55 | _scheduleThread.schedule(this, TimeSpan.FromMilliseconds(next));
56 |
57 | //检测写缓冲区 如果能写则触发写事件
58 | if(!_ukcp.WriteQueue.IsEmpty&&_ukcp.canSend(false)
59 | ){
60 | _ukcp.notifyWriteEvent();
61 | }
62 | } catch (Exception e) {
63 | Console.WriteLine(e);
64 | }
65 | }
66 |
67 | public void Run(ITimeout timeout)
68 | {
69 | _imessageExecutor.execute(this);
70 | }
71 |
72 | public void Run()
73 | {
74 | _imessageExecutor.execute(this);
75 | }
76 | }
77 | }
--------------------------------------------------------------------------------
/dotNetty-kcp/ServerChannelHandler.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using base_kcp;
3 | using DotNetty.Transport.Channels;
4 | using DotNetty.Transport.Channels.Sockets;
5 | using dotNetty_kcp.thread;
6 | using DotNetty.Buffers;
7 | using fec;
8 | using fec.fec;
9 |
10 | namespace dotNetty_kcp
11 | {
12 | public class ServerChannelHandler:ChannelHandlerAdapter
13 | {
14 | private readonly IChannelManager _channelManager;
15 |
16 | private readonly ChannelConfig _channelConfig ;
17 |
18 | private readonly IExecutorPool _executorPool;
19 |
20 | private readonly KcpListener _kcpListener;
21 | private readonly IScheduleThread _scheduleThread;
22 |
23 |
24 | public ServerChannelHandler(IChannelManager channelManager, ChannelConfig channelConfig, IExecutorPool executorPool, KcpListener kcpListener, IScheduleThread scheduleThread)
25 | {
26 | _channelManager = channelManager;
27 | _channelConfig = channelConfig;
28 | _executorPool = executorPool;
29 | _kcpListener = kcpListener;
30 | _scheduleThread = scheduleThread;
31 | }
32 |
33 | public override void ExceptionCaught(IChannelHandlerContext context, Exception exception)
34 | {
35 | Console.WriteLine(exception);
36 | }
37 |
38 |
39 | public override void ChannelRead(IChannelHandlerContext context, object message)
40 | {
41 | var msg = (DatagramPacket) message;
42 | var channel = context.Channel;
43 | var ukcp = _channelManager.get(msg);
44 | var content = msg.Content;
45 | User user;
46 | if (ukcp != null)
47 | {
48 | user = ukcp.user();
49 | //每次收到消息重绑定地址
50 | user.RemoteAddress = msg.Sender;
51 | ukcp.read(content);
52 | return;
53 | }
54 |
55 |
56 | //如果是新连接第一个包的sn必须为0
57 | var sn = getSn(content,_channelConfig);
58 | if(sn!=0)
59 | {
60 | msg.Release();
61 | return;
62 | }
63 |
64 | var messageExecutor = _executorPool.GetAutoMessageExecutor();
65 | KcpOutput kcpOutput = new KcpOutPutImp();
66 |
67 | ReedSolomon reedSolomon = null;
68 | if(_channelConfig.FecDataShardCount!=0&&_channelConfig.FecParityShardCount!=0){
69 | reedSolomon = ReedSolomon.create(_channelConfig.FecDataShardCount,_channelConfig.FecParityShardCount);
70 | }
71 |
72 | ukcp = new Ukcp(kcpOutput,_kcpListener,messageExecutor,reedSolomon,_channelConfig);
73 |
74 | user = new User(channel,msg.Sender,msg.Recipient);
75 | ukcp.user(user);
76 | _channelManager.New(msg.Sender,ukcp,msg);
77 |
78 | messageExecutor.execute(new ConnectTask(ukcp, _kcpListener));
79 |
80 | ukcp.read(content);
81 |
82 | var scheduleTask = new ScheduleTask(_channelManager,ukcp,_scheduleThread);
83 | _scheduleThread.schedule(scheduleTask, TimeSpan.FromMilliseconds(ukcp.getInterval()));
84 | }
85 |
86 |
87 | private int getSn(IByteBuffer byteBuf,ChannelConfig channelConfig){
88 | var headerSize = 0;
89 | if (channelConfig.Crc32Check)
90 | {
91 | headerSize+=Ukcp.HEADER_CRC;
92 | }
93 | if(channelConfig.FecDataShardCount!=0&&channelConfig.FecParityShardCount!=0){
94 | headerSize+= Fec.fecHeaderSizePlus2;
95 | }
96 | var sn = byteBuf.GetIntLE(byteBuf.ReaderIndex+Kcp.IKCP_SN_OFFSET+headerSize);
97 | return sn;
98 | }
99 |
100 |
101 | }
102 | }
--------------------------------------------------------------------------------
/dotNetty-kcp/ServerConvChannelManager.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Concurrent;
3 | using System.Collections.Generic;
4 | using System.Collections.ObjectModel;
5 | using System.Net;
6 | using System.Xml.Linq;
7 | using DotNetty.Transport.Channels;
8 | using DotNetty.Transport.Channels.Sockets;
9 |
10 | namespace dotNetty_kcp
11 | {
12 | /**
13 | * 根据conv确定一个session
14 | */
15 | public class ServerConvChannelManager : IChannelManager
16 | {
17 |
18 | private readonly ConcurrentDictionary _ukcps = new ConcurrentDictionary();
19 |
20 | private readonly int convIndex;
21 |
22 | public ServerConvChannelManager(int convIndex)
23 | {
24 | this.convIndex = convIndex;
25 | }
26 |
27 | public Ukcp get(DatagramPacket msg)
28 | {
29 | var conv = getConv(msg);
30 | _ukcps.TryGetValue(conv, out var ukcp);
31 | return ukcp;
32 | }
33 |
34 | private int getConv(DatagramPacket msg) {
35 | var bytebuffer = msg.Content;
36 | return bytebuffer.GetIntLE(convIndex);;
37 | }
38 |
39 |
40 |
41 | public void New(EndPoint endPoint, Ukcp ukcp,DatagramPacket msg)
42 | {
43 | var conv = ukcp.getConv();
44 | if (msg != null) {
45 | conv = getConv(msg);
46 | ukcp.setConv(conv);
47 | }
48 | _ukcps.TryAdd(conv, ukcp);
49 | }
50 |
51 | public void del(Ukcp ukcp)
52 | {
53 | _ukcps.TryRemove(ukcp.getConv(), out var temp);
54 | if (temp == null)
55 | {
56 | Console.WriteLine("ukcp session is not exist conv: " + ukcp.getConv());
57 | }
58 | }
59 |
60 | public ICollection getAll()
61 | {
62 | return this._ukcps.Values;
63 | }
64 | }
65 | }
--------------------------------------------------------------------------------
/dotNetty-kcp/ServerEndPointChannelManager.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Concurrent;
3 | using System.Collections.Generic;
4 | using System.Net;
5 | using DotNetty.Transport.Channels.Sockets;
6 |
7 | namespace dotNetty_kcp
8 | {
9 | public class ServerEndPointChannelManager : IChannelManager
10 | {
11 | private readonly ConcurrentDictionary _ukcps = new ConcurrentDictionary();
12 |
13 | public Ukcp get(DatagramPacket msg)
14 | {
15 | _ukcps.TryGetValue(msg.Sender, out var ukcp);
16 | return ukcp;
17 | }
18 |
19 | public void New(EndPoint endPoint, Ukcp ukcp, DatagramPacket msg)
20 | {
21 | _ukcps[endPoint] = ukcp;
22 | }
23 |
24 | public void del(Ukcp ukcp)
25 | {
26 | _ukcps.TryRemove(ukcp.user().RemoteAddress, out var temp);
27 | if (temp == null)
28 | {
29 | Console.WriteLine("ukcp session is not exist RemoteAddress: " + ukcp.user().RemoteAddress);
30 | }
31 | }
32 |
33 | public ICollection getAll()
34 | {
35 | return _ukcps.Values;
36 | }
37 | }
38 | }
--------------------------------------------------------------------------------
/dotNetty-kcp/User.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Net;
3 | using DotNetty.Transport.Channels;
4 |
5 | namespace dotNetty_kcp
6 | {
7 | public class User
8 | {
9 |
10 | private IChannel channel;
11 | private EndPoint remoteAddress;
12 | private EndPoint localAddress;
13 |
14 | private object Object;
15 |
16 |
17 | public User(IChannel channel, EndPoint remoteAddress, EndPoint localAddress)
18 | {
19 | this.channel = channel;
20 | this.remoteAddress = remoteAddress;
21 | this.localAddress = localAddress;
22 | }
23 |
24 |
25 | public IChannel Channel
26 | {
27 | get => channel;
28 | set => channel = value;
29 | }
30 |
31 |
32 | public EndPoint RemoteAddress
33 | {
34 | get => remoteAddress;
35 | set => remoteAddress = value;
36 | }
37 |
38 | public EndPoint LocalAddress
39 | {
40 | get => localAddress;
41 | set => localAddress = value;
42 | }
43 |
44 | public object O
45 | {
46 | get => Object;
47 | set => Object = value;
48 | }
49 | }
50 | }
--------------------------------------------------------------------------------
/dotNetty-kcp/WriteTask.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.IO;
3 | using DotNetty.Buffers;
4 | using DotNetty.Common;
5 | using dotNetty_kcp.thread;
6 |
7 | namespace dotNetty_kcp
8 | {
9 | public class WriteTask : ITask
10 | {
11 | private Ukcp kcp;
12 |
13 | private static readonly ThreadLocalPool RECYCLER =
14 | new ThreadLocalPool(handle => new WriteTask(handle));
15 |
16 | private readonly ThreadLocalPool.Handle recyclerHandle;
17 |
18 | private WriteTask(ThreadLocalPool.Handle recyclerHandle)
19 | {
20 | this.recyclerHandle = recyclerHandle;
21 | }
22 |
23 | public static WriteTask New(Ukcp kcp)
24 | {
25 | WriteTask recieveTask = RECYCLER.Take();
26 | recieveTask.kcp = kcp;
27 | return recieveTask;
28 | }
29 |
30 |
31 | public void execute()
32 | {
33 | try
34 | {
35 | //查看连接状态
36 | if (!kcp.isActive())
37 | {
38 | return;
39 | }
40 |
41 | //从发送缓冲区到kcp缓冲区
42 | var writeQueue = kcp.WriteQueue;
43 | IByteBuffer byteBuf = null;
44 | while (kcp.canSend(false))
45 | {
46 | if (!writeQueue.TryDequeue(out byteBuf))
47 | {
48 | break;
49 | }
50 | try
51 | {
52 | this.kcp.send(byteBuf);
53 | byteBuf.Release();
54 | }
55 | catch (IOException e)
56 | {
57 | kcp.getKcpListener().handleException(e, kcp);
58 | return;
59 | }
60 | }
61 |
62 | //如果有发送 则检测时间
63 | if (kcp.canSend(false) && (!kcp.checkFlush() || !kcp.isFastFlush()))
64 | {
65 | return;
66 | }
67 |
68 | long now = kcp.currentMs();
69 | long next = kcp.flush(now);
70 | //System.out.println(next);
71 | //System.out.println("耗时"+(System.currentTimeMillis()-now));
72 | kcp.setTsUpdate(now + next);
73 | }
74 | catch (Exception e)
75 | {
76 | Console.WriteLine(e);
77 | }
78 | finally
79 | {
80 | release();
81 | }
82 | }
83 |
84 | private void release()
85 | {
86 | kcp.WriteProcessing.Set(false);
87 | kcp = null;
88 | recyclerHandle.Release(this);
89 | }
90 | }
91 | }
--------------------------------------------------------------------------------
/dotNetty-kcp/dotNetty-kcp.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netstandard2.0
5 | dotNetty_kcp
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 | ..\base-kcp\fec\reedsolomon_csharp.dll
22 |
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/dotNetty-kcp/obj/Debug/netcoreapp2.2/dotNetty-kcp.AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | //------------------------------------------------------------------------------
2 | //
3 | // This code was generated by a tool.
4 | //
5 | // Changes to this file may cause incorrect behavior and will be lost if
6 | // the code is regenerated.
7 | //
8 | //------------------------------------------------------------------------------
9 |
10 | using System;
11 | using System.Reflection;
12 |
13 | [assembly: System.Reflection.AssemblyCompanyAttribute("dotNetty-kcp")]
14 | [assembly: System.Reflection.AssemblyConfigurationAttribute("Debug")]
15 | [assembly: System.Reflection.AssemblyFileVersionAttribute("1.0.0.0")]
16 | [assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0")]
17 | [assembly: System.Reflection.AssemblyProductAttribute("dotNetty-kcp")]
18 | [assembly: System.Reflection.AssemblyTitleAttribute("dotNetty-kcp")]
19 | [assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")]
20 |
21 | // Generated by the MSBuild WriteCodeFragment class.
22 |
23 |
--------------------------------------------------------------------------------
/dotNetty-kcp/obj/Debug/netcoreapp2.2/dotNetty-kcp.AssemblyInfoInputs.cache:
--------------------------------------------------------------------------------
1 | 9cb1c9051ff4ced2c885876a0ec47666c808d9c7
2 |
--------------------------------------------------------------------------------
/dotNetty-kcp/obj/Debug/netcoreapp2.2/dotNetty-kcp.assets.cache:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/l42111996/csharp-kcp/cc7e42dbc2979c09e87625f5328e466e31fde744/dotNetty-kcp/obj/Debug/netcoreapp2.2/dotNetty-kcp.assets.cache
--------------------------------------------------------------------------------
/dotNetty-kcp/obj/Debug/netcoreapp2.2/dotNetty-kcp.csproj.FileListAbsolute.txt:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/l42111996/csharp-kcp/cc7e42dbc2979c09e87625f5328e466e31fde744/dotNetty-kcp/obj/Debug/netcoreapp2.2/dotNetty-kcp.csproj.FileListAbsolute.txt
--------------------------------------------------------------------------------
/dotNetty-kcp/obj/Debug/netcoreapp2.2/dotNetty-kcp.csprojAssemblyReference.cache:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/l42111996/csharp-kcp/cc7e42dbc2979c09e87625f5328e466e31fde744/dotNetty-kcp/obj/Debug/netcoreapp2.2/dotNetty-kcp.csprojAssemblyReference.cache
--------------------------------------------------------------------------------
/dotNetty-kcp/obj/dotNetty-kcp.csproj.nuget.dgspec.json:
--------------------------------------------------------------------------------
1 | {
2 | "format": 1,
3 | "restore": {
4 | "/Users/king/Documents/workspace/github/csharp-kcp/dotNetty-kcp/dotNetty-kcp.csproj": {}
5 | },
6 | "projects": {
7 | "/Users/king/Documents/workspace/github/csharp-kcp/base-kcp/base-kcp.csproj": {
8 | "version": "1.0.0",
9 | "restore": {
10 | "projectUniqueName": "/Users/king/Documents/workspace/github/csharp-kcp/base-kcp/base-kcp.csproj",
11 | "projectName": "base-kcp",
12 | "projectPath": "/Users/king/Documents/workspace/github/csharp-kcp/base-kcp/base-kcp.csproj",
13 | "packagesPath": "/Users/king/.nuget/packages/",
14 | "outputPath": "/Users/king/Documents/workspace/github/csharp-kcp/base-kcp/obj/",
15 | "projectStyle": "PackageReference",
16 | "configFilePaths": [
17 | "/Users/king/.config/NuGet/NuGet.Config"
18 | ],
19 | "originalTargetFrameworks": [
20 | "netcoreapp2.2"
21 | ],
22 | "sources": {
23 | "https://api.nuget.org/v3/index.json": {}
24 | },
25 | "frameworks": {
26 | "netcoreapp2.2": {
27 | "projectReferences": {}
28 | }
29 | },
30 | "warningProperties": {
31 | "warnAsError": [
32 | "NU1605"
33 | ]
34 | }
35 | },
36 | "frameworks": {
37 | "netcoreapp2.2": {
38 | "dependencies": {
39 | "DotNetty.Buffers": {
40 | "target": "Package",
41 | "version": "[0.6.0, )"
42 | },
43 | "Microsoft.NETCore.App": {
44 | "suppressParent": "All",
45 | "target": "Package",
46 | "version": "[2.2.0, )",
47 | "autoReferenced": true
48 | }
49 | },
50 | "imports": [
51 | "net461",
52 | "net462",
53 | "net47",
54 | "net471",
55 | "net472",
56 | "net48"
57 | ],
58 | "assetTargetFallback": true,
59 | "warn": true,
60 | "runtimeIdentifierGraphPath": "/usr/local/share/dotnet/sdk/3.1.102/RuntimeIdentifierGraph.json"
61 | }
62 | }
63 | },
64 | "/Users/king/Documents/workspace/github/csharp-kcp/dotNetty-kcp/dotNetty-kcp.csproj": {
65 | "version": "1.0.0",
66 | "restore": {
67 | "projectUniqueName": "/Users/king/Documents/workspace/github/csharp-kcp/dotNetty-kcp/dotNetty-kcp.csproj",
68 | "projectName": "dotNetty-kcp",
69 | "projectPath": "/Users/king/Documents/workspace/github/csharp-kcp/dotNetty-kcp/dotNetty-kcp.csproj",
70 | "packagesPath": "/Users/king/.nuget/packages/",
71 | "outputPath": "/Users/king/Documents/workspace/github/csharp-kcp/dotNetty-kcp/obj/",
72 | "projectStyle": "PackageReference",
73 | "configFilePaths": [
74 | "/Users/king/.config/NuGet/NuGet.Config"
75 | ],
76 | "originalTargetFrameworks": [
77 | "netcoreapp2.2"
78 | ],
79 | "sources": {
80 | "https://api.nuget.org/v3/index.json": {}
81 | },
82 | "frameworks": {
83 | "netcoreapp2.2": {
84 | "projectReferences": {
85 | "/Users/king/Documents/workspace/github/csharp-kcp/base-kcp/base-kcp.csproj": {
86 | "projectPath": "/Users/king/Documents/workspace/github/csharp-kcp/base-kcp/base-kcp.csproj"
87 | }
88 | }
89 | }
90 | },
91 | "warningProperties": {
92 | "warnAsError": [
93 | "NU1605"
94 | ]
95 | }
96 | },
97 | "frameworks": {
98 | "netcoreapp2.2": {
99 | "dependencies": {
100 | "DotNetty.Codecs": {
101 | "target": "Package",
102 | "version": "[0.6.0, )"
103 | },
104 | "DotNetty.Common": {
105 | "target": "Package",
106 | "version": "[0.6.0, )"
107 | },
108 | "DotNetty.Handlers": {
109 | "target": "Package",
110 | "version": "[0.6.0, )"
111 | },
112 | "DotNetty.Transport": {
113 | "target": "Package",
114 | "version": "[0.6.0, )"
115 | },
116 | "Microsoft.NETCore.App": {
117 | "suppressParent": "All",
118 | "target": "Package",
119 | "version": "[2.2.0, )",
120 | "autoReferenced": true
121 | }
122 | },
123 | "imports": [
124 | "net461",
125 | "net462",
126 | "net47",
127 | "net471",
128 | "net472",
129 | "net48"
130 | ],
131 | "assetTargetFallback": true,
132 | "warn": true,
133 | "runtimeIdentifierGraphPath": "/usr/local/share/dotnet/sdk/3.1.102/RuntimeIdentifierGraph.json"
134 | }
135 | }
136 | }
137 | }
138 | }
--------------------------------------------------------------------------------
/dotNetty-kcp/obj/dotNetty-kcp.csproj.nuget.g.props:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | True
5 | NuGet
6 | $(MSBuildThisFileDirectory)project.assets.json
7 | /Users/king/.nuget/packages/
8 | /Users/king/.nuget/packages/
9 | PackageReference
10 | 5.3.0
11 |
12 |
13 | $(MSBuildAllProjects);$(MSBuildThisFileFullPath)
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/dotNetty-kcp/obj/dotNetty-kcp.csproj.nuget.g.targets:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | $(MSBuildAllProjects);$(MSBuildThisFileFullPath)
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/dotNetty-kcp/obj/project.packagespec.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": "1.0.0",
3 | "restore": {
4 | "projectUniqueName": "/Users/king/Documents/workspace/github/csharp-kcp/dotNetty-kcp/dotNetty-kcp.csproj",
5 | "projectName": "dotNetty-kcp",
6 | "projectPath": "/Users/king/Documents/workspace/github/csharp-kcp/dotNetty-kcp/dotNetty-kcp.csproj",
7 | "outputPath": "/Users/king/Documents/workspace/github/csharp-kcp/dotNetty-kcp/obj/",
8 | "projectStyle": "PackageReference",
9 | "originalTargetFrameworks": [
10 | "netcoreapp2.2"
11 | ],
12 | "sources": {
13 | "https://api.nuget.org/v3/index.json": {}
14 | },
15 | "frameworks": {
16 | "netcoreapp2.2": {
17 | "projectReferences": {
18 | "/Users/king/Documents/workspace/github/csharp-kcp/base-kcp/base-kcp.csproj": {
19 | "projectPath": "/Users/king/Documents/workspace/github/csharp-kcp/base-kcp/base-kcp.csproj"
20 | }
21 | }
22 | }
23 | },
24 | "warningProperties": {
25 | "warnAsError": [
26 | "NU1605"
27 | ]
28 | }
29 | },
30 | "frameworks": {
31 | "netcoreapp2.2": {
32 | "dependencies": {
33 | "DotNetty.Codecs": {
34 | "target": "Package",
35 | "version": "[0.6.0, )"
36 | },
37 | "DotNetty.Common": {
38 | "target": "Package",
39 | "version": "[0.6.0, )"
40 | },
41 | "DotNetty.Handlers": {
42 | "target": "Package",
43 | "version": "[0.6.0, )"
44 | },
45 | "DotNetty.Transport": {
46 | "target": "Package",
47 | "version": "[0.6.0, )"
48 | },
49 | "Microsoft.NETCore.App": {
50 | "suppressParent": "All",
51 | "target": "Package",
52 | "version": "[2.2.0, )",
53 | "autoReferenced": true
54 | }
55 | },
56 | "imports": [
57 | "net461",
58 | "net462",
59 | "net47",
60 | "net471",
61 | "net472",
62 | "net48"
63 | ],
64 | "assetTargetFallback": true,
65 | "warn": true,
66 | "runtimeIdentifierGraphPath": "/usr/local/share/dotnet/sdk/3.1.102/RuntimeIdentifierGraph.json"
67 | }
68 | }
69 | }
--------------------------------------------------------------------------------
/dotNetty-kcp/queue/ConcurrentCircularArrayQueue.cs:
--------------------------------------------------------------------------------
1 | using DotNetty.Common.Internal;
2 | using DotNetty.Common.Utilities;
3 |
4 | namespace base_kcp
5 | {
6 | public abstract class ConcurrentCircularArrayQueue : ConcurrentCircularArrayQueueL0Pad
7 | where T : class
8 | {
9 | protected long Mask;
10 | protected readonly T[] Buffer;
11 |
12 | protected ConcurrentCircularArrayQueue(int capacity)
13 | {
14 | int actualCapacity = IntegerExtensions.RoundUpToPowerOfTwo(capacity);
15 | this.Mask = actualCapacity - 1;
16 | // pad data on either end with some empty slots.
17 | this.Buffer = new T[actualCapacity + RefArrayAccessUtil.RefBufferPad * 2];
18 | }
19 |
20 | ///
21 | /// Calculates an element offset based on a given array index.
22 | ///
23 | /// The desirable element index.
24 | /// The offset in bytes within the array for a given index.
25 | protected long CalcElementOffset(long index) => RefArrayAccessUtil.CalcElementOffset(index, this.Mask);
26 |
27 | ///
28 | /// A plain store (no ordering/fences) of an element to a given offset.
29 | ///
30 | /// Computed via .
31 | /// A kitty.
32 | protected void SpElement(long offset, T e) => RefArrayAccessUtil.SpElement(this.Buffer, offset, e);
33 |
34 | ///
35 | /// An ordered store(store + StoreStore barrier) of an element to a given offset.
36 | ///
37 | /// Computed via .
38 | /// An orderly kitty.
39 | protected void SoElement(long offset, T e) => RefArrayAccessUtil.SoElement(this.Buffer, offset, e);
40 |
41 | ///
42 | /// A plain load (no ordering/fences) of an element from a given offset.
43 | ///
44 | /// Computed via .
45 | /// The element at the offset.
46 | protected T LpElement(long offset) => RefArrayAccessUtil.LpElement(this.Buffer, offset);
47 |
48 | ///
49 | /// A volatile load (load + LoadLoad barrier) of an element from a given offset.
50 | ///
51 | /// Computed via .
52 | /// The element at the offset.
53 | protected T LvElement(long offset) => RefArrayAccessUtil.LvElement(this.Buffer, offset);
54 |
55 | public override void Clear()
56 | {
57 | while (this.TryDequeue(out T _) || !this.IsEmpty)
58 | {
59 | // looping
60 | }
61 | }
62 |
63 | public int Capacity() => (int)(this.Mask + 1);
64 | }
65 |
66 | public abstract class ConcurrentCircularArrayQueueL0Pad : AbstractQueue
67 | {
68 | #pragma warning disable 169 // padded reference
69 | long p00, p01, p02, p03, p04, p05, p06, p07;
70 | long p30, p31, p32, p33, p34, p35, p36, p37;
71 | #pragma warning restore 169
72 | }
73 | }
--------------------------------------------------------------------------------
/dotNetty-kcp/queue/RefArrayAccessUtil.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Threading;
3 |
4 | namespace base_kcp
5 | {
6 | public class RefArrayAccessUtil
7 | {
8 | public static readonly int RefBufferPad = 64 * 2 / IntPtr.Size;
9 |
10 | ///
11 | /// A plain store (no ordering/fences) of an element to a given offset.
12 | ///
13 | /// The element type.
14 | /// The source buffer.
15 | /// Computed via
16 | /// An orderly kitty.
17 | public static void SpElement(T[] buffer, long offset, T e) => buffer[offset] = e;
18 |
19 | ///
20 | /// An ordered store(store + StoreStore barrier) of an element to a given offset.
21 | ///
22 | /// The element type.
23 | /// The source buffer.
24 | /// Computed via
25 | ///
26 | public static void SoElement(T[] buffer, long offset, T e) where T : class => Volatile.Write(ref buffer[offset], e);
27 |
28 | ///
29 | /// A plain load (no ordering/fences) of an element from a given offset.
30 | ///
31 | /// The element type.
32 | /// The source buffer.
33 | /// Computed via
34 | /// The element at the given in the given .
35 | public static T LpElement(T[] buffer, long offset) => buffer[offset];
36 |
37 | ///
38 | /// A volatile load (load + LoadLoad barrier) of an element from a given offset.
39 | ///
40 | /// The element type.
41 | /// The source buffer.
42 | /// Computed via
43 | /// The element at the given in the given .
44 | public static T LvElement(T[] buffer, long offset) where T : class => Volatile.Read(ref buffer[offset]);
45 |
46 | ///
47 | /// Gets the offset in bytes within the array for a given index.
48 | ///
49 | /// The desired element index.
50 | /// Mask for the index.
51 | /// The offset (in bytes) within the array for a given index.
52 | public static long CalcElementOffset(long index, long mask) => RefBufferPad + (index & mask);
53 | }
54 | }
--------------------------------------------------------------------------------
/dotNetty-kcp/thread/AbstratcMessageExecutor.cs:
--------------------------------------------------------------------------------
1 | using System.Threading;
2 | using base_kcp;
3 |
4 | namespace dotNetty_kcp.thread
5 | {
6 | public abstract class AbstratcMessageExecutor:IMessageExecutor
7 | {
8 | private Thread _thread;
9 |
10 | private volatile bool shutdown;
11 | private volatile bool close;
12 |
13 | private readonly object _gate = new object();
14 | private static int id;
15 |
16 |
17 | /**
18 | * 启动消息处理器
19 | */
20 | public virtual void start()
21 | {
22 | _thread = new Thread(run) {Name = "ThreadMessageExecutor-" + id++};
23 | _thread.Start();
24 | }
25 |
26 | /****
27 | *
28 | */
29 | public void stop(bool stopImmediately)
30 | {
31 | if (shutdown)
32 | return;
33 | shutdown = true;
34 | if (stopImmediately)
35 | {
36 | close = true;
37 | lock (_gate)
38 | {
39 | Monitor.Pulse(_gate);
40 | }
41 |
42 | return;
43 | }
44 | while (!isEmpty())
45 | {
46 | Thread.Sleep(1);
47 | }
48 | close = true;
49 | lock (_gate)
50 | {
51 | Monitor.Pulse(_gate);
52 | }
53 | }
54 |
55 | public abstract bool isFull();
56 |
57 | protected abstract bool isEmpty();
58 |
59 | protected abstract bool TryDequeue(out ITask task);
60 |
61 | protected abstract bool TryEnqueue( ITask task);
62 |
63 |
64 | private void run()
65 | {
66 | while (!close)
67 | {
68 | if (TryDequeue(out var task))
69 | {
70 | task.execute();
71 | continue;
72 | }
73 | lock (_gate)
74 | {
75 | Monitor.Wait(_gate);
76 | }
77 | }
78 | }
79 |
80 |
81 |
82 |
83 | public bool execute(ITask iTask)
84 | {
85 | if (shutdown)
86 | return false;
87 | bool result = TryEnqueue(iTask);
88 | lock (_gate)
89 | {
90 | Monitor.Pulse(_gate);
91 | }
92 | return result;
93 | }
94 |
95 | }
96 | }
--------------------------------------------------------------------------------
/dotNetty-kcp/thread/AtomicBoolean.cs:
--------------------------------------------------------------------------------
1 | using System.Threading;
2 |
3 | namespace dotNetty_kcp.thread
4 | {
5 | public class AtomicBoolean
6 | {
7 | private int _value;
8 |
9 | public AtomicBoolean()
10 | : this(false) {
11 |
12 | }
13 |
14 | /// Creates a new AtomicBoolean instance with the initial value provided.
15 | ///
16 | public AtomicBoolean(bool value) {
17 | _value = value ? 1 : 0;
18 | }
19 |
20 | ///
21 | /// This method returns the current value.
22 | ///
23 | ///
24 | /// The bool value to be accessed atomically.
25 | ///
26 | public bool Get() {
27 | return _value != 0;
28 | }
29 |
30 | ///
31 | /// This method sets the current value atomically.
32 | ///
33 | ///
34 | /// The new value to set.
35 | ///
36 | public void Set(bool value) {
37 | Interlocked.Exchange(ref _value, value ? 1 : 0);
38 | }
39 |
40 | ///
41 | /// This method atomically sets the value and returns the original value.
42 | ///
43 | ///
44 | /// The new value.
45 | ///
46 | ///
47 | /// The value before setting to the new value.
48 | ///
49 | public bool GetAndSet(bool value) {
50 | return Interlocked.Exchange(ref _value, value ? 1 : 0) != 0;
51 | }
52 |
53 | ///
54 | /// Atomically sets the value to the given updated value if the current value == the expected value.
55 | ///
56 | ///
57 | /// The value to compare against.
58 | ///
59 | ///
60 | /// The value to set if the value is equal to the expected value.
61 | ///
62 | ///
63 | /// true if the comparison and set was successful. A false indicates the comparison failed.
64 | ///
65 | public bool CompareAndSet(bool expected, bool result) {
66 | int e = expected ? 1 : 0;
67 | int r = result ? 1 : 0;
68 | return Interlocked.CompareExchange(ref _value, r, e) == e;
69 | }
70 |
71 | ///
72 | /// This operator allows an implicit cast from AtomicBoolean to int.
73 | ///
74 | public static implicit operator bool(AtomicBoolean value) {
75 | return value.Get();
76 | }
77 | }
78 | }
--------------------------------------------------------------------------------
/dotNetty-kcp/thread/DistuptorMessageExecutor.cs:
--------------------------------------------------------------------------------
1 | using System.Threading.Tasks;
2 |
3 | namespace dotNetty_kcp.thread
4 | {
5 | /**
6 | *
7 | * 单生产者单消费者 500W tps
8 | *
9 | */
10 | public class DistuptorMessageExecutor:AbstratcMessageExecutor
11 | {
12 | private RingBuffer _ringBuffer;
13 |
14 | private const int MAX_QUEUE_SIZE = 2 << 10;
15 |
16 |
17 | public override void start()
18 | {
19 | _ringBuffer = new RingBuffer(MAX_QUEUE_SIZE);
20 | base.start();
21 | }
22 |
23 |
24 | public override bool isFull()
25 | {
26 | return _ringBuffer.Count == MAX_QUEUE_SIZE;
27 | }
28 |
29 | protected override bool isEmpty()
30 | {
31 | return _ringBuffer.Count == 0;
32 | }
33 |
34 | protected override bool TryDequeue(out ITask task)
35 | {
36 | return _ringBuffer.TryDequeue(out task);
37 | }
38 |
39 | protected override bool TryEnqueue(ITask task)
40 | {
41 | return _ringBuffer.tryEnqueue(task);
42 | }
43 | }
44 | }
--------------------------------------------------------------------------------
/dotNetty-kcp/thread/EventLoopScheduleThread.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using DotNetty.Transport.Channels;
3 |
4 | namespace dotNetty_kcp.thread
5 | {
6 | public class EventLoopScheduleThread : IScheduleThread
7 | {
8 | private readonly IEventLoop _eventLoop = new SingleThreadEventLoop();
9 |
10 |
11 | public void schedule(IScheduleTask scheduleTask, TimeSpan timeSpan)
12 | {
13 | _eventLoop.Schedule(scheduleTask, timeSpan);
14 | }
15 |
16 | public void stop()
17 | {
18 | if (_eventLoop.IsShuttingDown)
19 | {
20 | return;
21 | }
22 | _eventLoop.ShutdownGracefullyAsync();
23 | }
24 | }
25 | }
--------------------------------------------------------------------------------
/dotNetty-kcp/thread/ExecutorPool.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.Threading;
3 | using base_kcp;
4 |
5 | namespace dotNetty_kcp.thread
6 | {
7 | public class ExecutorPool:IExecutorPool
8 | {
9 | private List _messageExecutors = new List();
10 |
11 | private int atomicIndex;
12 |
13 | public IMessageExecutor CreateMessageExecutor()
14 | {
15 | IMessageExecutor executor = new ThreadMessageExecutor();
16 | executor.start();
17 | _messageExecutors.Add(executor);
18 | return executor;
19 | }
20 |
21 | public void stop(bool stopImmediately)
22 | {
23 | foreach (var messageExecutor in _messageExecutors)
24 | {
25 | messageExecutor.stop(stopImmediately);
26 |
27 | }
28 | }
29 |
30 | public IMessageExecutor GetAutoMessageExecutor()
31 | {
32 | Interlocked.Increment(ref atomicIndex);
33 | return _messageExecutors[atomicIndex % _messageExecutors.Count];
34 | }
35 |
36 | public void scheduleTask(IScheduleTask scheduleTask)
37 | {
38 | }
39 | }
40 | }
--------------------------------------------------------------------------------
/dotNetty-kcp/thread/HashedWheelScheduleThread.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using DotNetty.Common.Utilities;
3 |
4 | namespace dotNetty_kcp.thread
5 | {
6 | /**
7 | * netty的实现轮实现,在unity环境下测试会导致cpu跑到50%
8 | * 服务器端使用不错
9 | */
10 | public class HashedWheelScheduleThread:IScheduleThread
11 | {
12 |
13 | private readonly HashedWheelTimer _hashedWheelTimer = new HashedWheelTimer(TimeSpan.FromMilliseconds(1),512,-1 );
14 |
15 | public void schedule(IScheduleTask scheduleTask,TimeSpan timeSpan)
16 | {
17 | _hashedWheelTimer.NewTimeout(scheduleTask,timeSpan);
18 | }
19 |
20 | public void stop()
21 | {
22 | _hashedWheelTimer.StopAsync();
23 | }
24 | }
25 | }
--------------------------------------------------------------------------------
/dotNetty-kcp/thread/IExecutorPool.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace dotNetty_kcp.thread
4 | {
5 | public interface IExecutorPool
6 | {
7 | IMessageExecutor CreateMessageExecutor();
8 |
9 | void stop(bool stopImmediately);
10 |
11 | IMessageExecutor GetAutoMessageExecutor();
12 |
13 |
14 | void scheduleTask(IScheduleTask scheduleTask);
15 |
16 | }
17 | }
--------------------------------------------------------------------------------
/dotNetty-kcp/thread/IMessageExecutor.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace dotNetty_kcp.thread
4 | {
5 | public interface IMessageExecutor
6 | {
7 | /**
8 | * 启动消息处理器
9 | */
10 | void start();
11 |
12 | /**
13 | * 停止消息处理器
14 | * shutdownRightNow false该方法会堵塞当前队列全部执行完再关闭
15 | */
16 | void stop(bool stopImmediately);
17 |
18 | /**
19 | * 判断队列是否已经达到上限了
20 | * @return
21 | */
22 | bool isFull();
23 |
24 | /**
25 | * 执行任务
26 | * 注意: 如果线程等于当前线程 则直接执行 如果非当前线程放进队列
27 | *
28 | * @param iTask
29 | */
30 | bool execute(ITask iTask);
31 |
32 |
33 | }
34 | }
--------------------------------------------------------------------------------
/dotNetty-kcp/thread/IScheduleThread.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using DotNetty.Common.Utilities;
3 |
4 | namespace dotNetty_kcp.thread
5 | {
6 | public interface IScheduleThread
7 | {
8 | void schedule(IScheduleTask scheduleTask,TimeSpan timeSpan);
9 |
10 |
11 | void stop();
12 | }
13 | }
--------------------------------------------------------------------------------
/dotNetty-kcp/thread/ITask.cs:
--------------------------------------------------------------------------------
1 | namespace dotNetty_kcp.thread
2 | {
3 | public interface ITask
4 | {
5 | void execute();
6 | }
7 | }
--------------------------------------------------------------------------------
/dotNetty-kcp/thread/MessageExecutorTest.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using dotNetty_kcp.thread;
3 |
4 | namespace base_kcp
5 | {
6 | public class MessageExecutorTest:ITask
7 | {
8 | private static IMessageExecutor _messageExecutor;
9 |
10 | public int i;
11 |
12 | public static long start = KcpUntils.currentMs();
13 |
14 | private static int index = 0;
15 |
16 | public MessageExecutorTest(int i)
17 | {
18 | this.i = i;
19 | }
20 |
21 | public static int addIndex;
22 |
23 | public static void en()
24 | {
25 | int i = 0;
26 | while (true)
27 | {
28 | var queueTest = new MessageExecutorTest(i);
29 | if (_messageExecutor.execute(queueTest))
30 | {
31 | i++;
32 | }
33 | }
34 | }
35 |
36 |
37 | public void execute()
38 | {
39 | long now = KcpUntils.currentMs();
40 | if (now - start > 1000)
41 | {
42 | Console.WriteLine("i "+(i-index) +"time "+(now-start));
43 | index = i;
44 | start = now;
45 | }
46 | }
47 |
48 | public static void test()
49 | {
50 | _messageExecutor = new DistuptorMessageExecutor();
51 | _messageExecutor.start();
52 | en();
53 | }
54 | }
55 | }
--------------------------------------------------------------------------------
/dotNetty-kcp/thread/RingBuffer.cs:
--------------------------------------------------------------------------------
1 | using System.Runtime.CompilerServices;
2 | using System.Runtime.InteropServices;
3 | using System.Threading;
4 |
5 | namespace dotNetty_kcp.thread
6 | {
7 | public class RingBuffer
8 | {
9 | private readonly T[] _entries;
10 | private readonly int _modMask;
11 | private Volatile.PaddedLong _consumerCursor = new Volatile.PaddedLong();
12 | private Volatile.PaddedLong _producerCursor = new Volatile.PaddedLong();
13 |
14 | ///
15 | /// Creates a new RingBuffer with the given capacity
16 | ///
17 | /// The capacity of the buffer
18 | /// Only a single thread may attempt to consume at any one time
19 | public RingBuffer(int capacity)
20 | {
21 | capacity = NextPowerOfTwo(capacity);
22 | _modMask = capacity - 1;
23 | _entries = new T[capacity];
24 | }
25 |
26 | ///
27 | /// The maximum number of items that can be stored
28 | ///
29 | public int Capacity
30 | {
31 | get { return _entries.Length; }
32 | }
33 |
34 | public T this[long index]
35 | {
36 | get
37 | {
38 | unchecked
39 | {
40 | return _entries[index & _modMask];
41 | }
42 | }
43 | set
44 | {
45 | unchecked
46 | {
47 | _entries[index & _modMask] = value;
48 | }
49 | }
50 | }
51 |
52 | ///
53 | /// Removes an item from the buffer.
54 | ///
55 | /// The next available item
56 | public T Dequeue()
57 | {
58 | var next = _consumerCursor.ReadAcquireFence() + 1;
59 | while (_producerCursor.ReadAcquireFence() < next
60 | ) // makes sure we read the data from _entries after we have read the producer cursor
61 | {
62 | Thread.SpinWait(1);
63 | }
64 |
65 | var result = this[next];
66 | _consumerCursor
67 | .WriteReleaseFence(
68 | next); // makes sure we read the data from _entries before we update the consumer cursor
69 | return result;
70 | }
71 |
72 | ///
73 | /// Attempts to remove an items from the queue
74 | ///
75 | /// the items
76 | /// True if successful
77 | public bool TryDequeue(out T obj)
78 | {
79 | var next = _consumerCursor.ReadAcquireFence() + 1;
80 |
81 | if (_producerCursor.ReadAcquireFence() < next)
82 | {
83 | obj = default(T);
84 | return false;
85 | }
86 |
87 | obj = Dequeue();
88 | return true;
89 | }
90 |
91 | ///
92 | /// Add an item to the buffer
93 | ///
94 | ///
95 | public void Enqueue(T item)
96 | {
97 | var next = _producerCursor.ReadAcquireFence() + 1;
98 |
99 | long wrapPoint = next - _entries.Length;
100 | long min = _consumerCursor.ReadAcquireFence();
101 |
102 | while (wrapPoint > min)
103 | {
104 | min = _consumerCursor.ReadAcquireFence();
105 | Thread.SpinWait(1);
106 | }
107 |
108 | this[next] = item;
109 | _producerCursor
110 | .WriteReleaseFence(
111 | next); // makes sure we write the data in _entries before we update the producer cursor
112 | }
113 |
114 | ///
115 | /// Add an item to the buffer
116 | ///
117 | ///
118 | public bool tryEnqueue(T item)
119 | {
120 | var next = _producerCursor.ReadAcquireFence() + 1;
121 |
122 | long wrapPoint = next - _entries.Length;
123 | long min = _consumerCursor.ReadAcquireFence();
124 |
125 | if (wrapPoint>min)
126 | {
127 | return false;
128 | }
129 |
130 | this[next] = item;
131 | _producerCursor
132 | .WriteReleaseFence(
133 | next); // makes sure we write the data in _entries before we update the producer cursor
134 | return true;
135 | }
136 |
137 |
138 | ///
139 | /// The number of items in the buffer
140 | ///
141 | /// for indicative purposes only, may contain stale data
142 | public int Count
143 | {
144 | get { return (int) (_producerCursor.ReadFullFence() - _consumerCursor.ReadFullFence()); }
145 | }
146 |
147 | private static int NextPowerOfTwo(int x)
148 | {
149 | var result = 2;
150 | while (result < x)
151 | {
152 | result <<= 1;
153 | }
154 |
155 | return result;
156 | }
157 | }
158 |
159 | public static class Volatile
160 | {
161 | private const int CacheLineSize = 64;
162 |
163 | [StructLayout(LayoutKind.Explicit, Size = CacheLineSize * 2)]
164 | public struct PaddedLong
165 | {
166 | [FieldOffset(CacheLineSize)] private long _value;
167 |
168 | ///
169 | /// Create a new with the given initial value.
170 | ///
171 | /// Initial value
172 | public PaddedLong(long value)
173 | {
174 | _value = value;
175 | }
176 |
177 | ///
178 | /// Read the value without applying any fence
179 | ///
180 | /// The current value
181 | public long ReadUnfenced()
182 | {
183 | return _value;
184 | }
185 |
186 | ///
187 | /// Read the value applying acquire fence semantic
188 | ///
189 | /// The current value
190 | public long ReadAcquireFence()
191 | {
192 | var value = _value;
193 | Thread.MemoryBarrier();
194 | return value;
195 | }
196 |
197 | ///
198 | /// Read the value applying full fence semantic
199 | ///
200 | /// The current value
201 | public long ReadFullFence()
202 | {
203 | Thread.MemoryBarrier();
204 | return _value;
205 | }
206 |
207 | ///
208 | /// Read the value applying a compiler only fence, no CPU fence is applied
209 | ///
210 | /// The current value
211 | [MethodImpl(MethodImplOptions.NoOptimization)]
212 | public long ReadCompilerOnlyFence()
213 | {
214 | return _value;
215 | }
216 |
217 | ///
218 | /// Write the value applying release fence semantic
219 | ///
220 | /// The new value
221 | public void WriteReleaseFence(long newValue)
222 | {
223 | Thread.MemoryBarrier();
224 | _value = newValue;
225 | }
226 |
227 | ///
228 | /// Write the value applying full fence semantic
229 | ///
230 | /// The new value
231 | public void WriteFullFence(long newValue)
232 | {
233 | Thread.MemoryBarrier();
234 | _value = newValue;
235 | }
236 |
237 | ///
238 | /// Write the value applying a compiler fence only, no CPU fence is applied
239 | ///
240 | /// The new value
241 | [MethodImpl(MethodImplOptions.NoOptimization)]
242 | public void WriteCompilerOnlyFence(long newValue)
243 | {
244 | _value = newValue;
245 | }
246 |
247 | ///
248 | /// Write without applying any fence
249 | ///
250 | /// The new value
251 | public void WriteUnfenced(long newValue)
252 | {
253 | _value = newValue;
254 | }
255 |
256 | ///
257 | /// Atomically set the value to the given updated value if the current value equals the comparand
258 | ///
259 | /// The new value
260 | /// The comparand (expected value)
261 | ///
262 | public bool AtomicCompareExchange(long newValue, long comparand)
263 | {
264 | return Interlocked.CompareExchange(ref _value, newValue, comparand) == comparand;
265 | }
266 |
267 | ///
268 | /// Atomically set the value to the given updated value
269 | ///
270 | /// The new value
271 | /// The original value
272 | public long AtomicExchange(long newValue)
273 | {
274 | return Interlocked.Exchange(ref _value, newValue);
275 | }
276 |
277 | ///
278 | /// Atomically add the given value to the current value and return the sum
279 | ///
280 | /// The value to be added
281 | /// The sum of the current value and the given value
282 | public long AtomicAddAndGet(long delta)
283 | {
284 | return Interlocked.Add(ref _value, delta);
285 | }
286 |
287 | ///
288 | /// Atomically increment the current value and return the new value
289 | ///
290 | /// The incremented value.
291 | public long AtomicIncrementAndGet()
292 | {
293 | return Interlocked.Increment(ref _value);
294 | }
295 |
296 | ///
297 | /// Atomically increment the current value and return the new value
298 | ///
299 | /// The decremented value.
300 | public long AtomicDecrementAndGet()
301 | {
302 | return Interlocked.Decrement(ref _value);
303 | }
304 |
305 | ///
306 | /// Returns the string representation of the current value.
307 | ///
308 | /// the string representation of the current value.
309 | public override string ToString()
310 | {
311 | var value = ReadFullFence();
312 | return value.ToString();
313 | }
314 | }
315 | }
316 | }
--------------------------------------------------------------------------------
/dotNetty-kcp/thread/ThreadMessageExecutor.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Threading;
4 | using System.Threading.Tasks;
5 | using base_kcp;
6 | using dotNetty_kcp.thread;
7 |
8 | namespace dotNetty_kcp.thread
9 | {
10 | /**
11 | * 多生产者单消费者 560万 tps
12 | */
13 | public class ThreadMessageExecutor:AbstratcMessageExecutor
14 | {
15 |
16 | private MpscArrayQueue _queue;
17 |
18 | private const int MAX_QUEUE_SIZE = 2 << 10;
19 |
20 |
21 | /**
22 | * 启动消息处理器
23 | */
24 | public override void start()
25 | {
26 | _queue = new MpscArrayQueue(MAX_QUEUE_SIZE);
27 | base.start();
28 | }
29 |
30 |
31 | /**
32 | * 判断队列是否已经达到上限了
33 | * @return
34 | */
35 | public override bool isFull()
36 | {
37 | return _queue.Count == MAX_QUEUE_SIZE;
38 | }
39 |
40 | protected override bool isEmpty()
41 | {
42 |
43 | return _queue.IsEmpty;
44 | }
45 |
46 | protected override bool TryDequeue(out ITask task)
47 | {
48 | return _queue.TryDequeue(out task);
49 | }
50 |
51 | protected override bool TryEnqueue(ITask task)
52 | {
53 | return _queue.TryEnqueue(task);
54 | }
55 | }
56 | }
--------------------------------------------------------------------------------
/example-Kcp/KcpRttExampleClient.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Linq;
3 | using System.Net;
4 | using System.Timers;
5 | using base_kcp;
6 | using DotNetty.Buffers;
7 | using dotNetty_kcp;
8 | using fec;
9 |
10 | namespace example_Kcp
11 | {
12 | public class KcpRttExampleClient : KcpListener
13 | {
14 |
15 | public static int fastResend = 0;
16 |
17 | private static Ukcp _ukcp;
18 | public static void start()
19 | {
20 | ChannelConfig channelConfig = new ChannelConfig();
21 | channelConfig.initNodelay(true,40,2,true);
22 | channelConfig.Sndwnd=512;
23 | channelConfig.Rcvwnd=512;
24 | channelConfig.Mtu=512;
25 | channelConfig.FecDataShardCount=3;
26 | channelConfig.FecParityShardCount=1;
27 | channelConfig.AckNoDelay=true;
28 | channelConfig.Crc32Check = true;
29 | channelConfig.Conv = 55;
30 | //channelConfig.setTimeoutMillis(10000);
31 |
32 | KcpClient kcpClient = new KcpClient();
33 | kcpClient.init(channelConfig);
34 |
35 | KcpRttExampleClient kcpClientRttExample = new KcpRttExampleClient();
36 | //kcpClient.connect(new InetSocketAddress("127.0.0.1",20003),channelConfig,kcpClientRttExample);
37 |
38 | EndPoint remoteAddress = new IPEndPoint(IPAddress.Parse("127.0.0.1"),20003);
39 | _ukcp = kcpClient.connect(remoteAddress,channelConfig,kcpClientRttExample);
40 |
41 | try
42 | {
43 | kcpClientRttExample.init();
44 | }
45 | catch (Exception e)
46 | {
47 | Console.WriteLine(e);
48 | throw;
49 | }
50 |
51 |
52 | }
53 | private IByteBuffer data;
54 |
55 | private int[] rtts;
56 |
57 | private volatile int count;
58 |
59 |
60 | private long startTime;
61 |
62 |
63 | Timer timer20 = new Timer();
64 |
65 | ElapsedEventHandler sendhandler;
66 | private ElapsedEventHandler overHandler;
67 |
68 | public void init()
69 | {
70 | sendhandler = new ElapsedEventHandler(sendFunc);
71 | overHandler = new ElapsedEventHandler(overPrint);
72 |
73 | timer20.Elapsed += sendhandler;
74 | }
75 |
76 |
77 | public KcpRttExampleClient()
78 | {
79 | data = Unpooled.DirectBuffer(200);
80 | for (int i = 0; i < data.Capacity; i++)
81 | {
82 | data.WriteByte((byte) i);
83 | }
84 |
85 | rtts = new int[300];
86 | for (int i = 0; i < rtts.Length; i++)
87 | {
88 | rtts[i] = -1;
89 | }
90 |
91 | timer20.Enabled = true;
92 | timer20.Interval = 20;
93 | timer20.Start();
94 | startTime = KcpUntils.currentMs();
95 | }
96 |
97 | private void overPrint(object source, ElapsedEventArgs e)
98 | {
99 | var sum = rtts.Sum();
100 | var max = rtts.Max();
101 | Console.WriteLine("average: " + (sum / rtts.Length)+" max:"+max);
102 | Console.WriteLine(Snmp.snmp.ToString());
103 | timer20.Elapsed -= overHandler;
104 | }
105 |
106 |
107 | private void sendFunc(object source, ElapsedEventArgs e)
108 | {
109 | var byteBuf = rttMsg(++count);
110 | _ukcp.write(byteBuf);
111 | byteBuf.Release();
112 | if (count >= rtts.Length) {
113 | // finish
114 | timer20.Elapsed -= sendhandler;
115 | byteBuf = rttMsg(-1);
116 | _ukcp.write(byteBuf);
117 | byteBuf.Release();
118 | }
119 | }
120 |
121 |
122 |
123 | /**
124 | * count+timestamp+dataLen+data
125 | *
126 | * @param count
127 | * @return
128 | */
129 | public IByteBuffer rttMsg(int count) {
130 | IByteBuffer buf = Unpooled.DirectBuffer(10);
131 | buf.WriteShort(count);
132 | buf.WriteInt((int) (KcpUntils.currentMs()- startTime));
133 |
134 | //int dataLen = new Random().nextInt(200);
135 | //buf.writeBytes(new byte[dataLen]);
136 |
137 | int dataLen = data.ReadableBytes;
138 | buf.WriteShort(dataLen);
139 | buf.WriteBytes(data, data.ReaderIndex, dataLen);
140 | return buf;
141 | }
142 |
143 | public void onConnected(Ukcp ukcp)
144 | {
145 |
146 |
147 | }
148 |
149 | public void handleReceive(IByteBuffer byteBuf, Ukcp ukcp)
150 | {
151 | int curCount = byteBuf.ReadShort();
152 | if (curCount == -1)
153 | {
154 | timer20.Elapsed += overHandler;
155 | }
156 | else
157 | {
158 | int idx = curCount - 1;
159 | long time = byteBuf.ReadInt();
160 | if (rtts[idx] != -1)
161 | {
162 | Console.WriteLine("end");
163 | }
164 | //log.info("rcv count {} {}", curCount, System.currentTimeMillis());
165 | rtts[idx] = (int) (KcpUntils.currentMs() - startTime - time);
166 | Console.WriteLine("rtt : " + curCount + " " + rtts[idx]);
167 | }
168 | }
169 |
170 | public void handleException(Exception ex, Ukcp ukcp)
171 | {
172 | Console.WriteLine(ex);
173 | }
174 |
175 | public void handleClose(Ukcp ukcp)
176 | {
177 | }
178 | }
179 | }
--------------------------------------------------------------------------------
/example-Kcp/KcpRttExampleServer.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Threading;
3 | using DotNetty.Buffers;
4 | using dotNetty_kcp;
5 | using fec;
6 |
7 | namespace example_Kcp
8 | {
9 | public class KcpRttExampleServer:KcpListener
10 | {
11 |
12 | public static void start()
13 | {
14 | KcpRttExampleServer kcpRttExampleServer = new KcpRttExampleServer();
15 |
16 | ChannelConfig channelConfig = new ChannelConfig();
17 | channelConfig.initNodelay(true,40,2,true);
18 | channelConfig.Sndwnd=512;
19 | channelConfig.Rcvwnd=512;
20 | channelConfig.Mtu=512;
21 | channelConfig.FecDataShardCount=3;
22 | channelConfig.FecParityShardCount=1;
23 | channelConfig.AckNoDelay=true;
24 | channelConfig.TimeoutMillis=10000;
25 | channelConfig.UseConvChannel = true;
26 | KcpServer kcpServer = new KcpServer();
27 | kcpServer.init(Environment.ProcessorCount, kcpRttExampleServer,channelConfig,20003);
28 | }
29 |
30 | public void onConnected(Ukcp ukcp)
31 | {
32 |
33 | }
34 |
35 | public void handleReceive(IByteBuffer byteBuf, Ukcp ukcp)
36 | {
37 | short curCount = byteBuf.GetShort(byteBuf.ReaderIndex);
38 | Console.WriteLine(Thread.CurrentThread.Name+" 收到消息 "+curCount);
39 | ukcp.write(byteBuf);
40 | if (curCount == -1) {
41 | ukcp.close();
42 | }
43 | }
44 |
45 | public void handleException(Exception ex, Ukcp ukcp)
46 | {
47 | throw new NotImplementedException();
48 | }
49 |
50 | public void handleClose(Ukcp ukcp)
51 | {
52 | Console.WriteLine(Snmp.snmp.ToString());
53 | Snmp.snmp = new Snmp();
54 | }
55 | }
56 | }
--------------------------------------------------------------------------------
/example-Kcp/Program.cs:
--------------------------------------------------------------------------------
1 | using base_kcp;
2 |
3 | namespace example_Kcp
4 | {
5 | public class Program
6 | {
7 | public static void Main(string[] args)
8 | {
9 |
10 | KcpRttExampleClient.start();
11 |
12 | // KcpRttExampleServer.start();
13 |
14 | // MessageExecutorTest.test();
15 |
16 |
17 | }
18 | }
19 | }
--------------------------------------------------------------------------------
/example-Kcp/example-Kcp.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Exe
5 | netstandard2.0
6 | example_Kcp
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/example-Kcp/obj/Debug/netcoreapp2.2/example-Kcp.AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | //------------------------------------------------------------------------------
2 | //
3 | // This code was generated by a tool.
4 | //
5 | // Changes to this file may cause incorrect behavior and will be lost if
6 | // the code is regenerated.
7 | //
8 | //------------------------------------------------------------------------------
9 |
10 | using System;
11 | using System.Reflection;
12 |
13 | [assembly: System.Reflection.AssemblyCompanyAttribute("example-Kcp")]
14 | [assembly: System.Reflection.AssemblyConfigurationAttribute("Debug")]
15 | [assembly: System.Reflection.AssemblyFileVersionAttribute("1.0.0.0")]
16 | [assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0")]
17 | [assembly: System.Reflection.AssemblyProductAttribute("example-Kcp")]
18 | [assembly: System.Reflection.AssemblyTitleAttribute("example-Kcp")]
19 | [assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")]
20 |
21 | // Generated by the MSBuild WriteCodeFragment class.
22 |
23 |
--------------------------------------------------------------------------------
/example-Kcp/obj/Debug/netcoreapp2.2/example-Kcp.AssemblyInfoInputs.cache:
--------------------------------------------------------------------------------
1 | 0d081404cc91e2afbe91a746948ced0b1dad0925
2 |
--------------------------------------------------------------------------------
/example-Kcp/obj/Debug/netcoreapp2.2/example-Kcp.assets.cache:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/l42111996/csharp-kcp/cc7e42dbc2979c09e87625f5328e466e31fde744/example-Kcp/obj/Debug/netcoreapp2.2/example-Kcp.assets.cache
--------------------------------------------------------------------------------
/example-Kcp/obj/Debug/netcoreapp2.2/example-Kcp.csproj.FileListAbsolute.txt:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/l42111996/csharp-kcp/cc7e42dbc2979c09e87625f5328e466e31fde744/example-Kcp/obj/Debug/netcoreapp2.2/example-Kcp.csproj.FileListAbsolute.txt
--------------------------------------------------------------------------------
/example-Kcp/obj/example-Kcp.csproj.nuget.dgspec.json:
--------------------------------------------------------------------------------
1 | {
2 | "format": 1,
3 | "restore": {
4 | "/Users/king/Documents/workspace/github/csharp-kcp/example-Kcp/example-Kcp.csproj": {}
5 | },
6 | "projects": {
7 | "/Users/king/Documents/workspace/github/csharp-kcp/base-kcp/base-kcp.csproj": {
8 | "version": "1.0.0",
9 | "restore": {
10 | "projectUniqueName": "/Users/king/Documents/workspace/github/csharp-kcp/base-kcp/base-kcp.csproj",
11 | "projectName": "base-kcp",
12 | "projectPath": "/Users/king/Documents/workspace/github/csharp-kcp/base-kcp/base-kcp.csproj",
13 | "packagesPath": "/Users/king/.nuget/packages/",
14 | "outputPath": "/Users/king/Documents/workspace/github/csharp-kcp/base-kcp/obj/",
15 | "projectStyle": "PackageReference",
16 | "configFilePaths": [
17 | "/Users/king/.config/NuGet/NuGet.Config"
18 | ],
19 | "originalTargetFrameworks": [
20 | "netcoreapp2.2"
21 | ],
22 | "sources": {
23 | "https://api.nuget.org/v3/index.json": {}
24 | },
25 | "frameworks": {
26 | "netcoreapp2.2": {
27 | "projectReferences": {}
28 | }
29 | },
30 | "warningProperties": {
31 | "warnAsError": [
32 | "NU1605"
33 | ]
34 | }
35 | },
36 | "frameworks": {
37 | "netcoreapp2.2": {
38 | "dependencies": {
39 | "DotNetty.Buffers": {
40 | "target": "Package",
41 | "version": "[0.6.0, )"
42 | },
43 | "Microsoft.NETCore.App": {
44 | "suppressParent": "All",
45 | "target": "Package",
46 | "version": "[2.2.0, )",
47 | "autoReferenced": true
48 | }
49 | },
50 | "imports": [
51 | "net461",
52 | "net462",
53 | "net47",
54 | "net471",
55 | "net472",
56 | "net48"
57 | ],
58 | "assetTargetFallback": true,
59 | "warn": true,
60 | "runtimeIdentifierGraphPath": "/usr/local/share/dotnet/sdk/3.1.102/RuntimeIdentifierGraph.json"
61 | }
62 | }
63 | },
64 | "/Users/king/Documents/workspace/github/csharp-kcp/dotNetty-kcp/dotNetty-kcp.csproj": {
65 | "version": "1.0.0",
66 | "restore": {
67 | "projectUniqueName": "/Users/king/Documents/workspace/github/csharp-kcp/dotNetty-kcp/dotNetty-kcp.csproj",
68 | "projectName": "dotNetty-kcp",
69 | "projectPath": "/Users/king/Documents/workspace/github/csharp-kcp/dotNetty-kcp/dotNetty-kcp.csproj",
70 | "packagesPath": "/Users/king/.nuget/packages/",
71 | "outputPath": "/Users/king/Documents/workspace/github/csharp-kcp/dotNetty-kcp/obj/",
72 | "projectStyle": "PackageReference",
73 | "configFilePaths": [
74 | "/Users/king/.config/NuGet/NuGet.Config"
75 | ],
76 | "originalTargetFrameworks": [
77 | "netcoreapp2.2"
78 | ],
79 | "sources": {
80 | "https://api.nuget.org/v3/index.json": {}
81 | },
82 | "frameworks": {
83 | "netcoreapp2.2": {
84 | "projectReferences": {
85 | "/Users/king/Documents/workspace/github/csharp-kcp/base-kcp/base-kcp.csproj": {
86 | "projectPath": "/Users/king/Documents/workspace/github/csharp-kcp/base-kcp/base-kcp.csproj"
87 | }
88 | }
89 | }
90 | },
91 | "warningProperties": {
92 | "warnAsError": [
93 | "NU1605"
94 | ]
95 | }
96 | },
97 | "frameworks": {
98 | "netcoreapp2.2": {
99 | "dependencies": {
100 | "DotNetty.Codecs": {
101 | "target": "Package",
102 | "version": "[0.6.0, )"
103 | },
104 | "DotNetty.Common": {
105 | "target": "Package",
106 | "version": "[0.6.0, )"
107 | },
108 | "DotNetty.Handlers": {
109 | "target": "Package",
110 | "version": "[0.6.0, )"
111 | },
112 | "DotNetty.Transport": {
113 | "target": "Package",
114 | "version": "[0.6.0, )"
115 | },
116 | "Microsoft.NETCore.App": {
117 | "suppressParent": "All",
118 | "target": "Package",
119 | "version": "[2.2.0, )",
120 | "autoReferenced": true
121 | }
122 | },
123 | "imports": [
124 | "net461",
125 | "net462",
126 | "net47",
127 | "net471",
128 | "net472",
129 | "net48"
130 | ],
131 | "assetTargetFallback": true,
132 | "warn": true,
133 | "runtimeIdentifierGraphPath": "/usr/local/share/dotnet/sdk/3.1.102/RuntimeIdentifierGraph.json"
134 | }
135 | }
136 | },
137 | "/Users/king/Documents/workspace/github/csharp-kcp/example-Kcp/example-Kcp.csproj": {
138 | "version": "1.0.0",
139 | "restore": {
140 | "projectUniqueName": "/Users/king/Documents/workspace/github/csharp-kcp/example-Kcp/example-Kcp.csproj",
141 | "projectName": "example-Kcp",
142 | "projectPath": "/Users/king/Documents/workspace/github/csharp-kcp/example-Kcp/example-Kcp.csproj",
143 | "packagesPath": "/Users/king/.nuget/packages/",
144 | "outputPath": "/Users/king/Documents/workspace/github/csharp-kcp/example-Kcp/obj/",
145 | "projectStyle": "PackageReference",
146 | "configFilePaths": [
147 | "/Users/king/.config/NuGet/NuGet.Config"
148 | ],
149 | "originalTargetFrameworks": [
150 | "netcoreapp2.2"
151 | ],
152 | "sources": {
153 | "https://api.nuget.org/v3/index.json": {}
154 | },
155 | "frameworks": {
156 | "netcoreapp2.2": {
157 | "projectReferences": {
158 | "/Users/king/Documents/workspace/github/csharp-kcp/dotNetty-kcp/dotNetty-kcp.csproj": {
159 | "projectPath": "/Users/king/Documents/workspace/github/csharp-kcp/dotNetty-kcp/dotNetty-kcp.csproj"
160 | }
161 | }
162 | }
163 | },
164 | "warningProperties": {
165 | "warnAsError": [
166 | "NU1605"
167 | ]
168 | }
169 | },
170 | "frameworks": {
171 | "netcoreapp2.2": {
172 | "dependencies": {
173 | "Microsoft.NETCore.App": {
174 | "suppressParent": "All",
175 | "target": "Package",
176 | "version": "[2.2.0, )",
177 | "autoReferenced": true
178 | }
179 | },
180 | "imports": [
181 | "net461",
182 | "net462",
183 | "net47",
184 | "net471",
185 | "net472",
186 | "net48"
187 | ],
188 | "assetTargetFallback": true,
189 | "warn": true,
190 | "runtimeIdentifierGraphPath": "/usr/local/share/dotnet/sdk/3.1.102/RuntimeIdentifierGraph.json"
191 | }
192 | }
193 | }
194 | }
195 | }
--------------------------------------------------------------------------------
/example-Kcp/obj/example-Kcp.csproj.nuget.g.props:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | True
5 | NuGet
6 | $(MSBuildThisFileDirectory)project.assets.json
7 | /Users/king/.nuget/packages/
8 | /Users/king/.nuget/packages/
9 | PackageReference
10 | 5.3.0
11 |
12 |
13 | $(MSBuildAllProjects);$(MSBuildThisFileFullPath)
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/example-Kcp/obj/example-Kcp.csproj.nuget.g.targets:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | $(MSBuildAllProjects);$(MSBuildThisFileFullPath)
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/example-Kcp/obj/project.packagespec.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": "1.0.0",
3 | "restore": {
4 | "projectUniqueName": "/Users/king/Documents/workspace/github/csharp-kcp/example-Kcp/example-Kcp.csproj",
5 | "projectName": "example-Kcp",
6 | "projectPath": "/Users/king/Documents/workspace/github/csharp-kcp/example-Kcp/example-Kcp.csproj",
7 | "outputPath": "/Users/king/Documents/workspace/github/csharp-kcp/example-Kcp/obj/",
8 | "projectStyle": "PackageReference",
9 | "originalTargetFrameworks": [
10 | "netcoreapp2.2"
11 | ],
12 | "sources": {
13 | "https://api.nuget.org/v3/index.json": {}
14 | },
15 | "frameworks": {
16 | "netcoreapp2.2": {
17 | "projectReferences": {
18 | "/Users/king/Documents/workspace/github/csharp-kcp/dotNetty-kcp/dotNetty-kcp.csproj": {
19 | "projectPath": "/Users/king/Documents/workspace/github/csharp-kcp/dotNetty-kcp/dotNetty-kcp.csproj"
20 | }
21 | }
22 | }
23 | },
24 | "warningProperties": {
25 | "warnAsError": [
26 | "NU1605"
27 | ]
28 | }
29 | },
30 | "frameworks": {
31 | "netcoreapp2.2": {
32 | "dependencies": {
33 | "Microsoft.NETCore.App": {
34 | "suppressParent": "All",
35 | "target": "Package",
36 | "version": "[2.2.0, )",
37 | "autoReferenced": true
38 | }
39 | },
40 | "imports": [
41 | "net461",
42 | "net462",
43 | "net47",
44 | "net471",
45 | "net472",
46 | "net48"
47 | ],
48 | "assetTargetFallback": true,
49 | "warn": true,
50 | "runtimeIdentifierGraphPath": "/usr/local/share/dotnet/sdk/3.1.102/RuntimeIdentifierGraph.json"
51 | }
52 | }
53 | }
--------------------------------------------------------------------------------
/kcp4game/GameChannel.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using doNetty_tcp.code;
3 | using DotNetty.Buffers;
4 | using DotNetty.Transport.Channels;
5 | using DotNetty.Transport.Channels.Sockets;
6 | using dotNetty_kcp;
7 |
8 | namespace kcp4game
9 | {
10 | public class GameChannel
11 | {
12 | private Ukcp _kcpChannel;
13 |
14 | private IChannel _tcpChannel;
15 |
16 | private IProtoDecodeEncode _protoDecodeEncode;
17 |
18 |
19 | public void sendMessage(Message message)
20 | {
21 | var lenth = _protoDecodeEncode.CalculationLenth(message);
22 | var bytebuffer = PooledByteBufferAllocator.Default.DirectBuffer(lenth);
23 | _protoDecodeEncode.encode(bytebuffer,message);
24 |
25 | // if (protocolType == Ukcp.TCP_PROTOCOL)
26 | // {
27 | // _tcpChannel.WriteAndFlushAsync(bytebuffer);
28 | // }
29 | //TODO 缓冲区满了?
30 | try
31 | {
32 | // if (protocolType == Ukcp.UDP_PROTOCOL)
33 | // {
34 | // _kcpChannel.writeUdpMessage(bytebuffer);
35 | // }
36 | // else if(protocolType== Ukcp.KCP_PROTOCOL)
37 | // {
38 | // _kcpChannel.writeMessage(bytebuffer);
39 | // }
40 | }
41 | finally
42 | {
43 | bytebuffer.Release();
44 | }
45 | }
46 | }
47 | }
--------------------------------------------------------------------------------
/kcp4game/GameClientService.cs:
--------------------------------------------------------------------------------
1 | using DotNetty.Buffers;
2 | using DotNetty.Transport.Channels;
3 | using DotNetty.Transport.Channels.Sockets;
4 | using dotNetty_kcp;
5 |
6 | namespace kcp4game
7 | {
8 | public class GameClientService
9 | {
10 | private readonly KcpClient _kcpClient = new KcpClient();
11 |
12 |
13 | }
14 | }
--------------------------------------------------------------------------------
/kcp4game/GameConfig.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Net;
3 | using dotNetty_kcp;
4 |
5 | namespace kcp4game
6 | {
7 | public class GameConfig:ChannelConfig
8 | {
9 | private string tcpIp;
10 | private int tcpPort;
11 |
12 | private EndPoint _remoteEndPoint;
13 |
14 | public GameConfig()
15 | {
16 | }
17 |
18 |
19 | public string TcpIp
20 | {
21 | get => tcpIp;
22 | set => tcpIp = value;
23 | }
24 |
25 | public int TcpPort
26 | {
27 | get => tcpPort;
28 | set => tcpPort = value;
29 | }
30 |
31 | public EndPoint RemoteEndPoint
32 | {
33 | get => _remoteEndPoint;
34 | set => _remoteEndPoint = value;
35 | }
36 | }
37 | }
--------------------------------------------------------------------------------
/kcp4game/GameService.cs:
--------------------------------------------------------------------------------
1 | namespace kcp4game
2 | {
3 | public class GameService
4 | {
5 |
6 |
7 |
8 | }
9 | }
--------------------------------------------------------------------------------
/kcp4game/GameUntils.cs:
--------------------------------------------------------------------------------
1 | namespace kcp4game
2 | {
3 | public class GameUntils
4 | {
5 | }
6 | }
--------------------------------------------------------------------------------
/kcp4game/IGameHandler.cs:
--------------------------------------------------------------------------------
1 | namespace kcp4game
2 | {
3 | public interface IGameHandler
4 | {
5 |
6 | }
7 | }
--------------------------------------------------------------------------------
/kcp4game/KcpService.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using doNetty_tcp.code;
3 | using DotNetty.Buffers;
4 | using dotNetty_kcp;
5 |
6 | namespace dotNetty_kcp
7 | {
8 | public class KcpService:KcpListener
9 | {
10 | private IProtoDecodeEncode _protoDecodeEncode;
11 |
12 | private IMessageManager _messageManager;
13 |
14 |
15 | public void onConnected(Ukcp ukcp)
16 | {
17 |
18 | }
19 |
20 | public void handleReceive(IByteBuffer byteBuf, Ukcp ukcp)
21 | {
22 | var message = _protoDecodeEncode.decode(byteBuf);
23 | var handler = _messageManager.getHandler(message.MessageId);
24 | handler.handler(message);
25 | }
26 |
27 | public void handleException(Exception ex, Ukcp ukcp)
28 | {
29 |
30 |
31 | }
32 |
33 | public void handleClose(Ukcp ukcp)
34 | {
35 |
36 |
37 | }
38 |
39 | }
40 | }
--------------------------------------------------------------------------------
/kcp4game/Message.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using base_kcp;
3 | using DotNetty.Buffers;
4 |
5 | namespace kcp4game
6 | {
7 | public class Message
8 | {
9 | /**
10 | * Ukcp.KCP_PROTOCOL
11 | */
12 | private int protocolType;
13 |
14 | private int messageId;
15 |
16 | private T body;
17 |
18 | private Message()
19 | {
20 | }
21 |
22 | public static Message newMessage()
23 | {
24 | return new Message();
25 | }
26 |
27 | public int ProtocolType
28 | {
29 | get => protocolType;
30 | set => protocolType = value;
31 | }
32 |
33 | public int MessageId
34 | {
35 | get => messageId;
36 | set => messageId = value;
37 | }
38 |
39 | public T Body
40 | {
41 | get => body;
42 | set => body = value;
43 | }
44 | }
45 | }
--------------------------------------------------------------------------------
/kcp4game/Program.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace kcp4game
4 | {
5 | class Program
6 | {
7 | static void Main(string[] args)
8 | {
9 | Console.WriteLine("Hello World!");
10 | }
11 | }
12 | }
--------------------------------------------------------------------------------
/kcp4game/TcpClient.cs:
--------------------------------------------------------------------------------
1 | namespace doNetty_tcp
2 | {
3 | public class TcpClient
4 | {
5 |
6 | }
7 | }
--------------------------------------------------------------------------------
/kcp4game/TcpServer.cs:
--------------------------------------------------------------------------------
1 | namespace doNetty_tcp
2 | {
3 | public class TcpServer
4 | {
5 |
6 | }
7 | }
--------------------------------------------------------------------------------
/kcp4game/code/ClientChannelInitializer.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using DotNetty.Codecs;
3 | using DotNetty.Transport.Channels;
4 | using DotNetty.Transport.Channels.Sockets;
5 |
6 | namespace doNetty_tcp.code
7 | {
8 | public class ClientChannelInitializer:ChannelInitializer
9 | {
10 | private IProtoDecodeEncode _protoDecodeEncode;
11 |
12 | private IMessageManager _messageManager;
13 |
14 | protected override void InitChannel(TcpSocketChannel channel)
15 | {
16 | var pipeline = channel.Pipeline;
17 | pipeline.AddLast("frameDecoder", new LengthFieldBasedFrameDecoder(int.MaxValue, 0, 4));
18 | pipeline.AddLast("frameDecoder", new LengthFieldPrepender(4, false));
19 |
20 | pipeline.AddLast("byteToMessageDecoder", new TcpProtoDecode(_protoDecodeEncode));
21 | //encode
22 | pipeline.AddLast("MessageToByteEncoder",new TcpProtoEncode(_protoDecodeEncode));
23 |
24 | pipeline.AddLast("handler", new TcpChannelHandler(_messageManager));
25 |
26 |
27 |
28 | }
29 | }
30 | }
--------------------------------------------------------------------------------
/kcp4game/code/IDistributeManager.cs:
--------------------------------------------------------------------------------
1 | namespace doNetty_tcp.code
2 | {
3 | public interface IDistributeManager
4 | {
5 |
6 | }
7 | }
--------------------------------------------------------------------------------
/kcp4game/code/IHandler.cs:
--------------------------------------------------------------------------------
1 | using DotNetty.Buffers;
2 | using kcp4game;
3 |
4 | namespace doNetty_tcp.code
5 | {
6 | public interface IHandler:IProtoDecodeEncode
7 | {
8 | void handler(Message message);
9 |
10 | object getMessageId();
11 | }
12 | }
--------------------------------------------------------------------------------
/kcp4game/code/IMessageManager.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using DotNetty.Buffers;
3 |
4 | namespace doNetty_tcp.code
5 | {
6 | public interface IMessageManager
7 | {
8 | IHandler getHandler(object messageId);
9 |
10 | void addHandler(IHandler handler);
11 | }
12 | }
--------------------------------------------------------------------------------
/kcp4game/code/IProtoDecodeEncode.cs:
--------------------------------------------------------------------------------
1 | using DotNetty.Buffers;
2 | using kcp4game;
3 |
4 | namespace doNetty_tcp.code
5 | {
6 | public interface IProtoDecodeEncode
7 | {
8 | int CalculationLenth(Message t);
9 |
10 | void encode(IByteBuffer byteBuffer,Message t);
11 |
12 | Message decode(IByteBuffer byteBuffer);
13 | }
14 | }
--------------------------------------------------------------------------------
/kcp4game/code/TcpChannelHandler.cs:
--------------------------------------------------------------------------------
1 | using DotNetty.Transport.Channels;
2 | using dotNetty_kcp;
3 | using kcp4game;
4 |
5 | namespace doNetty_tcp.code
6 | {
7 | public class TcpChannelHandler : SimpleChannelInboundHandler>
8 | {
9 | private readonly IMessageManager _messageManager;
10 |
11 | public TcpChannelHandler(IMessageManager messageManager)
12 | {
13 | _messageManager = messageManager;
14 | }
15 |
16 | protected override void ChannelRead0(IChannelHandlerContext ctx, Message msg)
17 | {
18 | var handler = _messageManager.getHandler(msg.MessageId);
19 | // msg.ProtocolType = Ukcp.TCP_PROTOCOL;
20 | handler.handler(msg);
21 | }
22 | }
23 | }
--------------------------------------------------------------------------------
/kcp4game/code/TcpProtoDecode.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using DotNetty.Buffers;
3 | using DotNetty.Codecs;
4 | using DotNetty.Transport.Channels;
5 | using kcp4game;
6 |
7 | namespace doNetty_tcp.code
8 | {
9 | public class TcpProtoDecode : ByteToMessageDecoder
10 | {
11 | private readonly IProtoDecodeEncode _protoDecodeEncode;
12 |
13 | public TcpProtoDecode(IProtoDecodeEncode protoDecodeEncode)
14 | {
15 | _protoDecodeEncode = protoDecodeEncode;
16 | }
17 |
18 | protected override void Decode(IChannelHandlerContext context, IByteBuffer input, List