388 lines
10 KiB
C#
388 lines
10 KiB
C#
|
using System.Buffers;
|
|||
|
using BufferOwner = System.Buffers.IMemoryOwner<byte>;
|
|||
|
|
|||
|
namespace System.Net.Sockets.Kcp
|
|||
|
{
|
|||
|
public class Kcp<Segment> : KcpCore<Segment>
|
|||
|
where Segment : IKcpSegment
|
|||
|
{
|
|||
|
/// <summary>
|
|||
|
/// create a new kcp control object, 'conv' must equal in two endpoint
|
|||
|
/// from the same connection.
|
|||
|
/// </summary>
|
|||
|
/// <param name="conv_"></param>
|
|||
|
/// <param name="callback"></param>
|
|||
|
/// <param name="rentable">可租用内存的回调</param>
|
|||
|
public Kcp(uint conv_, IKcpCallback callback, IRentable rentable = null)
|
|||
|
: base(conv_)
|
|||
|
{
|
|||
|
callbackHandle = callback;
|
|||
|
this.rentable = rentable;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
//extension 重构和新增加的部分============================================
|
|||
|
|
|||
|
IRentable rentable;
|
|||
|
/// <summary>
|
|||
|
/// 如果外部能够提供缓冲区则使用外部缓冲区,否则new byte[]
|
|||
|
/// </summary>
|
|||
|
/// <param name="needSize"></param>
|
|||
|
/// <returns></returns>
|
|||
|
internal protected override BufferOwner CreateBuffer(int needSize)
|
|||
|
{
|
|||
|
var res = rentable?.RentBuffer(needSize);
|
|||
|
if (res == null)
|
|||
|
{
|
|||
|
return base.CreateBuffer(needSize);
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
if (res.Memory.Length < needSize)
|
|||
|
{
|
|||
|
throw new ArgumentException($"{nameof(rentable.RentBuffer)} 指定的委托不符合标准,返回的" +
|
|||
|
$"BufferOwner.Memory.Length 小于 {nameof(needSize)}");
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
return res;
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// TryRecv Recv设计上同一时刻只允许一个线程调用。
|
|||
|
/// <para/>因为要保证数据顺序,多个线程同时调用Recv也没有意义。
|
|||
|
/// <para/>所以只需要部分加锁即可。
|
|||
|
/// </summary>
|
|||
|
/// <returns></returns>
|
|||
|
public (BufferOwner buffer, int avalidLength) TryRecv()
|
|||
|
{
|
|||
|
var peekSize = -1;
|
|||
|
lock (rcv_queueLock)
|
|||
|
{
|
|||
|
if (rcv_queue.Count == 0)
|
|||
|
{
|
|||
|
///没有可用包
|
|||
|
return (null, -1);
|
|||
|
}
|
|||
|
|
|||
|
var seq = rcv_queue[0];
|
|||
|
|
|||
|
if (seq.frg == 0)
|
|||
|
{
|
|||
|
peekSize = (int)seq.len;
|
|||
|
}
|
|||
|
|
|||
|
if (rcv_queue.Count < seq.frg + 1)
|
|||
|
{
|
|||
|
///没有足够的包
|
|||
|
return (null, -1);
|
|||
|
}
|
|||
|
|
|||
|
uint length = 0;
|
|||
|
|
|||
|
foreach (var item in rcv_queue)
|
|||
|
{
|
|||
|
length += item.len;
|
|||
|
if (item.frg == 0)
|
|||
|
{
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
peekSize = (int)length;
|
|||
|
|
|||
|
if (peekSize < 0)
|
|||
|
{
|
|||
|
return (null, -2);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
var buffer = CreateBuffer(peekSize);
|
|||
|
var recvlength = UncheckRecv(buffer.Memory.Span);
|
|||
|
return (buffer, recvlength);
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// TryRecv Recv设计上同一时刻只允许一个线程调用。
|
|||
|
/// <para/>因为要保证数据顺序,多个线程同时调用Recv也没有意义。
|
|||
|
/// <para/>所以只需要部分加锁即可。
|
|||
|
/// </summary>
|
|||
|
/// <param name="writer"></param>
|
|||
|
/// <returns></returns>
|
|||
|
public int TryRecv(IBufferWriter<byte> writer)
|
|||
|
{
|
|||
|
var peekSize = -1;
|
|||
|
lock (rcv_queueLock)
|
|||
|
{
|
|||
|
if (rcv_queue.Count == 0)
|
|||
|
{
|
|||
|
///没有可用包
|
|||
|
return -1;
|
|||
|
}
|
|||
|
|
|||
|
var seq = rcv_queue[0];
|
|||
|
|
|||
|
if (seq.frg == 0)
|
|||
|
{
|
|||
|
peekSize = (int)seq.len;
|
|||
|
}
|
|||
|
|
|||
|
if (rcv_queue.Count < seq.frg + 1)
|
|||
|
{
|
|||
|
///没有足够的包
|
|||
|
return -1;
|
|||
|
}
|
|||
|
|
|||
|
uint length = 0;
|
|||
|
|
|||
|
foreach (var item in rcv_queue)
|
|||
|
{
|
|||
|
length += item.len;
|
|||
|
if (item.frg == 0)
|
|||
|
{
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
peekSize = (int)length;
|
|||
|
|
|||
|
if (peekSize < 0)
|
|||
|
{
|
|||
|
return -2;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
return UncheckRecv(writer);
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// user/upper level recv: returns size, returns below zero for EAGAIN
|
|||
|
/// </summary>
|
|||
|
/// <param name="buffer"></param>
|
|||
|
/// <returns></returns>
|
|||
|
public int Recv(Span<byte> buffer)
|
|||
|
{
|
|||
|
if (0 == rcv_queue.Count)
|
|||
|
{
|
|||
|
return -1;
|
|||
|
}
|
|||
|
|
|||
|
var peekSize = PeekSize();
|
|||
|
if (peekSize < 0)
|
|||
|
{
|
|||
|
return -2;
|
|||
|
}
|
|||
|
|
|||
|
if (peekSize > buffer.Length)
|
|||
|
{
|
|||
|
return -3;
|
|||
|
}
|
|||
|
|
|||
|
/// 拆分函数
|
|||
|
var recvLength = UncheckRecv(buffer);
|
|||
|
|
|||
|
return recvLength;
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// user/upper level recv: returns size, returns below zero for EAGAIN
|
|||
|
/// </summary>
|
|||
|
/// <param name="writer"></param>
|
|||
|
/// <returns></returns>
|
|||
|
public int Recv(IBufferWriter<byte> writer)
|
|||
|
{
|
|||
|
if (0 == rcv_queue.Count)
|
|||
|
{
|
|||
|
return -1;
|
|||
|
}
|
|||
|
|
|||
|
var peekSize = PeekSize();
|
|||
|
if (peekSize < 0)
|
|||
|
{
|
|||
|
return -2;
|
|||
|
}
|
|||
|
|
|||
|
//if (peekSize > buffer.Length)
|
|||
|
//{
|
|||
|
// return -3;
|
|||
|
//}
|
|||
|
|
|||
|
/// 拆分函数
|
|||
|
var recvLength = UncheckRecv(writer);
|
|||
|
|
|||
|
return recvLength;
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// 这个函数不检查任何参数
|
|||
|
/// </summary>
|
|||
|
/// <param name="buffer"></param>
|
|||
|
/// <returns></returns>
|
|||
|
int UncheckRecv(Span<byte> buffer)
|
|||
|
{
|
|||
|
var recover = false;
|
|||
|
if (rcv_queue.Count >= rcv_wnd)
|
|||
|
{
|
|||
|
recover = true;
|
|||
|
}
|
|||
|
|
|||
|
#region merge fragment.
|
|||
|
/// merge fragment.
|
|||
|
|
|||
|
var recvLength = 0;
|
|||
|
lock (rcv_queueLock)
|
|||
|
{
|
|||
|
var count = 0;
|
|||
|
foreach (var seg in rcv_queue)
|
|||
|
{
|
|||
|
seg.data.CopyTo(buffer.Slice(recvLength));
|
|||
|
recvLength += (int)seg.len;
|
|||
|
|
|||
|
count++;
|
|||
|
int frg = seg.frg;
|
|||
|
|
|||
|
SegmentManager.Free(seg);
|
|||
|
if (frg == 0)
|
|||
|
{
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
if (count > 0)
|
|||
|
{
|
|||
|
rcv_queue.RemoveRange(0, count);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
#endregion
|
|||
|
|
|||
|
Move_Rcv_buf_2_Rcv_queue();
|
|||
|
|
|||
|
#region fast recover
|
|||
|
/// fast recover
|
|||
|
if (rcv_queue.Count < rcv_wnd && recover)
|
|||
|
{
|
|||
|
// ready to send back IKCP_CMD_WINS in ikcp_flush
|
|||
|
// tell remote my window size
|
|||
|
probe |= IKCP_ASK_TELL;
|
|||
|
}
|
|||
|
#endregion
|
|||
|
return recvLength;
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// 这个函数不检查任何参数
|
|||
|
/// </summary>
|
|||
|
/// <param name="writer"></param>
|
|||
|
/// <returns></returns>
|
|||
|
int UncheckRecv(IBufferWriter<byte> writer)
|
|||
|
{
|
|||
|
var recover = false;
|
|||
|
if (rcv_queue.Count >= rcv_wnd)
|
|||
|
{
|
|||
|
recover = true;
|
|||
|
}
|
|||
|
|
|||
|
#region merge fragment.
|
|||
|
/// merge fragment.
|
|||
|
|
|||
|
var recvLength = 0;
|
|||
|
lock (rcv_queueLock)
|
|||
|
{
|
|||
|
var count = 0;
|
|||
|
foreach (var seg in rcv_queue)
|
|||
|
{
|
|||
|
var len = (int)seg.len;
|
|||
|
var destination = writer.GetSpan(len);
|
|||
|
|
|||
|
seg.data.CopyTo(destination);
|
|||
|
writer.Advance(len);
|
|||
|
|
|||
|
recvLength += len;
|
|||
|
|
|||
|
count++;
|
|||
|
int frg = seg.frg;
|
|||
|
|
|||
|
SegmentManager.Free(seg);
|
|||
|
if (frg == 0)
|
|||
|
{
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
if (count > 0)
|
|||
|
{
|
|||
|
rcv_queue.RemoveRange(0, count);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
#endregion
|
|||
|
|
|||
|
Move_Rcv_buf_2_Rcv_queue();
|
|||
|
|
|||
|
#region fast recover
|
|||
|
/// fast recover
|
|||
|
if (rcv_queue.Count < rcv_wnd && recover)
|
|||
|
{
|
|||
|
// ready to send back IKCP_CMD_WINS in ikcp_flush
|
|||
|
// tell remote my window size
|
|||
|
probe |= IKCP_ASK_TELL;
|
|||
|
}
|
|||
|
#endregion
|
|||
|
return recvLength;
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// check the size of next message in the recv queue
|
|||
|
/// </summary>
|
|||
|
/// <returns></returns>
|
|||
|
public int PeekSize()
|
|||
|
{
|
|||
|
lock (rcv_queueLock)
|
|||
|
{
|
|||
|
if (rcv_queue.Count == 0)
|
|||
|
{
|
|||
|
///没有可用包
|
|||
|
return -1;
|
|||
|
}
|
|||
|
|
|||
|
var seq = rcv_queue[0];
|
|||
|
|
|||
|
if (seq.frg == 0)
|
|||
|
{
|
|||
|
return (int)seq.len;
|
|||
|
}
|
|||
|
|
|||
|
if (rcv_queue.Count < seq.frg + 1)
|
|||
|
{
|
|||
|
///没有足够的包
|
|||
|
return -1;
|
|||
|
}
|
|||
|
|
|||
|
uint length = 0;
|
|||
|
|
|||
|
foreach (var seg in rcv_queue)
|
|||
|
{
|
|||
|
length += seg.len;
|
|||
|
if (seg.frg == 0)
|
|||
|
{
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
return (int)length;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|