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;
							 | 
						|||
| 
								 | 
							
								        }
							 | 
						|||
| 
								 | 
							
								    }
							 | 
						|||
| 
								 | 
							
								}
							 |