/** * user/upper level send, returns below zero for error * * @param buffer * @return */ public int Send(ByteBuf buffer) { if (buffer.ReadableBytes() == 0) { return(-1); } // append to previous segment in streaming mode (if possible) if (this.stream && this.snd_queue.Count > 0) { Segment seg = snd_queue.Last(); if (seg.data != null && seg.data.ReadableBytes() < mss) { int capacity = mss - seg.data.ReadableBytes(); int extend = (buffer.ReadableBytes() < capacity) ? buffer.ReadableBytes() : capacity; seg.data.WriteBytes(buffer, extend); if (buffer.ReadableBytes() == 0) { return(0); } } } int count; if (buffer.ReadableBytes() <= mss) { count = 1; } else { count = (buffer.ReadableBytes() + mss - 1) / mss; } if (count > 255) { return(-2); } if (count == 0) { count = 1; } //fragment for (int i = 0; i < count; i++) { int size = buffer.ReadableBytes() > mss ? mss : buffer.ReadableBytes(); Segment seg = new Segment(size); seg.data.WriteBytes(buffer, size); seg.frg = this.stream?0:count - i - 1; snd_queue.Add(seg); } return(0); }
/** * encode a segment into buffer * * @param buf * @param offset * @return */ public int Encode(ByteBuf buf) { int off = buf.WriterIndex(); buf.WriteIntLE(conv); buf.WriteByte(cmd); buf.WriteByte((byte)frg); buf.WriteShortLE((short)wnd); buf.WriteIntLE(ts); buf.WriteIntLE(sn); buf.WriteIntLE(una); buf.WriteIntLE(data == null ? 0 : data.ReadableBytes()); return(buf.WriterIndex() - off); }
/** * 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); }
public override void output(ByteBuf msg, Kcp kcp, Object user) { this.client.Send(msg.GetRaw(), msg.ReadableBytes()); }