abstract public void output(ByteBuf msg, Kcp kcp, Object user);
/** * flush pending data */ private void Flush() { int cur = current; int change = 0; int lost = 0; if (updated == 0) { return; } Segment seg = new Segment(0); seg.conv = conv; seg.cmd = IKCP_CMD_ACK; seg.wnd = wnd_unused(); seg.una = rcv_nxt; // flush acknowledges int c = acklist.Count / 2; for (int i = 0; i < c; i++) { if (buffer.ReadableBytes() + IKCP_OVERHEAD > mtu) { this.output.output(buffer, this, user); buffer = new ByteBuf((mtu + IKCP_OVERHEAD) * 3); } seg.sn = acklist[i * 2 + 0]; seg.ts = acklist[i * 2 + 1]; seg.Encode(buffer); } acklist.Clear(); // probe window size (if remote window size equals zero) if (rmt_wnd == 0) { if (probe_wait == 0) { 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 (buffer.ReadableBytes() + IKCP_OVERHEAD > mtu) { this.output.output(buffer, this, user); buffer = new ByteBuf((mtu + IKCP_OVERHEAD) * 3); } seg.Encode(buffer); } // flush window probing commands if ((probe & IKCP_ASK_TELL) != 0) { seg.cmd = IKCP_CMD_WINS; if (buffer.ReadableBytes() + IKCP_OVERHEAD > mtu) { this.output.output(buffer, this, user); buffer = new ByteBuf((mtu + IKCP_OVERHEAD) * 3); } seg.Encode(buffer); } probe = 0; // calculate window size int cwnd_temp = Math.Min(snd_wnd, rmt_wnd); if (nocwnd == 0) { cwnd_temp = Math.Min(cwnd, cwnd_temp); } // move data from snd_queue to snd_buf c = 0; for (int i = 0; i < snd_queue.Count; i++) { Segment item = snd_queue[i]; if (_itimediff(snd_nxt, snd_una + cwnd_temp) >= 0) { break; } Segment newseg = item; newseg.conv = conv; newseg.cmd = IKCP_CMD_PUSH; newseg.wnd = seg.wnd; newseg.ts = cur; newseg.sn = snd_nxt++; newseg.una = rcv_nxt; newseg.resendts = cur; newseg.rto = rx_rto; newseg.fastack = 0; newseg.xmit = 0; snd_buf.Add(newseg); c++; } if (c > 0) { snd_queue.RemoveRange(0, c); } // calculate resent int resent = (fastresend > 0) ? fastresend : int.MaxValue; int rtomin = (nodelay == 0) ? (rx_rto >> 3) : 0; // flush data segments for (int i = 0; i < snd_buf.Count; i++) { Segment segment = snd_buf[i]; bool needsend = false; if (segment.xmit == 0) { needsend = true; segment.xmit++; segment.rto = rx_rto; segment.resendts = cur + segment.rto + rtomin; } else if (_itimediff(cur, segment.resendts) >= 0) { needsend = true; segment.xmit++; xmit++; if (nodelay == 0) { segment.rto += rx_rto; } else { segment.rto += rx_rto / 2; } segment.resendts = cur + segment.rto; lost = 1; } else if (segment.fastack >= resent) { needsend = true; segment.xmit++; segment.fastack = 0; segment.resendts = cur + segment.rto; change++; } if (needsend) { segment.ts = cur; segment.wnd = seg.wnd; segment.una = rcv_nxt; int need = IKCP_OVERHEAD + segment.data.ReadableBytes(); if (buffer.ReadableBytes() + need > mtu) { this.output.output(buffer, this, user); buffer = new ByteBuf((mtu + IKCP_OVERHEAD) * 3); } segment.Encode(buffer); if (segment.data.ReadableBytes() > 0) { buffer.WriteBytes(segment.data.Duplicate()); } if (segment.xmit >= dead_link) { state = -1; } } } // flash remain segments if (buffer.ReadableBytes() > 0) { this.output.output(buffer, this, user); buffer = new ByteBuf((mtu + IKCP_OVERHEAD) * 3); } // update ssthresh if (change != 0) { int 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; } }
/** * * when you received a low level packet (eg. UDP packet), call it * * @param data * @return */ public int Input(ByteBuf data) { int una_temp = snd_una; int flag = 0, maxack = 0; if (data == null || data.ReadableBytes() < IKCP_OVERHEAD) { return(-1); } while (true) { bool readed = false; int ts; int sn; int len; int una; int conv_; int wnd; byte cmd; byte frg; if (data.ReadableBytes() < IKCP_OVERHEAD) { break; } conv_ = data.ReadIntLE(); if (this.conv != conv_) { return(-1); } cmd = data.ReadByte(); frg = data.ReadByte(); wnd = data.ReadShortLE(); ts = data.ReadIntLE(); sn = data.ReadIntLE(); una = data.ReadIntLE(); len = data.ReadIntLE(); if (data.ReadableBytes() < len) { return(-2); } switch ((int)cmd) { case IKCP_CMD_PUSH: case IKCP_CMD_ACK: case IKCP_CMD_WASK: case IKCP_CMD_WINS: break; default: return(-3); } rmt_wnd = wnd & 0x0000ffff; Parse_una(una); Shrink_buf(); switch (cmd) { case IKCP_CMD_ACK: if (_itimediff(current, ts) >= 0) { Update_ack(_itimediff(current, ts)); } Parse_ack(sn); Shrink_buf(); if (flag == 0) { flag = 1; maxack = sn; } else if (_itimediff(sn, maxack) > 0) { maxack = sn; } break; case IKCP_CMD_PUSH: if (_itimediff(sn, rcv_nxt + rcv_wnd) < 0) { Ack_push(sn, ts); if (_itimediff(sn, rcv_nxt) >= 0) { Segment seg = new Segment(len); seg.conv = conv_; seg.cmd = cmd; seg.frg = frg & 0x000000ff; seg.wnd = wnd; seg.ts = ts; seg.sn = sn; seg.una = una; if (len > 0) { seg.data.WriteBytes(data, len); readed = true; } Parse_data(seg); } } break; case IKCP_CMD_WASK: // ready to send back IKCP_CMD_WINS in Ikcp_flush // tell remote my window size probe |= IKCP_ASK_TELL; break; case IKCP_CMD_WINS: // do nothing break; default: return(-3); } if (!readed) { data.SkipBytes(len); } } if (flag != 0) { Parse_fastack(maxack); } if (_itimediff(snd_una, una_temp) > 0) { if (this.cwnd < this.rmt_wnd) { if (this.cwnd < this.ssthresh) { this.cwnd++; this.incr += mss; } else { if (this.incr < mss) { this.incr = mss; } this.incr += (mss * mss) / this.incr + (mss / 16); if ((this.cwnd + 1) * mss <= this.incr) { this.cwnd++; } } if (this.cwnd > this.rmt_wnd) { this.cwnd = this.rmt_wnd; this.incr = this.rmt_wnd * mss; } } } return(0); }
/** * user/upper level recv: returns size, returns below zero for EAGAIN * * @param buffer * @return */ public int Receive(ByteBuf buffer) { if (rcv_queue.Count <= 0) { return(-1); } int peekSize = PeekSize(); if (peekSize < 0) { return(-2); } bool recover = rcv_queue.Count >= rcv_wnd; // merge fragment. int c = 0; int len = 0; for (int i = 0; i < rcv_queue.Count; i++) { Segment seg = rcv_queue[i]; len += seg.data.ReadableBytes(); buffer.WriteBytes(seg.data); c++; if (seg.frg == 0) { break; } } if (c > 0) { rcv_queue.RemoveRange(0, c); } if (len != peekSize) { throw new Exception("数据异常."); } // move available data from rcv_buf -> rcv_queue c = 0; for (int i = 0; i < rcv_buf.Count; i++) { Segment seg = rcv_buf[i]; if (seg.sn == rcv_nxt && rcv_queue.Count < rcv_wnd) { rcv_queue.Add(seg); rcv_nxt++; c++; } else { break; } } if (c > 0) { rcv_buf.RemoveRange(0, c); } // fast recover if (rcv_queue.Count < rcv_wnd && recover) { // ready to send back IKCP_CMD_WINS in ikcp_flush // tell remote my window size probe |= IKCP_ASK_TELL; } return(len); }
public override void output(ByteBuf msg, Kcp kcp, Object user) { this.client.Send(msg.GetRaw(), msg.ReadableBytes()); }
/** * 处理收到的消息 */ protected abstract void HandleReceive(ByteBuf bb);