// when you received a low level packet (eg. UDP packet), call it public int Input(byte[] data) { var s_una = snd_una; if (data.Length < IKCP_OVERHEAD) { return(0); } var offset = 0; while (true) { UInt32 ts = 0; UInt32 sn = 0; UInt32 length = 0; UInt32 una = 0; UInt32 conv_ = 0; UInt16 wnd = 0; byte cmd = 0; byte frg = 0; if (data.Length - offset < IKCP_OVERHEAD) { break; } 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); 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); } rmt_wnd = (UInt32)wnd; parse_una(una); shrink_buf(); if (IKCP_CMD_ACK == cmd) { if (_itimediff(current, ts) >= 0) { update_ack(_itimediff(current, ts)); } parse_ack(sn); shrink_buf(); } else if (IKCP_CMD_PUSH == cmd) { if (_itimediff(sn, rcv_nxt + rcv_wnd) < 0) { ack_push(sn, ts); if (_itimediff(sn, rcv_nxt) >= 0) { //var seg = new Segment((int)length); QueueNode <Segment> node = AlocSegmentNode((int)length); Segment seg = node.item; 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.item.data, 0, length); } parse_data(node); } } } 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; } else if (IKCP_CMD_WINS == cmd) { // do nothing } else { return(-3); } offset += (int)length; } 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); }
// flush pending data void flush() { var current_ = current; var change = 0; var lost = 0; if (0 == updated) { return; } Segment seg = m_oTeampSeg;// new Segment(0); seg.ResetState(); seg.conv = conv; seg.cmd = IKCP_CMD_ACK; seg.wnd = (UInt32)wnd_unused(); seg.una = rcv_nxt; // flush acknowledges var count = acklist.Count / 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]; acklist.Clear(); // probe window size (if remote window size equals zero) 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; // calculate window size var cwnd_ = _imin_(snd_wnd, rmt_wnd); if (0 == nocwnd) { cwnd_ = _imin_(cwnd, cwnd_); } count = 0; int nLen = snd_queue.Count; QueueNode <Segment> node = null; Segment segment = null; for (var k = 0; k < nLen; k++) { if (_itimediff(snd_nxt, snd_una + cwnd_) >= 0) { break; } node = snd_queue[k]; segment = node.item; // var newseg = snd_queue[k]; segment.conv = conv; segment.cmd = IKCP_CMD_PUSH; segment.wnd = seg.wnd; segment.ts = current_; segment.sn = snd_nxt; segment.una = rcv_nxt; segment.resendts = current_; segment.rto = rx_rto; segment.fastack = 0; segment.xmit = 0; //snd_buf = append<Segment>(snd_buf, newseg); snd_buf.Add(node); snd_nxt++; count++; } if (0 < count) { //snd_queue = slice<Segment>(snd_queue, count, snd_queue.Length); snd_queue.RemoveRange(0, count); } // calculate resent var resent = (UInt32)fastresend; if (fastresend <= 0) { resent = 0xffffffff; } var rtomin = rx_rto >> 3; if (nodelay != 0) { rtomin = 0; } // flush data segments nLen = snd_buf.Count; //foreach (var segment in snd_buf) { for (int i = 0; i < nLen; ++i) { node = snd_buf[i]; segment = node.item; var needsend = false; _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) { 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.item.nLen; if (offset + need > mtu) { output(buffer, offset); //Array.Clear(buffer, 0, offset); offset = 0; } offset += segment.encode(buffer, offset); if (segment.data.item.nLen > 0) { Array.Copy(segment.data.item.data, 0, buffer, offset, segment.data.item.nLen); offset += segment.data.item.nLen; } if (segment.xmit >= dead_link) { //state = 0; } } } // flash remain segments if (offset > 0) { output(buffer, offset); //Array.Clear(buffer, 0, offset); offset = 0; } // update 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; } }
// user/upper level send, returns below zero for error public int Send(byte[] buffer, int nBufferSize) { if (0 == nBufferSize) { return(-1); } var count = 0; if (nBufferSize < mss) { count = 1; } else { count = (int)(nBufferSize + mss - 1) / (int)mss; } if (255 < count) { return(-2); } if (0 == count) { count = 1; } var offset = 0; QueueNode <Segment> node = null; Segment seg = null; for (var i = 0; i < count; i++) { var size = 0; if (nBufferSize - offset > mss) { size = (int)mss; } else { size = nBufferSize - offset; } node = AlocSegmentNode(size); seg = node.item; Array.Copy(buffer, offset, seg.data.item.data, 0, size); offset += size; seg.frg = (UInt32)(count - i - 1); snd_queue.Add(node); /* * var seg = new Segment(size); * Array.Copy(buffer, offset, seg.data.item.data, 0, size); * offset += size; * seg.frg = (UInt32)(count - i - 1); * snd_queue = append<Segment>(snd_queue, seg); */ } return(0); }
void parse_data(QueueNode <Segment> newNode) { var sn = newNode.item.sn; if (_itimediff(sn, rcv_nxt + rcv_wnd) >= 0 || _itimediff(sn, rcv_nxt) < 0) { return; } var n = rcv_buf.Count - 1; var after_idx = -1; var repeat = false; QueueNode <Segment> node = null; Segment seg = null; for (int i = n; i >= 0; i--) { node = rcv_buf[i]; seg = node.item; if (seg.sn == sn) { repeat = true; break; } if (_itimediff(sn, seg.sn) > 0) { after_idx = i; break; } } if (!repeat) { /* * if (after_idx == -1) * { * //rcv_buf = append<Segment>(new Segment[1] { newseg }, rcv_buf); * rcv_buf.Insert(0, newNode); * } * * 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))); * * rcv_buf.Insert(after_idx + 1, newNode); * * } */ rcv_buf.Insert(after_idx + 1, newNode); } // move available data from rcv_buf -> rcv_queue MoveRecvBuff2RecvQueue(); /* * // move available data from 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); * } */ }
internal void Clear() { ResetState(); data = null; }
internal Segment() { data = null; }
// 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; if (_itimediff(current_, ts_flush_) >= 10000 || _itimediff(current_, ts_flush_) < -10000) { ts_flush_ = current_; } if (_itimediff(current_, ts_flush_) >= 0) { return(current_); } tm_flush_ = (int)_itimediff(ts_flush_, current_); QueueNode <Segment> node = null; int nCount = snd_buf.Count; for (int i = 0; i < nCount; ++i) { node = snd_buf[i]; Segment seg = node.item; var diff = _itimediff(seg.resendts, current_); if (diff <= 0) { return(current_); } if (diff < tm_packet) { tm_packet = (int)diff; } } /* * 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; * } */ minimal = (int)tm_packet; if (tm_packet >= tm_flush_) { minimal = (int)tm_flush_; } if (minimal >= interval) { minimal = (int)interval; } return(current_ + (UInt32)minimal); }
//压入节点(在生产者线程调用) public void Push(QueueNode <ByteData> node) { m_oDataQueue.Push(node); }
//回收节点(在消费者线程调用) public void Recycle(ref QueueNode <ByteData> node) { m_oRecycleQueue.Push(node); }