KCP:
using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Text; public class KCP { #region 常量 public const int IKCP_RTO_NDL = 30; // rto: 无延时模式下最小超时重传时间 no delay min rto public const int IKCP_RTO_MIN = 100; // rto: 正常模式最小超时重传 normal min rto public const int IKCP_RTO_DEF = 200; // rto: 默认超时重传 public const int IKCP_RTO_MAX = 60000; // rto: 最大超时超时 public const int IKCP_CMD_PUSH = 81; // cmd: 协议类型 [正常接收数据] push data public const int IKCP_CMD_ACK = 82; // cmd: 协议类型 [收到ack回复] ack public const int IKCP_CMD_WASK = 83; // cmd: 协议类型 [询问对方窗口size] window probe (ask) public const int IKCP_CMD_WINS = 84; // cmd: 协议类型 [告知对方我的窗口size] window size (tell) public const int IKCP_ASK_SEND = 1; // cmd: 是否需要发送 IKCP_CMD_WASK public const int IKCP_ASK_TELL = 2; // cmd: 是否需要发送 IKCP_CMD_WINS public const int IKCP_WND_SND = 32; // wnd: 发送队列滑动窗口最大值 public const int IKCP_WND_RCV = 32; // wnd: 接收队列滑动窗口最大值 public const int IKCP_MTU_DEF = 1400; // segment: 报文默认大小 [mtu 网络最小传输单元] public const int IKCP_ACK_FAST = 3; // null: 没有被用使用 public const int IKCP_INTERVAL = 100; // flush: 控制刷新时间间隔 public const int IKCP_OVERHEAD = 24; // segment: 报文默认大小 [mtu 网络最小传输单元] public const int IKCP_DEADLINK = 10; // rto: ??? public const int IKCP_THRESH_INIT = 2; // ssthresh: 慢热启动 初始窗口大小 public const int IKCP_THRESH_MIN = 2; // ssthresh: 慢热启动 最小窗口大小 public const int IKCP_PROBE_INIT = 7000; // probe: 请求询问远端窗口大小的初始时间 7 secs to probe window size public const int IKCP_PROBE_LIMIT = 120000; // probe: 请求询问远端窗口大小的最大时间 up to 120 secs to probe window #endregion #region 工具方法 // encode 8 bits unsigned int public static int ikcp_encode8u(byte[] p, int offset, byte c) { p[0 + offset] = c; return 1; } // decode 8 bits unsigned int public static int ikcp_decode8u(byte[] p, int offset, ref byte c) { c = p[0 + offset]; return 1; } /* encode 16 bits unsigned int (lsb) */ public static int ikcp_encode16u(byte[] p, int offset, UInt16 w) { p[0 + offset] = (byte)(w >> 0); p[1 + offset] = (byte)(w >> 8); return 2; } /* decode 16 bits unsigned int (lsb) */ public static int ikcp_decode16u(byte[] p, int offset, ref UInt16 c) { UInt16 result = 0; result |= (UInt16)p[0 + offset]; result |= (UInt16)(p[1 + offset] << 8); c = result; return 2; } /* encode 32 bits unsigned int (lsb) */ public static int ikcp_encode32u(byte[] p, int offset, UInt32 l) { p[0 + offset] = (byte)(l >> 0); p[1 + offset] = (byte)(l >> 8); p[2 + offset] = (byte)(l >> 16); p[3 + offset] = (byte)(l >> 24); return 4; } /* decode 32 bits unsigned int (lsb) */ public static int ikcp_decode32u(byte[] p, int offset, ref UInt32 c) { UInt32 result = 0; result |= (UInt32)p[0 + offset]; result |= (UInt32)(p[1 + offset] << 8); result |= (UInt32)(p[2 + offset] << 16); result |= (UInt32)(p[3 + offset] << 24); c = result; return 4; } public static byte[] slice(byte[] p, int start, int stop) { var bytes = new byte[stop - start]; Array.Copy(p, start, bytes, 0, bytes.Length); return bytes; } public static T[] slice<T>(T[] p, int start, int stop) { var arr = new T[stop - start]; var index = 0; for (var i = start; i < stop; i++) { arr[index] = p[i]; index++; } return arr; } public static byte[] append(byte[] p, byte c) { var bytes = new byte[p.Length + 1]; Array.Copy(p, bytes, p.Length); bytes[p.Length] = c; return bytes; } public static T[] append<T>(T[] p, T c) { var arr = new T[p.Length + 1]; for (var i = 0; i < p.Length; i++) arr[i] = p[i]; arr[p.Length] = c; return arr; } public static T[] append<T>(T[] p, T[] cs) { var arr = new T[p.Length + cs.Length]; for (var i = 0; i < p.Length; i++) arr[i] = p[i]; for (var i = 0; i < cs.Length; i++ ) arr[p.Length+i] = cs[i]; return arr; } static UInt32 _imin_(UInt32 a, UInt32 b) { return a <= b ? a : b; } static UInt32 _imax_(UInt32 a, UInt32 b) { return a >= b ? a : b; } static UInt32 _ibound_(UInt32 lower, UInt32 middle, UInt32 upper) { return _imin_(_imax_(lower, middle), upper); } static Int32 _itimediff(UInt32 later, UInt32 earlier) { return ((Int32)(later - earlier)); } #endregion #region 报文 // KCP Segment Definition internal class Segment { internal UInt32 conv = 0; // ID:服务器为当前连接分配的唯一ID internal UInt32 cmd = 0; // 报文类型:RUDP_CMD_PUSH | RUDP_CMD_ACK | RUDP_CMD_WASK|RUDP_CMD_WINS internal UInt32 frg = 0; // 报文在包中的位置:包被分割成n个报文进行发送, frg = n - index - 1 [倒序索引] internal UInt32 wnd = 0; // 接收窗口未被使用的大小:wnd = rcv_wnd - rcv_queue.Length internal UInt32 ts = 0; // 报文发送时间 internal UInt32 sn = 0; // ack 当前报文,等待回应的帧ID internal UInt32 una = 0; // ack 当前未应答最小帧号 internal UInt32 resendts = 0; // 下一次重传的时间 internal UInt32 rto = 0; // 超时重传机制的时间系数,它是会动态调整的 internal UInt32 fastack = 0; // 这个值会累加,当超过一个阈值的时候会触发一次重传。累计规则:收到比它后的帧号的ack应答后,它会被累加 internal UInt32 xmit = 0; // 重传次数,当xmit > dead_link 表示连接断开 internal byte[] data; internal Segment(int size) { this.data = new byte[size]; } // encode a segment into buffer internal int encode(byte[] ptr, int offset) { var offset_ = offset; offset += ikcp_encode32u(ptr, offset, conv); offset += ikcp_encode8u(ptr, offset, (byte)cmd); offset += ikcp_encode8u(ptr, offset, (byte)frg); offset += ikcp_encode16u(ptr, offset, (UInt16)wnd); offset += ikcp_encode32u(ptr, offset, ts); offset += ikcp_encode32u(ptr, offset, sn); offset += ikcp_encode32u(ptr, offset, una); offset += ikcp_encode32u(ptr, offset, (UInt32)data.Length); return offset - offset_; } } #endregion // kcp members. UInt32 conv; // 唯一ID UInt32 mtu; // 报文大小 UInt32 mss; // 报文body大小 UInt32 state; // 一个描述连接是否死亡的状态 UInt32 snd_una; // 发送队列等待的ack最小帧号 UInt32 snd_nxt; // 发送队列下一个待发送的帧号 UInt32 rcv_nxt; // 接收队列下一个待接收的帧号 UInt32 ts_recent; // [未使用] UInt32 ts_lastack; // [未使用] UInt32 ssthresh; // 慢热启动系数,初始为 IKCP_THRESH_INIT #region rto 超时重传 UInt32 rx_rttval; // UInt32 rx_srtt; UInt32 rx_rto; // 超时重传系数 UInt32 rx_minrto; #endregion UInt32 snd_wnd; // 发送窗口大小 UInt32 rcv_wnd; // 接收窗口大小 UInt32 rmt_wnd; // 远端接收窗口大小 UInt32 cwnd; // 窗口大小 UInt32 probe; // 请求窗口size | 发送窗口size UInt32 current; // 当前Update时间 UInt32 interval; // 刷新间隔时间 UInt32 ts_flush; // 上一次flush的时间 UInt32 xmit; // 重传次数 UInt32 nodelay; // 无延迟模式系数 UInt32 updated; // 是否是第一次更新 UInt32 ts_probe; // 下一次请求探测远端窗口大小的时间 UInt32 probe_wait; // 探测等待时间,一个动态调整的值 UInt32 dead_link; // 超时重传超过这个值,视作连接断开 UInt32 incr; // 慢热启动 Segment[] snd_queue = new Segment[0]; // 发送队列 buffer -> snd_queue -> snd_buf snd_buf = 窗口 Segment[] rcv_queue = new Segment[0]; // 接收队列 rcv_buf -> rcv_queue -> buffer rcv_buf = 窗口 Segment[] snd_buf = new Segment[0]; Segment[] rcv_buf = new Segment[0]; UInt32[] acklist = new UInt32[0]; // 待返回的ack应答序列 byte[] buffer; // 内部报文容器 Int32 fastresend; // 快速重传 Int32 nocwnd; Int32 logmask; // buffer, size Action<byte[], int> output; // create a new kcp control object, 'conv' must equal in two endpoint // from the same connection. public KCP(UInt32 conv_, Action<byte[], int> output_) { conv = conv_; snd_wnd = IKCP_WND_SND; rcv_wnd = IKCP_WND_RCV; rmt_wnd = IKCP_WND_RCV; mtu = IKCP_MTU_DEF; mss = mtu - IKCP_OVERHEAD; rx_rto = IKCP_RTO_DEF; rx_minrto = IKCP_RTO_MIN; interval = IKCP_INTERVAL; ts_flush = IKCP_INTERVAL; ssthresh = IKCP_THRESH_INIT; dead_link = IKCP_DEADLINK; buffer = new byte[(mtu+IKCP_OVERHEAD)*3]; output = output_; } /// <summary> /// 检测是否有完整的包,返回包的长度,-1 表示没有 /// </summary> public int PeekSize() { // 1.接收队列为空 if (0 == rcv_queue.Length) return -1; // 2.当 frg = 0 表示已经接收到包的最后一个报文 var seq = rcv_queue[0]; if (0 == seq.frg) return seq.data.Length; // 3.接收队列的大小 小于 包的总报文数量,肯定不够 if (rcv_queue.Length < seq.frg + 1) return -1; // 4.计算一个完整包的长度 int length = 0; foreach (var item in rcv_queue) { length += item.data.Length; if (0 == item.frg) // 当 frg = 0 为最后一个报文 break; } return length; } /// <summary> /// rcv_queue -> buffer /// </summary> public int Recv(byte[] buffer) { // 1.处理异常流程 if (0 == rcv_queue.Length) return -1; // 接收队列为空 var peekSize = PeekSize(); if (0 > peekSize) return -2; // size小于0 if (peekSize > buffer.Length) return -3; // size大于buffer长度 var fast_recover = false; if (rcv_queue.Length >= rcv_wnd) fast_recover = true; // 接收队列大于滑动窗口的size,触发快速发送我的窗口size给远端 // 2.拷贝rcv_queue到buffer,直到 frg = 0 var count = 0; var n = 0; foreach (var seg in rcv_queue) { Array.Copy(seg.data, 0, buffer, n, seg.data.Length); n += seg.data.Length; count++; if (0 == seg.frg) break; } if (0 < count) { rcv_queue = slice<Segment>(rcv_queue, count, rcv_queue.Length); } // 3.rcv_buf -> rcv_queue:直到碰到缺口,或窗口变满 count = 0; foreach (var seg in rcv_buf) { if (seg.sn == rcv_nxt && rcv_queue.Length < rcv_wnd) { rcv_queue = append<Segment>(rcv_queue, seg); rcv_nxt++; count++; } else { break; } } if(0 < count) rcv_buf = slice<Segment>(rcv_buf, count, rcv_buf.Length); // fast recover if (rcv_queue.Length < rcv_wnd && fast_recover) { // ready to send back IKCP_CMD_WINS in ikcp_flush // tell remote my window size probe |= IKCP_ASK_TELL; // 通知远端,我的窗口size } return n; } /// <summary> /// buffer -> snd_queue: -1:buff为null -2:buff > mss * 255 超过上限 /// </summary> /// <param name="buffer"></param> /// <returns></returns> public int Send(byte[] buffer) { // 1.计算拆分的报文数 count if (0 == buffer.Length) return -1; var count = 0; if (buffer.Length < mss) count = 1; else count = (int)(buffer.Length + mss - 1) / (int)mss; if (255 < count) return -2; if (0 == count) count = 1; // 2.把 buff 转换成 snd_queue var offset = 0; for (var i = 0; i < count; i++) { var size = 0; if (buffer.Length - offset > mss) size = (int)mss; else size = buffer.Length - offset; var seg = new Segment(size); Array.Copy(buffer, offset, seg.data, 0, size); offset += size; seg.frg = (UInt32)(count - i - 1); snd_queue = append<Segment>(snd_queue, seg); } return 0; } /// <summary> /// rtt算法:调整rto的大小 [超时重传] /// </summary> void update_ack(Int32 rtt) { if (0 == rx_srtt) { rx_srtt = (UInt32)rtt; rx_rttval = (UInt32)rtt / 2; } else { Int32 delta = (Int32)((UInt32)rtt - rx_srtt); if (0 > delta) delta = -delta; rx_rttval = (3 * rx_rttval + (uint)delta) / 4; rx_srtt = (UInt32)((7 * rx_srtt + rtt) / 8); if (rx_srtt < 1) rx_srtt = 1; } var rto = (int)(rx_srtt + _imax_(1, 4 * rx_rttval)); rx_rto = _ibound_(rx_minrto, (UInt32)rto, IKCP_RTO_MAX); } /// <summary> /// 重置:snd_una 最小待确认帧号 /// </summary> void shrink_buf() { if (snd_buf.Length > 0) snd_una = snd_buf[0].sn; else snd_una = snd_nxt; } /// <summary> /// 把收到ack的报文,从snd_buf确认队列中移除 /// </summary> void parse_ack(UInt32 sn) { // 1.如果sn小于snd_una,说明是一个重复ack包, sn大于snd_nxt,说明sn是错的 if (_itimediff(sn, snd_una) < 0 || _itimediff(sn, snd_nxt) >= 0) return; // 2.把已经应答的内容,从snd_buf里面移出 var index = 0; foreach (var seg in snd_buf) { if (sn == seg.sn) { snd_buf = append<Segment>(slice<Segment>(snd_buf, 0, index), slice<Segment>(snd_buf, index + 1, snd_buf.Length)); break; } else { seg.fastack++; // 说明后面的应答了,但是前面的还没有,所以增加前面的ack的权重 } index++; } } /// <summary> /// snd_buf 向下移动 /// </summary> void parse_una(UInt32 una) { var count = 0; foreach (var seg in snd_buf) { if (_itimediff(una, seg.sn) > 0) count++; else break; } if (0 < count) snd_buf = slice<Segment>(snd_buf, count, snd_buf.Length); } void ack_push(UInt32 sn, UInt32 ts) { acklist = append<UInt32>(acklist, new UInt32[2]{sn, ts}); } void ack_get(int p, ref UInt32 sn, ref UInt32 ts) { sn = acklist[p * 2 + 0]; ts = acklist[p * 2 + 1]; } /// <summary> /// 把报文放入 rcv_buf /// </summary> void parse_data(Segment newseg) { var sn = newseg.sn; if (_itimediff(sn, rcv_nxt + rcv_wnd) >= 0 || _itimediff(sn, rcv_nxt) < 0) return; // 超出窗口,或者重复消息都会被丢弃 // 1.查询报文的位置 var n = rcv_buf.Length - 1; var after_idx = -1; var repeat = false; for (var i = n; i >= 0; i--) { var seg = rcv_buf[i]; if (seg.sn == sn) { repeat = true; break; } if (_itimediff(sn, seg.sn) > 0) { after_idx = i; break; } } // 2.插入到指定位置 if (!repeat) { if (after_idx == -1) rcv_buf = append<Segment>(new Segment[1] { newseg }, rcv_buf); else rcv_buf = append<Segment>(slice<Segment>(rcv_buf, 0, after_idx + 1), append<Segment>(new Segment[1] { newseg }, slice<Segment>(rcv_buf, after_idx + 1, rcv_buf.Length))); } // 3.rcv_buf -> rcv_queue var count = 0; foreach (var seg in rcv_buf) { if (seg.sn == rcv_nxt && rcv_queue.Length < rcv_wnd) { rcv_queue = append<Segment>(rcv_queue, seg); rcv_nxt++; count++; } else { break; } } if (0 < count) { rcv_buf = slice<Segment>(rcv_buf, count, rcv_buf.Length); } } /// <summary> /// 接收报文处理:-1:conv不合法 -2:报文体内容为0 -3:cmd类型不对 /// 一个mtu不止一个报文 /// </summary> public int Input(byte[] data) { var s_una = snd_una; if (data.Length < IKCP_OVERHEAD) return 0; var offset = 0; while (true) { // 1.获取报文内容 UInt32 ts = 0; UInt32 sn = 0; UInt32 length = 0; UInt32 una = 0; UInt32 conv_ = 0; UInt16 wnd = 0; byte cmd = 0; byte frg = 0; // 1.1 判断大小是否够一个最小报文 if (data.Length - offset < IKCP_OVERHEAD) break; // 1.2 即便大小够,也不一定是一个报文,需要判断 conv_ 值 offset += ikcp_decode32u(data, offset, ref conv_); if (conv != conv_) return -1; offset += ikcp_decode8u(data, offset, ref cmd); offset += ikcp_decode8u(data, offset, ref frg); offset += ikcp_decode16u(data, offset, ref wnd); offset += ikcp_decode32u(data, offset, ref ts); offset += ikcp_decode32u(data, offset, ref sn); offset += ikcp_decode32u(data, offset, ref una); offset += ikcp_decode32u(data, offset, ref length); // 1.3 大小不一致 if (data.Length - offset < length) return -2; switch (cmd) { case IKCP_CMD_PUSH: case IKCP_CMD_ACK: case IKCP_CMD_WASK: case IKCP_CMD_WINS: break; default: return -3; // 1.4 协议类型不对 } rmt_wnd = (UInt32)wnd; parse_una(una); // 刷新snd_buf shrink_buf(); // 重置:snd_una // 1.5 基于协议内容处理报文 if (IKCP_CMD_ACK == cmd) { if (_itimediff(current, ts) >= 0) { update_ack(_itimediff(current, ts)); // 调整 rto } parse_ack(sn); // 刷新 snd_buf,基于ack shrink_buf(); // 重置:snd_una } else if (IKCP_CMD_PUSH == cmd) { // 1.5.1 处理普通报文 if (_itimediff(sn, rcv_nxt + rcv_wnd) < 0) { ack_push(sn, ts); if (_itimediff(sn, rcv_nxt) >= 0) { var seg = new Segment((int)length); seg.conv = conv_; seg.cmd = (UInt32)cmd; seg.frg = (UInt32)frg; seg.wnd = (UInt32)wnd; seg.ts = ts; seg.sn = sn; seg.una = una; if (length > 0) Array.Copy(data, offset, seg.data, 0, length); parse_data(seg); } } } else if (IKCP_CMD_WASK == cmd) { // ready to send back IKCP_CMD_WINS in Ikcp_flush // tell remote my window size probe |= IKCP_ASK_TELL; // 远端请求窗口size,把状态置为发送size } else if (IKCP_CMD_WINS == cmd) { // do nothing } else { return -3; } offset += (int)length; } // 1.6 慢热启动 if (_itimediff(snd_una, s_una) > 0) { if (cwnd < rmt_wnd) { var mss_ = 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 > rmt_wnd) { cwnd = rmt_wnd; incr = rmt_wnd * mss_; } } } return 0; } Int32 wnd_unused() { if (rcv_queue.Length < rcv_wnd) return (Int32)(int)rcv_wnd - rcv_queue.Length; return 0; } /// <summary> /// 刷新数据 /// </summary> void flush() { var current_ = current; var buffer_ = buffer; var change = 0; var lost = 0; if (0 == updated) return; var seg = new Segment(0); seg.conv = conv; seg.cmd = IKCP_CMD_ACK; seg.wnd = (UInt32)wnd_unused(); seg.una = rcv_nxt; #region ACK:应答 // 1.ACK:对接收到的报文进行应答 var count = acklist.Length / 2; var offset = 0; for (var i = 0; i < count; i++) { if (offset + IKCP_OVERHEAD > mtu) { output(buffer, offset); //Array.Clear(buffer, 0, offset); offset = 0; } ack_get(i, ref seg.sn, ref seg.ts); offset += seg.encode(buffer, offset); } acklist = new UInt32[0]; #endregion #region WASK:询问对端窗口大小 if (0 == rmt_wnd) { if (0 == probe_wait) { probe_wait = IKCP_PROBE_INIT; ts_probe = current + probe_wait; } else { if (_itimediff(current, ts_probe) >= 0) { if (probe_wait < IKCP_PROBE_INIT) probe_wait = IKCP_PROBE_INIT; probe_wait += probe_wait / 2; if (probe_wait > IKCP_PROBE_LIMIT) probe_wait = IKCP_PROBE_LIMIT; ts_probe = current + probe_wait; probe |= IKCP_ASK_SEND; } } } else { ts_probe = 0; probe_wait = 0; } // flush window probing commands if ((probe & IKCP_ASK_SEND) != 0) { seg.cmd = IKCP_CMD_WASK; if (offset + IKCP_OVERHEAD > (int)mtu) { output(buffer, offset); //Array.Clear(buffer, 0, offset); offset = 0; } offset += seg.encode(buffer, offset); } probe = 0; #endregion #region Push:snd_queue -> snd_buf var cwnd_ = _imin_(snd_wnd, rmt_wnd); if (0 == nocwnd) cwnd_ = _imin_(cwnd, cwnd_); count = 0; for (var k = 0; k < snd_queue.Length; k++ ) { if (_itimediff(snd_nxt, snd_una + cwnd_) >= 0) break; var newseg = snd_queue[k]; newseg.conv = conv; newseg.cmd = IKCP_CMD_PUSH; newseg.wnd = seg.wnd; newseg.ts = current_; newseg.sn = snd_nxt; newseg.una = rcv_nxt; newseg.resendts = current_; newseg.rto = rx_rto; newseg.fastack = 0; newseg.xmit = 0; snd_buf = append<Segment>(snd_buf, newseg); snd_nxt++; count++; } if (0 < count) { snd_queue = slice<Segment>(snd_queue, count, snd_queue.Length); } #endregion #region Resent:超时重传 // calculate resent var resent = (UInt32)fastresend; if (fastresend <= 0) resent = 0xffffffff; var rtomin = rx_rto >> 3; if(nodelay != 0) rtomin = 0; // flush data segments foreach (var segment in snd_buf) { var needsend = false; var debug = _itimediff(current_, segment.resendts); if (0 == segment.xmit) { // 满足条件:一次都没重发 needsend = true; segment.xmit++; segment.rto = rx_rto; segment.resendts = current_ + segment.rto + rtomin; } else if (_itimediff(current_, segment.resendts) >= 0) { // 满足条件:当前时间超过,重发时间 needsend = true; segment.xmit++; xmit++; if (0 == nodelay) segment.rto += rx_rto; else segment.rto += rx_rto / 2; segment.resendts = current_ + segment.rto; lost = 1; } else if (segment.fastack >= resent){ // 满足条件:fastack 超过重传阈值 needsend = true; segment.xmit++; segment.fastack = 0; segment.resendts = current_ + segment.rto; change++; } // 重传操作 if (needsend) { segment.ts = current_; segment.wnd = seg.wnd; segment.una = rcv_nxt; var need = IKCP_OVERHEAD + segment.data.Length; if (offset + need > mtu) { output(buffer, offset); //Array.Clear(buffer, 0, offset); offset = 0; } offset += segment.encode(buffer, offset); if (segment.data.Length > 0) { Array.Copy(segment.data, 0, buffer, offset, segment.data.Length); offset += segment.data.Length; } if (segment.xmit >= dead_link) { // 单个报文重传超过指定次数,视为断线 state = 0; } } } #endregion // flash remain segments if (offset > 0) { output(buffer, offset); //Array.Clear(buffer, 0, offset); offset = 0; } #region ssthresh:慢热启动 if (change != 0) { var inflight = snd_nxt - snd_una; ssthresh = inflight / 2; if (ssthresh < IKCP_THRESH_MIN) ssthresh = IKCP_THRESH_MIN; cwnd = ssthresh + resent; incr = cwnd * mss; } if (lost != 0) { ssthresh = cwnd / 2; if (ssthresh < IKCP_THRESH_MIN) ssthresh = IKCP_THRESH_MIN; cwnd = 1; incr = mss; } if (cwnd < 1) { cwnd = 1; incr = mss; } #endregion } // update state (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. public void Update(UInt32 current_) { current = current_; if (0 == updated) { // 第一次处理 updated = 1; ts_flush = current; } var slap = _itimediff(current, ts_flush); if (slap >= 10000 || slap < -10000) { // 超时处理 ts_flush = current; slap = 0; } if (slap >= 0){ // 利用 ts_flush 控制刷新的时间间隔,interval 控制频率 ts_flush += interval; if (_itimediff(current, ts_flush) >= 0) ts_flush = current + interval; flush(); } } // 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) // 检测是否需要立即刷新 public UInt32 Check(UInt32 current_) { if (0 == updated) return current_; var ts_flush_ = ts_flush; var tm_flush_ = 0x7fffffff; var tm_packet = 0x7fffffff; var minimal = 0; // 1.如果超时,立即刷新 if (_itimediff(current_, ts_flush_) >= 10000 || _itimediff(current_, ts_flush_) < -10000) { ts_flush_ = current_; } // 2.如果超过了应该刷新的时间,立即刷新 if (_itimediff(current_, ts_flush_) >= 0) return current_; tm_flush_ = (int)_itimediff(ts_flush_, current_); // 3.如果有需要超时重传的报文,立即刷新 foreach (var seg in snd_buf) { var diff = _itimediff(seg.resendts, current_); if (diff <= 0) return current_; if (diff < tm_packet) tm_packet = (int)diff; } // 4.计算最终下一次更新的时间 minimal = (int)tm_packet; if (tm_packet >= tm_flush_) minimal = (int)tm_flush_; if (minimal >= interval) minimal = (int)interval; return current_ + (UInt32)minimal; } // change MTU size, default is 1400 public int SetMtu(Int32 mtu_) { if (mtu_ < 50 || mtu_ < (Int32)IKCP_OVERHEAD) return -1; var buffer_ = new byte[(mtu_ + IKCP_OVERHEAD) * 3]; if (null == buffer_) return -2; mtu = (UInt32)mtu_; mss = mtu - IKCP_OVERHEAD; buffer = buffer_; return 0; } public int Interval(Int32 interval_) { if (interval_ > 5000) { interval_ = 5000; } else if (interval_ < 10) { interval_ = 10; } interval = (UInt32)interval_; return 0; } // fastest: ikcp_nodelay(kcp, 1, 20, 2, 1) // nodelay: 0:disable(default), 1:enable // interval: internal update timer interval in millisec, default is 100ms // resend: 0:disable fast resend(default), 1:enable fast resend // nc: 0:normal congestion control(default), 1:disable congestion control public int NoDelay(int nodelay_, int interval_, int resend_, int nc_) { if (nodelay_ > 0) { nodelay = (UInt32)nodelay_; if (nodelay_ != 0) rx_minrto = IKCP_RTO_NDL; else rx_minrto = IKCP_RTO_MIN; } if (interval_ >= 0) { if (interval_ > 5000) { interval_ = 5000; } else if (interval_ < 10) { interval_ = 10; } interval = (UInt32)interval_; } if (resend_ >= 0) fastresend = resend_; if (nc_ >= 0) nocwnd = nc_; return 0; } // set maximum window size: sndwnd=32, rcvwnd=32 by default public int WndSize(int sndwnd, int rcvwnd) { if (sndwnd > 0) snd_wnd = (UInt32)sndwnd; if (rcvwnd > 0) rcv_wnd = (UInt32)rcvwnd; return 0; } // get how many packet is waiting to be sent public int WaitSnd() { return snd_buf.Length + snd_queue.Length; } }