├── .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 | 24 | -------------------------------------------------------------------------------- /.idea/.idea.csharp-kcp/.idea/inspectionProfiles/profiles_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | -------------------------------------------------------------------------------- /.idea/.idea.csharp-kcp/.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 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 | 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 output) 19 | { 20 | if (!input.IsReadable()) 21 | return; 22 | var message = _protoDecodeEncode.decode(input); 23 | output.Add(message); 24 | } 25 | } 26 | } -------------------------------------------------------------------------------- /kcp4game/code/TcpProtoEncode.cs: -------------------------------------------------------------------------------- 1 | using DotNetty.Buffers; 2 | using DotNetty.Codecs; 3 | using DotNetty.Transport.Channels; 4 | using kcp4game; 5 | 6 | namespace doNetty_tcp.code 7 | { 8 | public class TcpProtoEncode : MessageToByteEncoder> 9 | { 10 | private readonly IProtoDecodeEncode _protoDecodeEncode; 11 | 12 | public TcpProtoEncode(IProtoDecodeEncode protoDecodeEncode) 13 | { 14 | _protoDecodeEncode = protoDecodeEncode; 15 | } 16 | 17 | protected override void Encode(IChannelHandlerContext context, Message message, IByteBuffer output) 18 | { 19 | _protoDecodeEncode.encode(output, message); 20 | } 21 | } 22 | } -------------------------------------------------------------------------------- /kcp4game/kcp4game.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | netstandard2.0 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /kcp4game/obj/Debug/netcoreapp2.2/kcp4game.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("kcp4game")] 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("kcp4game")] 18 | [assembly: System.Reflection.AssemblyTitleAttribute("kcp4game")] 19 | [assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")] 20 | 21 | // Generated by the MSBuild WriteCodeFragment class. 22 | 23 | -------------------------------------------------------------------------------- /kcp4game/obj/Debug/netcoreapp2.2/kcp4game.AssemblyInfoInputs.cache: -------------------------------------------------------------------------------- 1 | 9c7256919add2c34eac9b129d56fa9e4f6647bba 2 | -------------------------------------------------------------------------------- /kcp4game/obj/Debug/netcoreapp2.2/kcp4game.assets.cache: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/l42111996/csharp-kcp/cc7e42dbc2979c09e87625f5328e466e31fde744/kcp4game/obj/Debug/netcoreapp2.2/kcp4game.assets.cache -------------------------------------------------------------------------------- /kcp4game/obj/Debug/netcoreapp2.2/kcp4game.csproj.FileListAbsolute.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/l42111996/csharp-kcp/cc7e42dbc2979c09e87625f5328e466e31fde744/kcp4game/obj/Debug/netcoreapp2.2/kcp4game.csproj.FileListAbsolute.txt -------------------------------------------------------------------------------- /kcp4game/obj/kcp4game.csproj.nuget.dgspec.json: -------------------------------------------------------------------------------- 1 | { 2 | "format": 1, 3 | "restore": { 4 | "/Users/king/Documents/workspace/github/csharp-kcp/kcp4game/kcp4game.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/kcp4game/kcp4game.csproj": { 138 | "version": "1.0.0", 139 | "restore": { 140 | "projectUniqueName": "/Users/king/Documents/workspace/github/csharp-kcp/kcp4game/kcp4game.csproj", 141 | "projectName": "kcp4game", 142 | "projectPath": "/Users/king/Documents/workspace/github/csharp-kcp/kcp4game/kcp4game.csproj", 143 | "packagesPath": "/Users/king/.nuget/packages/", 144 | "outputPath": "/Users/king/Documents/workspace/github/csharp-kcp/kcp4game/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 | } -------------------------------------------------------------------------------- /kcp4game/obj/kcp4game.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 | -------------------------------------------------------------------------------- /kcp4game/obj/kcp4game.csproj.nuget.g.targets: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | $(MSBuildAllProjects);$(MSBuildThisFileFullPath) 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /kcp4game/obj/project.packagespec.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "1.0.0", 3 | "restore": { 4 | "projectUniqueName": "/Users/king/Documents/workspace/github/csharp-kcp/kcp4game/kcp4game.csproj", 5 | "projectName": "kcp4game", 6 | "projectPath": "/Users/king/Documents/workspace/github/csharp-kcp/kcp4game/kcp4game.csproj", 7 | "outputPath": "/Users/king/Documents/workspace/github/csharp-kcp/kcp4game/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 | } --------------------------------------------------------------------------------