upm_guru_kcp/Runtime/csharp-kcp/dotNetty-kcp/Ukcp.cs

634 lines
17 KiB
C#
Raw Normal View History

2023-08-30 05:50:21 +00:00
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.IO;
using base_kcp;
using DotNetty.Buffers;
using DotNetty.Transport.Channels.Sockets;
using dotNetty_kcp.thread;
using DotNetty.Common.Internal;
using fec;
using fec.fec;
namespace dotNetty_kcp
{
public class Ukcp
{
public const int HEADER_CRC = 4, HEADER_NONCESIZE = 16;
private readonly Kcp _kcp;
private bool fastFlush = true;
private long tsUpdate = -1;
private bool _active;
private readonly FecEncode _fecEncode;
private readonly FecDecode _fecDecode;
private readonly ConcurrentQueue<IByteBuffer> _writeQueue;
private readonly MpscArrayQueue<IByteBuffer> _readQueue;
private readonly IMessageExecutor _iMessageExecutor;
private readonly KcpListener _kcpListener;
private readonly long _timeoutMillis;
private readonly AtomicBoolean _readProcessing = new AtomicBoolean();
private readonly AtomicBoolean _writeProcessing = new AtomicBoolean();
private readonly bool _crc32Check;
/**
*
*
**/
internal long LastRecieveTime { get; set; } = KcpUntils.currentMs();
/**
* Creates a new instance.
*
* @param output output for kcp
*/
public Ukcp(KcpOutput output, KcpListener kcpListener, IMessageExecutor iMessageExecutor,
ReedSolomon reedSolomon, ChannelConfig channelConfig)
{
this._timeoutMillis = channelConfig.TimeoutMillis;
this._crc32Check = channelConfig.Crc32Check;
this._kcp = new Kcp(channelConfig.Conv, output);
this._active = true;
this._kcpListener = kcpListener;
this._iMessageExecutor = iMessageExecutor;
//默认2<<11 可以修改
_writeQueue = new ConcurrentQueue<IByteBuffer>();
// <IByteBuffer>(2<<10);
_readQueue = new MpscArrayQueue<IByteBuffer>(2<<10);
//recieveList = new SpscLinkedQueue<>();
int headerSize = 0;
//init crc32
if(channelConfig.Crc32Check){
var kcpOutput = _kcp.Output;
kcpOutput = new Crc32OutPut(kcpOutput,headerSize);
_kcp.Output=kcpOutput;
headerSize+=HEADER_CRC;
}
//init fec
if (reedSolomon != null)
{
var kcpOutput = _kcp.Output;
_fecEncode = new FecEncode(headerSize, reedSolomon, channelConfig.Mtu);
_fecDecode = new FecDecode(3 * reedSolomon.getTotalShardCount(), reedSolomon, channelConfig.Mtu);
kcpOutput = new FecOutPut(kcpOutput, _fecEncode);
_kcp.Output = kcpOutput;
headerSize += Fec.fecHeaderSizePlus2;
}
_kcp.setReserved(headerSize);
initKcpConfig(channelConfig);
}
private void initKcpConfig(ChannelConfig channelConfig)
{
_kcp.initNodelay(channelConfig.Nodelay, channelConfig.Interval, channelConfig.Fastresend,
channelConfig.Nocwnd);
_kcp.SndWnd = channelConfig.Sndwnd;
_kcp.RcvWnd = channelConfig.Rcvwnd;
_kcp.Mtu = channelConfig.Mtu;
_kcp.Stream = channelConfig.Stream;
_kcp.AckNoDelay = channelConfig.AckNoDelay;
_kcp.setAckMaskSize(channelConfig.AckMaskSize);
fastFlush = channelConfig.FastFlush;
}
/**
* Receives ByteBufs.
*
* @param bufList received IByteBuffer will be add to the list
*/
protected internal void receive(List<IByteBuffer> bufList)
{
_kcp.recv(bufList);
}
protected internal IByteBuffer mergeReceive()
{
return _kcp.mergeRecv();
}
internal void input(IByteBuffer data, long current)
{
// _lastRecieveTime = KcpUntils.currentMs();
Snmp.snmp.InPkts++;
Snmp.snmp.InBytes += data.ReadableBytes;
if (_crc32Check)
{
long checksum = data.ReadUnsignedIntLE();
if (checksum != Crc32.ComputeChecksum(data,data.ReaderIndex,data.ReadableBytes))
{
Snmp.snmp.InCsumErrors++;
return;
}
}
if (_fecDecode != null)
{
FecPacket fecPacket = FecPacket.newFecPacket(data);
if (fecPacket.Flag == Fec.typeData)
{
data.SkipBytes(2);
input(data, true, current);
}
if (fecPacket.Flag == Fec.typeData || fecPacket.Flag == Fec.typeParity)
{
var byteBufs = _fecDecode.decode(fecPacket);
if (byteBufs != null)
{
foreach (var IByteBuffer in byteBufs)
{
input(IByteBuffer, false, current);
IByteBuffer.Release();
}
}
}
}
else
{
input(data, true, current);
}
}
private void input(IByteBuffer data, bool regular, long current)
{
int ret = _kcp.input(data, regular, current);
switch (ret)
{
case -1:
throw new IOException("No enough bytes of head");
case -2:
throw new IOException("No enough bytes of data");
case -3:
throw new IOException("Mismatch cmd");
case -4:
throw new IOException("Conv inconsistency");
default:
break;
}
}
/**
* Sends a IByteBuffer.
*
* @param buf
* @throws IOException
*/
internal void send(IByteBuffer buf)
{
int ret = _kcp.send(buf);
switch (ret)
{
case -2:
throw new IOException("Too many fragments");
default:
break;
}
}
/**
* The size of the first msg of the kcp.
*
* @return The size of the first msg of the kcp, or -1 if none of msg
*/
internal int peekSize()
{
return _kcp.peekSize();
}
/**
* Returns {@code true} if there are bytes can be received.
*
* @return
*/
protected internal bool canRecv()
{
return _kcp.canRecv();
}
/**
* Returns {@code true} if the kcp can send more bytes.
*
* @param curCanSend last state of canSend
* @return {@code true} if the kcp can send more bytes
*/
protected internal bool canSend(bool curCanSend)
{
int max = _kcp.SndWnd * 2;
int waitSnd = _kcp.waitSnd();
if (curCanSend)
{
return waitSnd < max;
}
else
{
int threshold = Math.Max(1, max / 2);
return waitSnd < threshold;
}
}
/**
* Udpates the kcp.
*
* @param current current time in milliseconds
* @return the next time to update
*/
internal long update(long current)
{
_kcp.update(current);
long nextTsUp = check(current);
setTsUpdate(nextTsUp);
return nextTsUp;
}
protected internal long flush(long current)
{
return _kcp.flush(false, current);
}
/**
* Determines when should you invoke udpate.
*
* @param current current time in milliseconds
* @return
* @see Kcp#check(long)
*/
protected internal long check(long current)
{
return _kcp.check(current);
}
/**
* Returns {@code true} if the kcp need to flush.
*
* @return {@code true} if the kcp need to flush
*/
protected internal bool checkFlush()
{
return _kcp.checkFlush();
}
/**
* Sets params of nodelay.
*
* @param nodelay {@code true} if nodelay mode is enabled
* @param interval protocol internal work interval, in milliseconds
* @param resend fast retransmission mode, 0 represents off by default, 2 can be set (2 ACK spans will result
* in direct retransmission)
* @param nc {@code true} if turn off flow control
*/
protected internal void nodelay(bool nodelay, int interval, int resend, bool nc)
{
_kcp.initNodelay(nodelay, interval, resend, nc);
}
/**
* Returns conv of kcp.
*
* @return conv of kcp
*/
public int getConv()
{
return _kcp.Conv;
}
/**
* Set the conv of kcp.
*
* @param conv the conv of kcp
*/
public void setConv(int conv)
{
_kcp.Conv = conv;
}
/**
* Returns {@code true} if and only if nodelay is enabled.
*
* @return {@code true} if and only if nodelay is enabled
*/
public bool isNodelay()
{
return _kcp.Nodelay;
}
/**
* Sets whether enable nodelay.
*
* @param nodelay {@code true} if enable nodelay
* @return this object
*/
public Ukcp setNodelay(bool nodelay)
{
_kcp.Nodelay = nodelay;
return this;
}
/**
* Returns update interval.
*
* @return update interval
*/
public int getInterval()
{
return _kcp.Interval;
}
/**
* Sets update interval
*
* @param interval update interval
* @return this object
*/
public Ukcp setInterval(int interval)
{
_kcp.setInterval(interval);
return this;
}
/**
* Returns the fastresend of kcp.
*
* @return the fastresend of kcp
*/
public int getFastResend()
{
return _kcp.Fastresend;
}
/**
* Sets the fastresend of kcp.
*
* @param fastResend
* @return this object
*/
public Ukcp setFastResend(int fastResend)
{
_kcp.Fastresend=fastResend;
return this;
}
public bool isNocwnd()
{
return _kcp.Nocwnd;
}
public Ukcp setNocwnd(bool nocwnd)
{
_kcp.Nocwnd = nocwnd;
return this;
}
public int getMinRto()
{
return _kcp.RxMinrto;
}
public Ukcp setMinRto(int minRto)
{
_kcp.RxMinrto = minRto;
return this;
}
public int getMtu()
{
return _kcp.Mtu;
}
public Ukcp setMtu(int mtu)
{
_kcp.setMtu(mtu);
return this;
}
public bool isStream()
{
return _kcp.Stream;
}
public Ukcp setStream(bool stream)
{
_kcp.Stream=stream;
return this;
}
public int getDeadLink()
{
return _kcp.DeadLink;
}
public Ukcp setDeadLink(int deadLink)
{
_kcp.DeadLink = deadLink;
return this;
}
/**
* Sets the {@link ByteBufAllocator} which is used for the kcp to allocate buffers.
*
* @param allocator the allocator is used for the kcp to allocate buffers
* @return this object
*/
public Ukcp setByteBufAllocator(IByteBufferAllocator allocator)
{
_kcp.ByteBufAllocator = allocator;
return this;
}
public int waitSnd()
{
return _kcp.waitSnd();
}
public int getRcvWnd()
{
return _kcp.RcvWnd;
}
protected internal bool isFastFlush()
{
return fastFlush;
}
public Ukcp setFastFlush(bool fastFlush)
{
this.fastFlush = fastFlush;
return this;
}
internal void read(IByteBuffer iByteBuffer)
{
if (_readQueue.TryEnqueue(iByteBuffer))
{
notifyReadEvent();
}
else
{
iByteBuffer.Release();
Console.WriteLine("conv "+_kcp.Conv+" recieveList is full");
}
}
/**
* 使
* 线
* @param IByteBuffer
* @return
*/
public bool write(IByteBuffer byteBuffer)
{
byteBuffer = byteBuffer.RetainedDuplicate();
_writeQueue.Enqueue(byteBuffer);
// if (!_writeQueue.TryEnqueue(byteBuffer))
// {
// Console.WriteLine("conv "+kcp.Conv+" sendList is full");
// byteBuffer.Release();
// return false;
// }
notifyWriteEvent();
return true;
}
/**
*
*/
public void close()
{
this._iMessageExecutor.execute(new CloseTask(this));
}
private void notifyReadEvent()
{
if (_readProcessing.CompareAndSet(false, true))
{
var readTask = ReadTask.New(this);
_iMessageExecutor.execute(readTask);
}
}
protected internal void notifyWriteEvent()
{
if (_writeProcessing.CompareAndSet(false, true))
{
var writeTask = WriteTask.New(this);
_iMessageExecutor.execute(writeTask);
}
}
protected internal long getTsUpdate()
{
return tsUpdate;
}
protected internal Ukcp setTsUpdate(long tsUpdate)
{
this.tsUpdate = tsUpdate;
return this;
}
protected internal KcpListener getKcpListener()
{
return _kcpListener;
}
public bool isActive()
{
return _active;
}
protected internal void internalClose()
{
_kcpListener.handleClose(this);
_active = false;
}
internal void release()
{
_kcp.State = -1;
_kcp.release();
IByteBuffer buffer;
while (_writeQueue.TryDequeue(out buffer))
{
buffer.Release();
}
while (_readQueue.TryDequeue(out buffer))
{
buffer.Release();
}
_fecEncode?.release();
_fecDecode?.release();
}
public User user()
{
return (User) _kcp.User;
}
public Ukcp user(User user)
{
_kcp.User = user;
return this;
}
internal ConcurrentQueue<IByteBuffer> WriteQueue => _writeQueue;
internal MpscArrayQueue<IByteBuffer> ReadQueue => _readQueue;
public long TimeoutMillis => _timeoutMillis;
internal long currentMs()
{
return _kcp.currentMs();
}
internal AtomicBoolean ReadProcessing => _readProcessing;
internal AtomicBoolean WriteProcessing => _writeProcessing;
protected internal KcpListener KcpListener => _kcpListener;
internal IMessageExecutor IMessageExecutor => _iMessageExecutor;
}
}