1841 lines
51 KiB
C#
1841 lines
51 KiB
C#
using System;
|
||
using System.Collections;
|
||
using System.Collections.Generic;
|
||
using System.Linq;
|
||
using DotNetty.Buffers;
|
||
using fec;
|
||
using Object = System.Object;
|
||
|
||
namespace base_kcp
|
||
{
|
||
public class Kcp
|
||
{
|
||
/**
|
||
* no delay min rto
|
||
*/
|
||
public const int IKCP_RTO_NDL = 30;
|
||
|
||
/**
|
||
* normal min rto
|
||
*/
|
||
public const int IKCP_RTO_MIN = 100;
|
||
|
||
public const int IKCP_RTO_DEF = 200;
|
||
|
||
public const int IKCP_RTO_MAX = 60000;
|
||
|
||
/**
|
||
* cmd: push data
|
||
*/
|
||
public const byte IKCP_CMD_PUSH = 81;
|
||
|
||
/**
|
||
* cmd: ack
|
||
*/
|
||
public const byte IKCP_CMD_ACK = 82;
|
||
|
||
/**
|
||
* cmd: window probe (ask)
|
||
* 询问对方当前剩余窗口大小 请求
|
||
*/
|
||
public const byte IKCP_CMD_WASK = 83;
|
||
|
||
/**
|
||
* cmd: window size (tell)
|
||
* 返回本地当前剩余窗口大小
|
||
*/
|
||
public const byte IKCP_CMD_WINS = 84;
|
||
|
||
/**
|
||
* need to send IKCP_CMD_WASK
|
||
*/
|
||
public const int IKCP_ASK_SEND = 1;
|
||
|
||
/**
|
||
* need to send IKCP_CMD_WINS
|
||
*/
|
||
public const int IKCP_ASK_TELL = 2;
|
||
|
||
public const int IKCP_WND_SND = 32;
|
||
|
||
public const int IKCP_WND_RCV = 32;
|
||
|
||
public const int IKCP_MTU_DEF = 1400;
|
||
|
||
public const int IKCP_INTERVAL = 100;
|
||
|
||
public int IKCP_OVERHEAD = 24;
|
||
|
||
public const int IKCP_DEADLINK = 20;
|
||
|
||
public const int IKCP_THRESH_INIT = 2;
|
||
|
||
public const int IKCP_THRESH_MIN = 2;
|
||
|
||
/**
|
||
* 7 secs to probe window size
|
||
*/
|
||
public const int IKCP_PROBE_INIT = 7000;
|
||
|
||
/**
|
||
* up to 120 secs to probe window
|
||
*/
|
||
public const int IKCP_PROBE_LIMIT = 120000;
|
||
|
||
public const int IKCP_SN_OFFSET = 12;
|
||
|
||
|
||
private int ackMaskSize = 0;
|
||
|
||
/**会话id**/
|
||
private int conv;
|
||
|
||
/**最大传输单元**/
|
||
private int mtu = IKCP_MTU_DEF;
|
||
|
||
/**最大分节大小 mtu减去头等部分**/
|
||
private int mss = 0;
|
||
|
||
/**状态**/
|
||
private int state;
|
||
|
||
/**已发送但未确认**/
|
||
private long sndUna;
|
||
|
||
/**下次发送下标**/
|
||
private long sndNxt;
|
||
|
||
/**下次接收下标**/
|
||
private long rcvNxt;
|
||
|
||
/**上次ack时间**/
|
||
private long tsLastack;
|
||
|
||
/**慢启动门限**/
|
||
private int ssthresh = IKCP_THRESH_INIT;
|
||
|
||
/**RTT(Round Trip Time)**/
|
||
private int rxRttval;
|
||
|
||
/**SRTT平滑RTT*/
|
||
private int rxSrtt;
|
||
|
||
/**RTO重传超时*/
|
||
private int rxRto = IKCP_RTO_DEF;
|
||
|
||
/**MinRTO最小重传超时*/
|
||
private int rxMinrto = IKCP_RTO_MIN;
|
||
|
||
/**发送窗口**/
|
||
private int sndWnd = IKCP_WND_SND;
|
||
|
||
/**接收窗口**/
|
||
private int rcvWnd = IKCP_WND_RCV;
|
||
|
||
/**当前对端可接收窗口**/
|
||
private int rmtWnd = IKCP_WND_RCV;
|
||
|
||
/**拥塞控制窗口**/
|
||
private int cwnd;
|
||
|
||
/**探测标志位**/
|
||
private int probe;
|
||
|
||
///**当前时间**/
|
||
//private long current;
|
||
/**间隔**/
|
||
private int interval = IKCP_INTERVAL;
|
||
|
||
/**发送**/
|
||
private long tsFlush = IKCP_INTERVAL;
|
||
|
||
/**是否无延迟 0不启用;1启用**/
|
||
private bool nodelay;
|
||
|
||
/**状态是否已更新**/
|
||
private bool updated;
|
||
|
||
/**探测时间**/
|
||
private long tsProbe;
|
||
|
||
/**探测等待**/
|
||
private int probeWait;
|
||
|
||
/**死连接 重传达到该值时认为连接是断开的**/
|
||
private int deadLink = IKCP_DEADLINK;
|
||
|
||
/**拥塞控制增量**/
|
||
private int incr;
|
||
|
||
/**收到包立即回ack**/
|
||
private bool ackNoDelay;
|
||
|
||
/**待发送窗口窗口**/
|
||
private Queue<Segment> sndQueue = new Queue<Segment>();
|
||
|
||
/**收到后有序的队列**/
|
||
private LinkedList<Segment> rcvQueue = new LinkedList<Segment>();
|
||
|
||
/**发送后待确认的队列**/
|
||
public LinkedList<Segment> sndBuf = new LinkedList<Segment>();
|
||
|
||
/**收到的消息 无序的**/
|
||
private LinkedList<Segment> rcvBuf = new LinkedList<Segment>();
|
||
|
||
private long[] acklist = new long[8];
|
||
|
||
private int ackcount;
|
||
|
||
private Object user;
|
||
|
||
/**是否快速重传 默认0关闭,可以设置2(2次ACK跨越将会直接重传)**/
|
||
private int fastresend;
|
||
|
||
/**是否关闭拥塞控制窗口**/
|
||
private bool nocwnd;
|
||
|
||
/**是否流传输**/
|
||
private bool stream;
|
||
|
||
/**头部预留长度 为fec checksum准备**/
|
||
private int reserved;
|
||
|
||
private KcpOutput output;
|
||
|
||
private IByteBufferAllocator byteBufAllocator = PooledByteBufferAllocator.Default;
|
||
|
||
/**ack二进制标识**/
|
||
private long ackMask;
|
||
private long lastRcvNxt;
|
||
|
||
|
||
private static long long2Uint(long n)
|
||
{
|
||
return n & 0x00000000FFFFFFFFL;
|
||
}
|
||
|
||
private static int ibound(int lower, int middle, int upper)
|
||
{
|
||
return Math.Min(Math.Max(lower, middle), upper);
|
||
}
|
||
|
||
private static int itimediff(long later, long earlier)
|
||
{
|
||
return (int) (later - earlier);
|
||
}
|
||
|
||
private static void outPut(IByteBuffer data, Kcp kcp)
|
||
{
|
||
// if (log.isDebugEnabled()) {
|
||
// log.debug("{} [RO] {} bytes", kcp, data.readableBytes());
|
||
// }
|
||
if (data.ReadableBytes == 0)
|
||
{
|
||
return;
|
||
}
|
||
|
||
kcp.output.outPut(data, kcp);
|
||
}
|
||
|
||
|
||
private static void encodeSeg(IByteBuffer buf, Segment seg)
|
||
{
|
||
int offset = buf.WriterIndex;
|
||
|
||
buf.WriteIntLE(seg.Conv);
|
||
buf.WriteByte(seg.Cmd);
|
||
buf.WriteByte(seg.Frg);
|
||
buf.WriteShortLE(seg.Wnd);
|
||
buf.WriteIntLE((int) seg.Ts);
|
||
buf.WriteIntLE((int) seg.Sn);
|
||
buf.WriteIntLE((int) seg.Una);
|
||
buf.WriteIntLE(seg.Data.ReadableBytes);
|
||
switch (seg.AckMaskSize)
|
||
{
|
||
case 8:
|
||
buf.WriteByte((int) seg.AckMask);
|
||
break;
|
||
case 16:
|
||
buf.WriteShortLE((int) seg.AckMask);
|
||
break;
|
||
case 32:
|
||
buf.WriteIntLE((int) seg.AckMask);
|
||
break;
|
||
case 64:
|
||
buf.WriteLongLE(seg.AckMask);
|
||
break;
|
||
}
|
||
|
||
Snmp.snmp.OutSegs++;
|
||
}
|
||
|
||
public Kcp(int conv, KcpOutput output)
|
||
{
|
||
this.conv = conv;
|
||
this.output = output;
|
||
this.mss = mtu - IKCP_OVERHEAD;
|
||
}
|
||
|
||
public void release()
|
||
{
|
||
release(sndBuf);
|
||
release(rcvBuf);
|
||
release(sndQueue);
|
||
release(rcvQueue);
|
||
}
|
||
|
||
private void release(ICollection segQueue)
|
||
{
|
||
foreach (Segment seg in segQueue)
|
||
{
|
||
seg.recycle(true);
|
||
}
|
||
}
|
||
|
||
private IByteBuffer createFlushByteBuf()
|
||
{
|
||
return byteBufAllocator.DirectBuffer(this.mtu);
|
||
}
|
||
|
||
public IByteBuffer mergeRecv()
|
||
{
|
||
if (rcvQueue.Count == 0)
|
||
{
|
||
return null;
|
||
}
|
||
|
||
int peek = peekSize();
|
||
|
||
if (peek < 0)
|
||
{
|
||
return null;
|
||
}
|
||
|
||
|
||
bool recover = rcvQueue.Count >= rcvWnd;
|
||
|
||
IByteBuffer byteBuf = null;
|
||
|
||
// merge fragment
|
||
int len = 0;
|
||
|
||
var itr = rcvQueue.First;
|
||
while (itr != null)
|
||
{
|
||
Segment seg = itr.Value;
|
||
var next = itr.Next;
|
||
rcvQueue.Remove(itr);
|
||
itr = next;
|
||
|
||
len += seg.Data.ReadableBytes;
|
||
int fragment = seg.Frg;
|
||
if (byteBuf == null)
|
||
{
|
||
if (fragment == 0)
|
||
{
|
||
byteBuf = seg.Data;
|
||
seg.recycle(false);
|
||
break;
|
||
}
|
||
|
||
byteBuf = byteBufAllocator.DirectBuffer(len);
|
||
}
|
||
|
||
byteBuf.WriteBytes(seg.Data);
|
||
seg.recycle(true);
|
||
if (fragment == 0)
|
||
{
|
||
break;
|
||
}
|
||
}
|
||
|
||
// move available data from rcv_buf -> rcv_queue
|
||
moveRcvData();
|
||
// fast recover
|
||
if (rcvQueue.Count < rcvWnd && recover)
|
||
{
|
||
// ready to send back IKCP_CMD_WINS in ikcp_flush
|
||
// tell remote my window size
|
||
probe |= IKCP_ASK_TELL;
|
||
}
|
||
|
||
return byteBuf;
|
||
}
|
||
|
||
|
||
/**
|
||
* 1,判断是否有完整的包,如果有就抛给下一层
|
||
* 2,整理消息接收队列,判断下一个包是否已经收到 收到放入rcvQueue
|
||
* 3,判断接收窗口剩余是否改变,如果改变记录需要通知
|
||
* @param bufList
|
||
* @return
|
||
*/
|
||
public int recv(List<IByteBuffer> bufList)
|
||
{
|
||
if (rcvQueue.Count == 0)
|
||
{
|
||
return -1;
|
||
}
|
||
|
||
int peek = peekSize();
|
||
|
||
if (peek < 0)
|
||
{
|
||
return -2;
|
||
}
|
||
|
||
//接收队列长度大于接收窗口?比如接收窗口是32个包,目前已经满32个包了,需要在恢复的时候告诉对方
|
||
bool recover = rcvQueue.Count >= rcvWnd;
|
||
|
||
// merge fragment
|
||
int len = 0;
|
||
|
||
|
||
var itr = rcvQueue.First;
|
||
|
||
while (itr != null)
|
||
{
|
||
Segment seg = itr.Value;
|
||
var next = itr.Next;
|
||
rcvQueue.Remove(itr);
|
||
itr = next;
|
||
|
||
len += seg.Data.ReadableBytes;
|
||
bufList.Add(seg.Data);
|
||
|
||
int fragment = seg.Frg;
|
||
|
||
seg.recycle(false);
|
||
|
||
if (fragment == 0)
|
||
{
|
||
break;
|
||
}
|
||
}
|
||
|
||
// move available data from rcv_buf -> rcv_queue
|
||
moveRcvData();
|
||
|
||
// fast recover接收队列长度小于接收窗口,说明还可以接数据,已经恢复了,在下次发包的时候告诉对方本方的窗口
|
||
if (rcvQueue.Count < rcvWnd && recover)
|
||
{
|
||
// ready to send back IKCP_CMD_WINS in ikcp_flush
|
||
// tell remote my window size
|
||
probe |= IKCP_ASK_TELL;
|
||
}
|
||
|
||
return len;
|
||
}
|
||
|
||
|
||
/**
|
||
* check the size of next message in the recv queue
|
||
* 检查接收队列里面是否有完整的一个包,如果有返回该包的字节长度
|
||
* @return -1 没有完整包, >0 一个完整包所含字节
|
||
*/
|
||
public int peekSize()
|
||
{
|
||
if (rcvQueue.Count == 0)
|
||
{
|
||
return -1;
|
||
}
|
||
|
||
Segment seg = rcvQueue.First();
|
||
//第一个包是一条应用层消息的最后一个分包?一条消息只有一个包的情况?
|
||
if (seg.Frg == 0)
|
||
{
|
||
return seg.Data.ReadableBytes;
|
||
}
|
||
|
||
//接收队列长度小于应用层消息分包数量?接收队列空间不够用于接收完整的一个消息?
|
||
if (rcvQueue.Count < seg.Frg + 1)
|
||
{
|
||
// Some segments have not arrived yet
|
||
return -1;
|
||
}
|
||
|
||
int len = 0;
|
||
var itr = rcvQueue.First;
|
||
while (itr != null)
|
||
{
|
||
var s = itr.Value;
|
||
len += s.Data.ReadableBytes;
|
||
if (s.Frg == 0)
|
||
{
|
||
break;
|
||
}
|
||
|
||
itr = itr.Next;
|
||
}
|
||
|
||
return len;
|
||
}
|
||
|
||
|
||
/**
|
||
* 判断一条消息是否完整收全了
|
||
* @return
|
||
*/
|
||
public bool canRecv()
|
||
{
|
||
if (rcvQueue.Count == 0)
|
||
{
|
||
return false;
|
||
}
|
||
|
||
Segment seg = rcvQueue.First();
|
||
if (seg.Frg == 0)
|
||
{
|
||
return true;
|
||
}
|
||
|
||
if (rcvQueue.Count < seg.Frg + 1)
|
||
{
|
||
// Some segments have not arrived yet
|
||
return false;
|
||
}
|
||
|
||
return true;
|
||
}
|
||
|
||
|
||
public int send(IByteBuffer buf)
|
||
{
|
||
int len = buf.ReadableBytes;
|
||
if (len == 0)
|
||
{
|
||
return -1;
|
||
}
|
||
|
||
// append to previous segment in streaming mode (if possible)
|
||
if (stream)
|
||
{
|
||
if (sndQueue.Count > 0)
|
||
{
|
||
Segment last = sndQueue.Last();
|
||
IByteBuffer lastData = last.Data;
|
||
int lastLen = lastData.ReadableBytes;
|
||
if (lastLen < mss)
|
||
{
|
||
int capacity = mss - lastLen;
|
||
int extend = len < capacity ? len : capacity;
|
||
if (lastData.MaxWritableBytes < extend)
|
||
{
|
||
// extend
|
||
IByteBuffer newBuf = byteBufAllocator.DirectBuffer(lastLen + extend);
|
||
newBuf.WriteBytes(lastData);
|
||
lastData.Release();
|
||
lastData = last.Data = newBuf;
|
||
}
|
||
|
||
lastData.WriteBytes(buf, extend);
|
||
|
||
len = buf.ReadableBytes;
|
||
if (len == 0)
|
||
{
|
||
return 0;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
int count;
|
||
if (len <= mss)
|
||
{
|
||
count = 1;
|
||
}
|
||
else
|
||
{
|
||
count = (len + mss - 1) / mss;
|
||
}
|
||
|
||
if (count > 255)
|
||
{
|
||
// Maybe don't need the conditon in stream mode
|
||
return -2;
|
||
}
|
||
|
||
if (count == 0)
|
||
{
|
||
// impossible
|
||
count = 1;
|
||
}
|
||
|
||
// segment
|
||
for (int i = 0; i < count; i++)
|
||
{
|
||
int size = len > mss ? mss : len;
|
||
Segment seg = Segment.createSegment(buf.ReadRetainedSlice(size));
|
||
seg.Frg = (short) (stream ? 0 : count - i - 1);
|
||
sndQueue.Enqueue(seg);
|
||
// sndQueue.add(seg);
|
||
len = buf.ReadableBytes;
|
||
}
|
||
|
||
return 0;
|
||
}
|
||
|
||
/**
|
||
* update ack.
|
||
* parse ack根据RTT计算SRTT和RTO即重传超时
|
||
* @param rtt
|
||
*/
|
||
private void updateAck(int rtt)
|
||
{
|
||
if (rxSrtt == 0)
|
||
{
|
||
rxSrtt = rtt;
|
||
rxRttval = rtt >> 2;
|
||
}
|
||
else
|
||
{
|
||
int delta = rtt - rxSrtt;
|
||
rxSrtt += delta >> 3;
|
||
delta = Math.Abs(delta);
|
||
if (rtt < rxSrtt - rxRttval)
|
||
{
|
||
rxRttval += (delta - rxRttval) >> 5;
|
||
}
|
||
else
|
||
{
|
||
rxRttval += (delta - rxRttval) >> 2;
|
||
}
|
||
|
||
//int delta = rtt - rxSrtt;
|
||
//if (delta < 0) {
|
||
// delta = -delta;
|
||
//}
|
||
//rxRttval = (3 * rxRttval + delta) / 4;
|
||
//rxSrtt = (7 * rxSrtt + rtt) / 8;
|
||
//if (rxSrtt < 1) {
|
||
// rxSrtt = 1;
|
||
//}
|
||
}
|
||
|
||
int rto = rxSrtt + Math.Max(interval, rxRttval << 2);
|
||
rxRto = ibound(rxMinrto, rto, IKCP_RTO_MAX);
|
||
}
|
||
|
||
private void shrinkBuf()
|
||
{
|
||
if (sndBuf.Count > 0)
|
||
{
|
||
Segment seg = sndBuf.First();
|
||
sndUna = seg.Sn;
|
||
}
|
||
else
|
||
{
|
||
sndUna = sndNxt;
|
||
}
|
||
}
|
||
|
||
private void parseAck(long sn)
|
||
{
|
||
if (itimediff(sn, sndUna) < 0 || itimediff(sn, sndNxt) >= 0)
|
||
{
|
||
return;
|
||
}
|
||
|
||
var itr = sndBuf.First;
|
||
while (itr != null)
|
||
{
|
||
var next = itr.Next;
|
||
Segment seg = itr.Value;
|
||
if (sn == seg.Sn)
|
||
{
|
||
sndBuf.Remove(itr);
|
||
seg.recycle(true);
|
||
break;
|
||
}
|
||
|
||
if (itimediff(sn, seg.Sn) < 0)
|
||
{
|
||
break;
|
||
}
|
||
|
||
itr = next;
|
||
}
|
||
}
|
||
|
||
private void parseUna(long una)
|
||
{
|
||
var itr = sndBuf.First;
|
||
while (itr != null)
|
||
{
|
||
var next = itr.Next;
|
||
Segment seg = itr.Value;
|
||
if (itimediff(una, seg.Sn) > 0)
|
||
{
|
||
sndBuf.Remove(itr);
|
||
seg.recycle(true);
|
||
}
|
||
else
|
||
{
|
||
break;
|
||
}
|
||
|
||
itr = next;
|
||
}
|
||
}
|
||
|
||
private void parseAckMask(long una, long ackMask)
|
||
{
|
||
if (ackMask == 0)
|
||
{
|
||
return;
|
||
}
|
||
|
||
var itr = sndBuf.First;
|
||
while (itr != null)
|
||
{
|
||
var next = itr.Next;
|
||
Segment seg = itr.Value;
|
||
int index = (int) (seg.Sn - una - 1);
|
||
if (index < 0)
|
||
{
|
||
continue;
|
||
}
|
||
|
||
if (index >= ackMaskSize)
|
||
break;
|
||
long mask = ackMask & 1 << index;
|
||
if (mask != 0)
|
||
{
|
||
sndBuf.Remove(itr);
|
||
seg.recycle(true);
|
||
}
|
||
|
||
itr = next;
|
||
}
|
||
}
|
||
|
||
|
||
private void parseFastack(long sn, long ts)
|
||
{
|
||
if (itimediff(sn, sndUna) < 0 || itimediff(sn, sndNxt) >= 0)
|
||
{
|
||
return;
|
||
}
|
||
|
||
foreach (var seg in sndBuf)
|
||
{
|
||
if (itimediff(sn, seg.Sn) < 0)
|
||
{
|
||
break;
|
||
//根据时间判断 在当前包时间之前的包才能被认定是需要快速重传的
|
||
}
|
||
else if (sn != seg.Sn && itimediff(seg.Ts, ts) <= 0)
|
||
{
|
||
seg.Fastack++;
|
||
}
|
||
}
|
||
}
|
||
|
||
|
||
private void ackPush(long sn, long ts)
|
||
{
|
||
int newSize = 2 * (ackcount + 1);
|
||
|
||
if (newSize > acklist.Count())
|
||
{
|
||
int newCapacity = acklist.Count() << 1; // double capacity
|
||
|
||
// if (newCapacity < 0) {
|
||
// throw new OutOfMemoryError();
|
||
// }
|
||
|
||
long[] newArray = new long[newCapacity];
|
||
Array.Copy(acklist, 0, newArray, 0, acklist.Count());
|
||
this.acklist = newArray;
|
||
}
|
||
|
||
acklist[2 * ackcount] = sn;
|
||
acklist[2 * ackcount + 1] = ts;
|
||
ackcount++;
|
||
}
|
||
|
||
private bool parseData(Segment newSeg)
|
||
{
|
||
long sn = newSeg.Sn;
|
||
|
||
if (itimediff(sn, rcvNxt + rcvWnd) >= 0 || itimediff(sn, rcvNxt) < 0)
|
||
{
|
||
newSeg.recycle(true);
|
||
return true;
|
||
}
|
||
|
||
bool repeat = false;
|
||
bool findPos = false;
|
||
|
||
var last = rcvBuf.Last;
|
||
while (last != null)
|
||
{
|
||
var front = last.Previous;
|
||
Segment seg = last.Value;
|
||
if (seg.Sn == sn)
|
||
{
|
||
repeat = true;
|
||
//Snmp.snmp.RepeatSegs.incrementAndGet();
|
||
break;
|
||
}
|
||
|
||
if (itimediff(sn, seg.Sn) > 0)
|
||
{
|
||
findPos = true;
|
||
break;
|
||
}
|
||
|
||
if (front == null)
|
||
{
|
||
break;
|
||
}
|
||
|
||
last = front;
|
||
}
|
||
|
||
if (repeat)
|
||
{
|
||
newSeg.recycle(true);
|
||
}
|
||
else if (last == null)
|
||
{
|
||
rcvBuf.AddLast(newSeg);
|
||
}
|
||
else if(findPos)
|
||
{
|
||
rcvBuf.AddAfter(last, newSeg);
|
||
}
|
||
else
|
||
{
|
||
rcvBuf.AddFirst(newSeg);
|
||
}
|
||
|
||
// var firstSn = rcvBuf.First.Value.Sn;
|
||
// foreach (var segment in rcvBuf)
|
||
// {
|
||
// if (segment.Sn == firstSn)
|
||
// continue;
|
||
// firstSn++;
|
||
// if (firstSn != segment.Sn)
|
||
// {
|
||
// Console.WriteLine();
|
||
// }
|
||
//
|
||
//
|
||
// }
|
||
|
||
|
||
|
||
// move available data from rcv_buf -> rcv_queue
|
||
moveRcvData(); // Invoke the method only if the segment is not repeat?
|
||
return repeat;
|
||
}
|
||
|
||
private void moveRcvData()
|
||
{
|
||
var itr = rcvBuf.First;
|
||
while (itr != null)
|
||
{
|
||
var next = itr.Next;
|
||
Segment seg = itr.Value;
|
||
if (seg.Sn == rcvNxt && rcvQueue.Count < rcvWnd)
|
||
{
|
||
rcvBuf.Remove(itr);
|
||
rcvQueue.AddLast(seg);
|
||
rcvNxt++;
|
||
}
|
||
else
|
||
{
|
||
break;
|
||
}
|
||
|
||
itr = next;
|
||
}
|
||
}
|
||
|
||
|
||
public int input(IByteBuffer data, bool regular, long current)
|
||
{
|
||
long oldSndUna = sndUna;
|
||
if (data == null || data.ReadableBytes < IKCP_OVERHEAD)
|
||
{
|
||
return -1;
|
||
}
|
||
// if (log.isDebugEnabled()) {
|
||
// log.debug("{} [RI] {} bytes", this, data.readableBytes());
|
||
// }
|
||
|
||
|
||
long latest = 0; // latest packet
|
||
bool flag = false;
|
||
int inSegs = 0;
|
||
|
||
|
||
long uintCurrent = long2Uint(current);
|
||
|
||
while (true)
|
||
{
|
||
int conv, len, wnd;
|
||
long ts, sn, una, ackMask;
|
||
byte cmd;
|
||
short frg;
|
||
Segment seg;
|
||
|
||
if (data.ReadableBytes < IKCP_OVERHEAD)
|
||
{
|
||
break;
|
||
}
|
||
|
||
conv = data.ReadIntLE();
|
||
if (conv != this.conv)
|
||
{
|
||
return -4;
|
||
}
|
||
|
||
cmd = data.ReadByte();
|
||
frg = data.ReadByte();
|
||
wnd = data.ReadUnsignedShortLE();
|
||
ts = data.ReadUnsignedIntLE();
|
||
sn = data.ReadUnsignedIntLE();
|
||
una = data.ReadUnsignedIntLE();
|
||
len = data.ReadIntLE();
|
||
|
||
|
||
switch (ackMaskSize)
|
||
{
|
||
case 8:
|
||
ackMask = data.ReadByte();
|
||
break;
|
||
case 16:
|
||
ackMask = data.ReadUnsignedShortLE();
|
||
break;
|
||
case 32:
|
||
ackMask = data.ReadUnsignedIntLE();
|
||
break;
|
||
case 64:
|
||
//TODO need unsignedLongLe
|
||
ackMask = data.ReadLongLE();
|
||
break;
|
||
default:
|
||
ackMask = 0;
|
||
break;
|
||
}
|
||
|
||
;
|
||
|
||
|
||
if (data.ReadableBytes < len)
|
||
{
|
||
return -2;
|
||
}
|
||
|
||
if (cmd != IKCP_CMD_PUSH && cmd != IKCP_CMD_ACK && cmd != IKCP_CMD_WASK && cmd != IKCP_CMD_WINS)
|
||
{
|
||
return -3;
|
||
}
|
||
|
||
//最后收到的来计算远程窗口大小
|
||
if (regular)
|
||
{
|
||
this.rmtWnd = wnd; //更新远端窗口大小删除已确认的包,una以前的包对方都收到了,可以把本地小于una的都删除掉
|
||
}
|
||
|
||
//this.rmtWnd = wnd;
|
||
parseUna(una);
|
||
shrinkBuf();
|
||
|
||
|
||
bool readed = false;
|
||
switch (cmd)
|
||
{
|
||
case IKCP_CMD_ACK:
|
||
{
|
||
parseAck(sn);
|
||
parseFastack(sn, ts);
|
||
flag = true;
|
||
latest = ts;
|
||
int rtt = itimediff(uintCurrent, ts);
|
||
// Debug.Log(GetHashCode()+" input ack: sn="+sn+", rtt="+rtt+", rto="+rxRto+",regular="+regular);
|
||
// if (log.isDebugEnabled()) {
|
||
// log.debug("{} input ack: sn={}, rtt={}, rto={} ,regular={}", this, sn, rtt, rxRto,regular);
|
||
// }
|
||
break;
|
||
}
|
||
case IKCP_CMD_PUSH:
|
||
{
|
||
bool repeat = true;
|
||
if (itimediff(sn, rcvNxt + rcvWnd) < 0)
|
||
{
|
||
ackPush(sn, ts);
|
||
if (itimediff(sn, rcvNxt) >= 0)
|
||
{
|
||
if (len > 0)
|
||
{
|
||
seg = Segment.createSegment(data.ReadRetainedSlice(len));
|
||
readed = true;
|
||
}
|
||
else
|
||
{
|
||
seg = Segment.createSegment(byteBufAllocator, 0);
|
||
}
|
||
|
||
seg.Conv = conv;
|
||
seg.Cmd = cmd;
|
||
seg.Frg = frg;
|
||
seg.Wnd = wnd;
|
||
seg.Ts = ts;
|
||
seg.Sn = sn;
|
||
seg.Una = una;
|
||
repeat = parseData(seg);
|
||
}
|
||
}
|
||
|
||
if (regular && repeat)
|
||
{
|
||
Snmp.snmp.RepeatSegs++;
|
||
}
|
||
|
||
// if (log.isDebugEnabled()) {
|
||
// log.debug("{} input push: sn={}, una={}, ts={},regular={}", this, sn, una, ts,regular);
|
||
// }
|
||
|
||
// Console.WriteLine(GetHashCode()+" input push: sn="+sn+", una="+una+", ts="+ts+",regular="+regular);
|
||
break;
|
||
}
|
||
case IKCP_CMD_WASK:
|
||
{
|
||
// ready to send back IKCP_CMD_WINS in ikcp_flush
|
||
// tell remote my window size
|
||
probe |= IKCP_ASK_TELL;
|
||
// if (log.isDebugEnabled()) {
|
||
// log.debug("{} input ask", this);
|
||
// }
|
||
break;
|
||
}
|
||
case IKCP_CMD_WINS:
|
||
{
|
||
// do nothing
|
||
// if (log.isDebugEnabled()) {
|
||
// log.debug("{} input tell: {}", this, wnd);
|
||
// }
|
||
break;
|
||
}
|
||
default:
|
||
return -3;
|
||
}
|
||
|
||
parseAckMask(una, ackMask);
|
||
|
||
if (!readed)
|
||
{
|
||
data.SkipBytes(len);
|
||
}
|
||
|
||
inSegs++;
|
||
}
|
||
|
||
// if (data.ReadableBytes > 0)
|
||
// {
|
||
// Console.WriteLine("ReadableBytes"+data.ReadableBytes);
|
||
// }
|
||
|
||
Snmp.snmp.InSegs += inSegs;
|
||
|
||
if (flag && regular)
|
||
{
|
||
int rtt = itimediff(uintCurrent, latest);
|
||
if (rtt >= 0)
|
||
{
|
||
updateAck(rtt); //收到ack包,根据ack包的时间计算srtt和rto
|
||
}
|
||
}
|
||
|
||
if (!nocwnd)
|
||
{
|
||
if (itimediff(sndUna, oldSndUna) > 0)
|
||
{
|
||
if (cwnd < rmtWnd)
|
||
{
|
||
int mss = this.mss;
|
||
if (cwnd < ssthresh)
|
||
{
|
||
cwnd++;
|
||
incr += mss;
|
||
}
|
||
else
|
||
{
|
||
if (incr < mss)
|
||
{
|
||
incr = mss;
|
||
}
|
||
|
||
incr += (mss * mss) / incr + (mss / 16);
|
||
if ((cwnd + 1) * mss <= incr)
|
||
{
|
||
cwnd++;
|
||
}
|
||
}
|
||
|
||
if (cwnd > rmtWnd)
|
||
{
|
||
cwnd = rmtWnd;
|
||
incr = rmtWnd * mss;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
|
||
if (ackNoDelay && ackcount > 0)
|
||
{
|
||
// ack immediately
|
||
flush(true, current);
|
||
}
|
||
|
||
return 0;
|
||
}
|
||
|
||
|
||
private int wndUnused()
|
||
{
|
||
if (rcvQueue.Count < rcvWnd)
|
||
{
|
||
return rcvWnd - rcvQueue.Count;
|
||
}
|
||
|
||
return 0;
|
||
}
|
||
|
||
|
||
private IByteBuffer makeSpace(IByteBuffer buffer, int space)
|
||
{
|
||
if (buffer.ReadableBytes + space > mtu)
|
||
{
|
||
outPut(buffer, this);
|
||
buffer = createFlushByteBuf();
|
||
buffer.SetWriterIndex(reserved);
|
||
}
|
||
|
||
return buffer;
|
||
}
|
||
|
||
private void flushBuffer(IByteBuffer buffer)
|
||
{
|
||
if (buffer.ReadableBytes > reserved)
|
||
{
|
||
outPut(buffer, this);
|
||
return;
|
||
}
|
||
|
||
buffer.Release();
|
||
}
|
||
|
||
|
||
private readonly long startTicks = DateTime.Now.Ticks;
|
||
|
||
public long currentMs()
|
||
{
|
||
long currentTicks = DateTime.Now.Ticks;
|
||
return (currentTicks - startTicks) / TimeSpan.TicksPerMillisecond;
|
||
}
|
||
|
||
/**
|
||
* ikcp_flush
|
||
*/
|
||
public long flush(bool ackOnly, long current)
|
||
{
|
||
// 'ikcp_update' haven't been called.
|
||
//if (!updated) {
|
||
// return;
|
||
//}
|
||
|
||
//long current = this.current;
|
||
//long uintCurrent = long2Uint(current);
|
||
|
||
Segment seg = Segment.createSegment(byteBufAllocator, 0);
|
||
seg.Conv = conv;
|
||
seg.Cmd = IKCP_CMD_ACK;
|
||
seg.AckMaskSize = this.ackMaskSize;
|
||
seg.Wnd = wndUnused(); //可接收数量
|
||
seg.Una = rcvNxt; //已接收数量,下次要接收的包的sn,这sn之前的包都已经收到
|
||
|
||
IByteBuffer buffer = createFlushByteBuf();
|
||
buffer.SetWriterIndex(reserved);
|
||
|
||
|
||
//计算ackMask
|
||
int count = ackcount;
|
||
|
||
if (lastRcvNxt != rcvNxt)
|
||
{
|
||
ackMask = 0;
|
||
lastRcvNxt = rcvNxt;
|
||
}
|
||
|
||
for (int i = 0; i < count; i++)
|
||
{
|
||
long sn = acklist[i * 2];
|
||
if (sn < rcvNxt)
|
||
continue;
|
||
int index = (int) (sn - rcvNxt - 1);
|
||
if (index >= ackMaskSize)
|
||
break;
|
||
if (index >= 0)
|
||
{
|
||
ackMask |= 1 << index;
|
||
}
|
||
}
|
||
|
||
seg.AckMask = ackMask;
|
||
|
||
|
||
// flush acknowledges有收到的包需要确认,则发确认包
|
||
for (int i = 0; i < count; i++)
|
||
{
|
||
buffer = makeSpace(buffer, IKCP_OVERHEAD);
|
||
long sn = acklist[i * 2];
|
||
if (sn >= rcvNxt || count - 1 == i)
|
||
{
|
||
seg.Sn = sn;
|
||
seg.Ts = acklist[i * 2 + 1];
|
||
encodeSeg(buffer, seg);
|
||
|
||
// Console.WriteLine(GetHashCode()+"flush ack: sn="+seg.Sn+", ts="+seg.Ts+" ,count="+count+" Una"+seg.Una);
|
||
|
||
// if (log.isDebugEnabled()) {
|
||
// log.debug("{} flush ack: sn={}, ts={} ,count={}", this, seg.sn, seg.ts,count);
|
||
// }
|
||
}
|
||
}
|
||
|
||
ackcount = 0;
|
||
|
||
|
||
if (ackOnly)
|
||
{
|
||
flushBuffer(buffer);
|
||
seg.recycle(true);
|
||
return interval;
|
||
}
|
||
|
||
// probe window size (if remote window size equals zero)
|
||
//拥堵控制 如果对方可接受窗口大小为0 需要询问对方窗口大小
|
||
if (rmtWnd == 0)
|
||
{
|
||
current = currentMs();
|
||
if (probeWait == 0)
|
||
{
|
||
probeWait = IKCP_PROBE_INIT;
|
||
tsProbe = current + probeWait;
|
||
}
|
||
else
|
||
{
|
||
if (itimediff(current, tsProbe) >= 0)
|
||
{
|
||
if (probeWait < IKCP_PROBE_INIT)
|
||
{
|
||
probeWait = IKCP_PROBE_INIT;
|
||
}
|
||
|
||
probeWait += probeWait / 2;
|
||
if (probeWait > IKCP_PROBE_LIMIT)
|
||
{
|
||
probeWait = IKCP_PROBE_LIMIT;
|
||
}
|
||
|
||
tsProbe = current + probeWait;
|
||
probe |= IKCP_ASK_SEND;
|
||
}
|
||
}
|
||
}
|
||
else
|
||
{
|
||
tsProbe = 0;
|
||
probeWait = 0;
|
||
}
|
||
|
||
// flush window probing commands
|
||
if ((probe & IKCP_ASK_SEND) != 0)
|
||
{
|
||
seg.Cmd = IKCP_CMD_WASK;
|
||
buffer = makeSpace(buffer, IKCP_OVERHEAD);
|
||
encodeSeg(buffer, seg);
|
||
// if (log.isDebugEnabled()) {
|
||
// log.debug("{} flush ask", this);
|
||
// }
|
||
}
|
||
|
||
// flush window probing commands
|
||
if ((probe & IKCP_ASK_TELL) != 0)
|
||
{
|
||
seg.Cmd = IKCP_CMD_WINS;
|
||
buffer = makeSpace(buffer, IKCP_OVERHEAD);
|
||
encodeSeg(buffer, seg);
|
||
// if (log.isDebugEnabled()) {
|
||
// log.debug("{} flush tell: wnd={}", this, seg.wnd);
|
||
// }
|
||
}
|
||
|
||
probe = 0;
|
||
|
||
// calculate window size
|
||
int cwnd0 = Math.Min(sndWnd, rmtWnd);
|
||
if (!nocwnd)
|
||
{
|
||
cwnd0 = Math.Min(this.cwnd, cwnd0);
|
||
}
|
||
|
||
int newSegsCount = 0;
|
||
// move data from snd_queue to snd_buf
|
||
while (itimediff(sndNxt, sndUna + cwnd0) < 0)
|
||
{
|
||
if (sndQueue.Count == 0)
|
||
{
|
||
break;
|
||
}
|
||
var newSeg = sndQueue.Dequeue();
|
||
newSeg.Conv = conv;
|
||
newSeg.Cmd = IKCP_CMD_PUSH;
|
||
newSeg.Sn = sndNxt;
|
||
sndBuf.AddLast(newSeg);
|
||
// sndBuf.AddLast(newSeg);
|
||
sndNxt++;
|
||
newSegsCount++;
|
||
}
|
||
|
||
// calculate resent
|
||
int resent = fastresend > 0 ? fastresend : 0x7fffffff;
|
||
|
||
// flush data segments
|
||
current = currentMs();
|
||
int change = 0;
|
||
bool lost = false;
|
||
int lostSegs = 0, fastRetransSegs = 0, earlyRetransSegs = 0;
|
||
long minrto = interval;
|
||
var itr = sndBuf.First;
|
||
|
||
while (itr != null)
|
||
{
|
||
var next = itr.Next;
|
||
Segment segment = itr.Value;
|
||
itr = next;
|
||
|
||
bool needsend = false;
|
||
if (segment.Xmit == 0)
|
||
{
|
||
needsend = true;
|
||
segment.Rto = rxRto;
|
||
segment.Resendts = current + segment.Rto;
|
||
// if (log.isDebugEnabled()) {
|
||
// log.debug("{} flush data: sn={}, resendts={}", this, segment.sn, (segment.resendts - current));
|
||
// }
|
||
}
|
||
else if (segment.Fastack >= resent)
|
||
{
|
||
needsend = true;
|
||
segment.Fastack = 0;
|
||
segment.Rto = rxRto;
|
||
segment.Resendts = current + segment.Rto;
|
||
change++;
|
||
fastRetransSegs++;
|
||
// if (log.isDebugEnabled()) {
|
||
// log.debug("{} fastresend. sn={}, xmit={}, resendts={} ", this, segment.sn, segment.xmit, (segment
|
||
// .resendts - current));
|
||
// }
|
||
}
|
||
else if (segment.Fastack > 0 && newSegsCount == 0)
|
||
{
|
||
// early retransmit
|
||
needsend = true;
|
||
segment.Fastack = 0;
|
||
segment.Rto = rxRto;
|
||
segment.Resendts = current + segment.Rto;
|
||
change++;
|
||
earlyRetransSegs++;
|
||
}
|
||
else if (itimediff(current, segment.Resendts) >= 0)
|
||
{
|
||
needsend = true;
|
||
if (!nodelay)
|
||
{
|
||
segment.Rto += rxRto;
|
||
}
|
||
else
|
||
{
|
||
segment.Rto += rxRto / 2;
|
||
}
|
||
|
||
segment.Fastack = 0;
|
||
segment.Resendts = current + segment.Rto;
|
||
lost = true;
|
||
lostSegs++;
|
||
// if (log.isDebugEnabled()) {
|
||
// log.debug("{} resend. sn={}, xmit={}, resendts={}", this, segment.sn, segment.xmit, (segment
|
||
// .resendts - current));
|
||
// }
|
||
}
|
||
|
||
|
||
if (needsend)
|
||
{
|
||
segment.Xmit++;
|
||
segment.Ts = long2Uint(current);
|
||
segment.Wnd = seg.Wnd;
|
||
segment.Una = rcvNxt;
|
||
segment.AckMaskSize = this.ackMaskSize;
|
||
segment.AckMask = ackMask;
|
||
|
||
IByteBuffer segData = segment.Data;
|
||
int segLen = segData.ReadableBytes;
|
||
int need = IKCP_OVERHEAD + segLen;
|
||
buffer = makeSpace(buffer, need);
|
||
|
||
//if (buffer.readableBytes() + need > mtu) {
|
||
// output(buffer, this);
|
||
// buffer = createFlushByteBuf();
|
||
//}
|
||
|
||
encodeSeg(buffer, segment);
|
||
|
||
if (segLen > 0)
|
||
{
|
||
// don't increases data's readerIndex, because the data may be resend.
|
||
buffer.WriteBytes(segData, segData.ReaderIndex, segLen);
|
||
}
|
||
|
||
if (segment.Xmit >= deadLink)
|
||
{
|
||
state = -1;
|
||
}
|
||
|
||
// get the nearest rto
|
||
long rto = itimediff(segment.Resendts, current);
|
||
if (rto > 0 && rto < minrto)
|
||
{
|
||
minrto = rto;
|
||
}
|
||
}
|
||
}
|
||
|
||
// flash remain segments
|
||
flushBuffer(buffer);
|
||
seg.recycle(true);
|
||
|
||
int sum = lostSegs;
|
||
if (lostSegs > 0)
|
||
{
|
||
Snmp.snmp.LostSegs += lostSegs;
|
||
}
|
||
|
||
if (fastRetransSegs > 0)
|
||
{
|
||
Snmp.snmp.FastRetransSegs += fastRetransSegs;
|
||
sum += fastRetransSegs;
|
||
}
|
||
|
||
if (earlyRetransSegs > 0)
|
||
{
|
||
Snmp.snmp.EarlyRetransSegs += earlyRetransSegs;
|
||
sum += earlyRetransSegs;
|
||
}
|
||
|
||
if (sum > 0)
|
||
{
|
||
Snmp.snmp.RetransSegs += sum;
|
||
}
|
||
|
||
// update ssthresh
|
||
if (!nocwnd)
|
||
{
|
||
if (change > 0)
|
||
{
|
||
int inflight = (int) (sndNxt - sndUna);
|
||
ssthresh = inflight / 2;
|
||
if (ssthresh < IKCP_THRESH_MIN)
|
||
{
|
||
ssthresh = IKCP_THRESH_MIN;
|
||
}
|
||
|
||
cwnd = ssthresh + resent;
|
||
incr = cwnd * mss;
|
||
}
|
||
|
||
if (lost)
|
||
{
|
||
ssthresh = cwnd0 / 2;
|
||
if (ssthresh < IKCP_THRESH_MIN)
|
||
{
|
||
ssthresh = IKCP_THRESH_MIN;
|
||
}
|
||
|
||
cwnd = 1;
|
||
incr = mss;
|
||
}
|
||
|
||
if (cwnd < 1)
|
||
{
|
||
cwnd = 1;
|
||
incr = mss;
|
||
}
|
||
}
|
||
|
||
return minrto;
|
||
}
|
||
|
||
|
||
/**
|
||
* update getState (call it repeatedly, every 10ms-100ms), or you can ask
|
||
* ikcp_check when to call it again (without ikcp_input/_send calling).
|
||
* 'current' - current timestamp in millisec.
|
||
*
|
||
* @param current
|
||
*/
|
||
public void update(long current)
|
||
{
|
||
if (!updated)
|
||
{
|
||
updated = true;
|
||
tsFlush = current;
|
||
}
|
||
|
||
int slap = itimediff(current, tsFlush);
|
||
|
||
if (slap >= 10000 || slap < -10000)
|
||
{
|
||
tsFlush = current;
|
||
slap = 0;
|
||
}
|
||
|
||
/*if (slap >= 0) {
|
||
tsFlush += setInterval;
|
||
if (itimediff(this.current, tsFlush) >= 0) {
|
||
tsFlush = this.current + setInterval;
|
||
}
|
||
flush();
|
||
}*/
|
||
|
||
if (slap >= 0)
|
||
{
|
||
tsFlush += interval;
|
||
if (itimediff(current, tsFlush) >= 0)
|
||
{
|
||
tsFlush = current + interval;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
tsFlush = current + interval;
|
||
}
|
||
|
||
flush(false, current);
|
||
}
|
||
|
||
/**
|
||
* Determine when should you invoke ikcp_update:
|
||
* returns when you should invoke ikcp_update in millisec, if there
|
||
* is no ikcp_input/_send calling. you can call ikcp_update in that
|
||
* time, instead of call update repeatly.
|
||
* Important to reduce unnacessary ikcp_update invoking. use it to
|
||
* schedule ikcp_update (eg. implementing an epoll-like mechanism,
|
||
* or optimize ikcp_update when handling massive kcp connections)
|
||
*
|
||
* @param current
|
||
* @return
|
||
*/
|
||
public long check(long current)
|
||
{
|
||
if (!updated)
|
||
{
|
||
return current;
|
||
}
|
||
|
||
long tsFlush = this.tsFlush;
|
||
int slap = itimediff(current, tsFlush);
|
||
if (slap >= 10000 || slap < -10000)
|
||
{
|
||
tsFlush = current;
|
||
slap = 0;
|
||
}
|
||
|
||
if (slap >= 0)
|
||
{
|
||
return current;
|
||
}
|
||
|
||
int tmFlush = itimediff(tsFlush, current);
|
||
int tmPacket = 0x7fffffff;
|
||
|
||
foreach (var seg in sndBuf)
|
||
{
|
||
int diff = itimediff(seg.Resendts, current);
|
||
if (diff <= 0)
|
||
{
|
||
return current;
|
||
}
|
||
|
||
if (diff < tmPacket)
|
||
{
|
||
tmPacket = diff;
|
||
}
|
||
}
|
||
|
||
|
||
int minimal = tmPacket < tmFlush ? tmPacket : tmFlush;
|
||
if (minimal >= interval)
|
||
{
|
||
minimal = interval;
|
||
}
|
||
|
||
return current + minimal;
|
||
}
|
||
|
||
|
||
public bool checkFlush()
|
||
{
|
||
if (ackcount > 0)
|
||
{
|
||
return true;
|
||
}
|
||
|
||
if (probe != 0)
|
||
{
|
||
return true;
|
||
}
|
||
|
||
if (sndBuf.Count > 0)
|
||
{
|
||
return true;
|
||
}
|
||
|
||
if (sndQueue.Count > 0)
|
||
{
|
||
return true;
|
||
}
|
||
|
||
return false;
|
||
}
|
||
|
||
public int setMtu(int mtu)
|
||
{
|
||
if (mtu < IKCP_OVERHEAD || mtu < 50)
|
||
{
|
||
return -1;
|
||
}
|
||
|
||
if (reserved >= mtu - IKCP_OVERHEAD || reserved < 0)
|
||
{
|
||
return -1;
|
||
}
|
||
|
||
this.mtu = mtu;
|
||
this.mss = mtu - IKCP_OVERHEAD - reserved;
|
||
return 0;
|
||
}
|
||
|
||
|
||
public int setInterval(int interval)
|
||
{
|
||
if (interval > 5000)
|
||
{
|
||
interval = 5000;
|
||
}
|
||
else if (interval < 10)
|
||
{
|
||
interval = 10;
|
||
}
|
||
|
||
this.interval = interval;
|
||
|
||
return 0;
|
||
}
|
||
|
||
public int initNodelay(bool nodelay, int interval, int resend, bool nc)
|
||
{
|
||
this.nodelay = nodelay;
|
||
if (nodelay)
|
||
{
|
||
this.rxMinrto = IKCP_RTO_NDL;
|
||
}
|
||
else
|
||
{
|
||
this.rxMinrto = IKCP_RTO_MIN;
|
||
}
|
||
|
||
if (interval >= 0)
|
||
{
|
||
if (interval > 5000)
|
||
{
|
||
interval = 5000;
|
||
}
|
||
else if (interval < 10)
|
||
{
|
||
interval = 10;
|
||
}
|
||
|
||
this.interval = interval;
|
||
}
|
||
|
||
if (resend >= 0)
|
||
{
|
||
fastresend = resend;
|
||
}
|
||
|
||
this.nocwnd = nc;
|
||
|
||
return 0;
|
||
}
|
||
|
||
public int waitSnd()
|
||
{
|
||
return this.sndBuf.Count + this.sndQueue.Count;
|
||
}
|
||
|
||
public void SetNodelay(bool nodelay)
|
||
{
|
||
this.nodelay = nodelay;
|
||
if (nodelay)
|
||
{
|
||
this.rxMinrto = IKCP_RTO_NDL;
|
||
}
|
||
else
|
||
{
|
||
this.rxMinrto = IKCP_RTO_MIN;
|
||
}
|
||
}
|
||
|
||
|
||
public void setAckMaskSize(int ackMaskSize)
|
||
{
|
||
this.ackMaskSize = ackMaskSize;
|
||
this.IKCP_OVERHEAD += (ackMaskSize / 8);
|
||
this.mss = mtu - IKCP_OVERHEAD - reserved;
|
||
}
|
||
|
||
public void setReserved(int reserved)
|
||
{
|
||
this.reserved = reserved;
|
||
this.mss = mtu - IKCP_OVERHEAD - reserved;
|
||
}
|
||
|
||
|
||
public int Conv
|
||
{
|
||
get => conv;
|
||
set => conv = value;
|
||
}
|
||
|
||
public int Mtu
|
||
{
|
||
get => mtu;
|
||
set => mtu = value;
|
||
}
|
||
|
||
public int Mss
|
||
{
|
||
get => mss;
|
||
set => mss = value;
|
||
}
|
||
|
||
public long SndUna
|
||
{
|
||
get => sndUna;
|
||
set => sndUna = value;
|
||
}
|
||
|
||
public long SndNxt
|
||
{
|
||
get => sndNxt;
|
||
set => sndNxt = value;
|
||
}
|
||
|
||
public long RcvNxt
|
||
{
|
||
get => rcvNxt;
|
||
set => rcvNxt = value;
|
||
}
|
||
|
||
public bool AckNoDelay
|
||
{
|
||
get => ackNoDelay;
|
||
set => ackNoDelay = value;
|
||
}
|
||
|
||
public object User
|
||
{
|
||
get => user;
|
||
set => user = value;
|
||
}
|
||
|
||
public int Fastresend
|
||
{
|
||
get => fastresend;
|
||
set => fastresend = value;
|
||
}
|
||
|
||
public bool Nocwnd
|
||
{
|
||
get => nocwnd;
|
||
set => nocwnd = value;
|
||
}
|
||
|
||
public bool Stream
|
||
{
|
||
get => stream;
|
||
set => stream = value;
|
||
}
|
||
|
||
public int RcvWnd
|
||
{
|
||
get => rcvWnd;
|
||
set => rcvWnd = value;
|
||
}
|
||
|
||
public int SndWnd
|
||
{
|
||
get => sndWnd;
|
||
set => sndWnd = value;
|
||
}
|
||
|
||
public int RxMinrto
|
||
{
|
||
get => rxMinrto;
|
||
set => rxMinrto = value;
|
||
}
|
||
|
||
public KcpOutput Output
|
||
{
|
||
get => output;
|
||
set => output = value;
|
||
}
|
||
|
||
public int Interval
|
||
{
|
||
get => interval;
|
||
set => interval = value;
|
||
}
|
||
|
||
public bool Nodelay
|
||
{
|
||
get => nodelay;
|
||
set => nodelay = value;
|
||
}
|
||
|
||
public int DeadLink
|
||
{
|
||
get => deadLink;
|
||
set => deadLink = value;
|
||
}
|
||
|
||
public IByteBufferAllocator ByteBufAllocator
|
||
{
|
||
get => byteBufAllocator;
|
||
set => byteBufAllocator = value;
|
||
}
|
||
|
||
public int State
|
||
{
|
||
get => state;
|
||
set => state = value;
|
||
}
|
||
}
|
||
} |