using System.Buffers.Binary; using System.Collections.Concurrent; using System.Collections.Generic; using System.Runtime.InteropServices; namespace System.Net.Sockets.Kcp { /// /// 动态申请非托管内存 /// public class SimpleSegManager : ISegmentManager { public static SimpleSegManager Default { get; } = new SimpleSegManager(); public KcpSegment Alloc(int appendDateSize) { return KcpSegment.AllocHGlobal(appendDateSize); } public void Free(KcpSegment seg) { KcpSegment.FreeHGlobal(seg); } public class Kcp : Kcp { public Kcp(uint conv_, IKcpCallback callback, IRentable rentable = null) : base(conv_, callback, rentable) { SegmentManager = Default; } } public class KcpIO : KcpIO { public KcpIO(uint conv_) : base(conv_) { SegmentManager = Default; } } } /// /// 申请固定大小非托管内存。使用这个就不能SetMtu了,大小已经写死。 /// /// 需要大量测试 public unsafe class UnSafeSegManager : ISegmentManager { /// /// 因为默认mtu是1400,并且内存需要内存行/内存页对齐。这里直接512对齐。 /// public const int blockSize = 512 * 3; public readonly object locker = new object(); public Stack blocks = new Stack(); public HashSet header = new HashSet(); public UnSafeSegManager() { Alloc(); } public static UnSafeSegManager Default { get; } = new UnSafeSegManager(); public KcpSegment Alloc(int appendDateSize) { lock (locker) { var total = KcpSegment.LocalOffset + KcpSegment.HeadOffset + appendDateSize; if (total > blockSize) { throw new ArgumentOutOfRangeException(); } if (blocks.Count > 0) { } else { Alloc(); } var ptr = blocks.Pop(); Span span = new Span(ptr.ToPointer(), blockSize); span.Clear(); return new KcpSegment((byte*)ptr.ToPointer(), (uint)appendDateSize); } } public void Free(KcpSegment seg) { IntPtr ptr = (IntPtr)seg.ptr; blocks.Push(ptr); } void Alloc() { int count = 50; IntPtr intPtr = Marshal.AllocHGlobal(blockSize * count); header.Add(intPtr); for (int i = 0; i < count; i++) { blocks.Push(intPtr + blockSize * i); } } ~UnSafeSegManager() { foreach (var item in header) { Marshal.FreeHGlobal(item); } } public class Kcp : Kcp { public Kcp(uint conv_, IKcpCallback callback, IRentable rentable = null) : base(conv_, callback, rentable) { SegmentManager = Default; } } public class KcpIO : KcpIO { public KcpIO(uint conv_) : base(conv_) { SegmentManager = Default; } } } /// /// 使用内存池,而不是非托管内存,有内存alloc,但是不多。可以解决Marshal.AllocHGlobal 内核调用带来的性能问题 /// public class PoolSegManager : ISegmentManager { /// /// 因为默认mtu是1400,并且内存需要内存行/内存页对齐。这里直接512对齐。 /// public const int blockSize = 512 * 3; ConcurrentStack Pool = new ConcurrentStack(); public static PoolSegManager Default { get; } = new PoolSegManager(); public Seg Alloc(int appendDateSize) { if (appendDateSize > blockSize) { throw new NotSupportedException(); } if (Pool.TryPop(out var ret)) { } else { ret = new Seg(blockSize); } ret.len = (uint)appendDateSize; return ret; } public void Free(Seg seg) { seg.cmd = 0; seg.conv = 0; seg.fastack = 0; seg.frg = 0; seg.len = 0; seg.resendts = 0; seg.rto = 0; seg.sn = 0; seg.ts = 0; seg.una = 0; seg.wnd = 0; seg.xmit = 0; Pool.Push(seg); } public class Seg : IKcpSegment { ///以下为需要网络传输的参数 public const int LocalOffset = 4 * 4; public const int HeadOffset = Kcp.IKCP_OVERHEAD; byte[] cache; public Seg(int blockSize) { cache = Buffers.ArrayPool.Shared.Rent(blockSize); } public byte cmd { get; set; } public uint conv { get; set; } public Span data => cache.AsSpan().Slice(0, (int)len); public uint fastack { get; set; } public byte frg { get; set; } public uint len { get; internal set; } public uint resendts { get; set; } public uint rto { get; set; } public uint sn { get; set; } public uint ts { get; set; } public uint una { get; set; } public ushort wnd { get; set; } public uint xmit { get; set; } public int Encode(Span buffer) { var datelen = (int)(HeadOffset + len); ///备用偏移值 现阶段没有使用 const int offset = 0; if (BitConverter.IsLittleEndian) { BinaryPrimitives.WriteUInt32LittleEndian(buffer.Slice(offset), conv); buffer[offset + 4] = cmd; buffer[offset + 5] = frg; BinaryPrimitives.WriteUInt16LittleEndian(buffer.Slice(offset + 6), wnd); BinaryPrimitives.WriteUInt32LittleEndian(buffer.Slice(offset + 8), ts); BinaryPrimitives.WriteUInt32LittleEndian(buffer.Slice(offset + 12), sn); BinaryPrimitives.WriteUInt32LittleEndian(buffer.Slice(offset + 16), una); BinaryPrimitives.WriteUInt32LittleEndian(buffer.Slice(offset + 20), len); data.CopyTo(buffer.Slice(HeadOffset)); } else { BinaryPrimitives.WriteUInt32BigEndian(buffer.Slice(offset), conv); buffer[offset + 4] = cmd; buffer[offset + 5] = frg; BinaryPrimitives.WriteUInt16BigEndian(buffer.Slice(offset + 6), wnd); BinaryPrimitives.WriteUInt32BigEndian(buffer.Slice(offset + 8), ts); BinaryPrimitives.WriteUInt32BigEndian(buffer.Slice(offset + 12), sn); BinaryPrimitives.WriteUInt32BigEndian(buffer.Slice(offset + 16), una); BinaryPrimitives.WriteUInt32BigEndian(buffer.Slice(offset + 20), len); data.CopyTo(buffer.Slice(HeadOffset)); } return datelen; } } public class Kcp : Kcp { public Kcp(uint conv_, IKcpCallback callback, IRentable rentable = null) : base(conv_, callback, rentable) { SegmentManager = Default; } } public class KcpIO : KcpIO { public KcpIO(uint conv_) : base(conv_) { SegmentManager = Default; } } } }