kcp java_KCP.java

//=====================================================================

//

// KCP - A Better ARQ Protocol Implementation

// skywind3000 (at) gmail.com, 2010-2011

//

// Features:

// + Average RTT reduce 30% - 40% vs traditional ARQ like tcp.

// + Maximum RTT reduce three times vs tcp.

// + Lightweight, distributed as a single source file.

//

//=====================================================================

package kcp;

import java.util.ArrayList;

public abstract class KCP {

//=====================================================================

// KCP BASIC

//=====================================================================

public final int IKCP_RTO_NDL = 30; // no delay min rto

public final int IKCP_RTO_MIN = 100; // normal min rto

public final int IKCP_RTO_DEF = 200;

public final int IKCP_RTO_MAX = 60000;

public final int IKCP_CMD_PUSH = 81; // cmd: push data

public final int IKCP_CMD_ACK = 82; // cmd: ack

public final int IKCP_CMD_WASK = 83; // cmd: window probe (ask)

public final int IKCP_CMD_WINS = 84; // cmd: window size (tell)

public final int IKCP_ASK_SEND = 1; // need to send IKCP_CMD_WASK

public final int IKCP_ASK_TELL = 2; // need to send IKCP_CMD_WINS

public final int IKCP_WND_SND = 32;

public final int IKCP_WND_RCV = 32;

public final int IKCP_MTU_DEF = 1400;

public final int IKCP_ACK_FAST = 3;

public final int IKCP_INTERVAL = 100;

public final int IKCP_OVERHEAD = 24;

public final int IKCP_DEADLINK = 10;

public final int IKCP_THRESH_INIT = 2;

public final int IKCP_THRESH_MIN = 2;

public final int IKCP_PROBE_INIT = 7000; // 7 secs to probe window size

public final int IKCP_PROBE_LIMIT = 120000; // up to 120 secs to probe window

protected abstract void output(byte[] buffer, int size); // 需具体实现

// encode 8 bits unsigned int

public static void ikcp_encode8u(byte[] p, int offset, byte c) {

p[0 + offset] = c;

}

// decode 8 bits unsigned int

public static byte ikcp_decode8u(byte[] p, int offset) {

return p[0 + offset];

}

/* encode 16 bits unsigned int (msb) */

public static void ikcp_encode16u(byte[] p, int offset, int w) {

p[offset + 0] = (byte) (w >> 8);

p[offset + 1] = (byte) (w >> 0);

}

/* decode 16 bits unsigned int (msb) */

public static int ikcp_decode16u(byte[] p, int offset) {

int ret = (p[offset + 0] & 0xFF) << 8

| (p[offset + 1] & 0xFF);

return ret;

}

/* encode 32 bits unsigned int (msb) */

public static void ikcp_encode32u(byte[] p, int offset, long l) {

p[offset + 0] = (byte) (l >> 24);

p[offset + 1] = (byte) (l >> 16);

p[offset + 2] = (byte) (l >> 8);

p[offset + 3] = (byte) (l >> 0);

}

/* decode 32 bits unsigned int (msb) */

public static long ikcp_decode32u(byte[] p, int offset) {

long ret = (p[offset + 0] & 0xFFL) << 24

| (p[offset + 1] & 0xFFL) << 16

| (p[offset + 2] & 0xFFL) << 8

| p[offset + 3] & 0xFFL;

return ret;

}

public static void slice(ArrayList list, int start, int stop) {

int size = list.size();

for (int i = 0; i < size; ++i) {

if (i < stop - start) {

list.set(i, list.get(i + start));

} else {

list.remove(stop - start);

}

}

}

static long _imin_(long a, long b) {

return a <= b ? a : b;

}

static long _imax_(long a, long b) {

return a >= b ? a : b;

}

static long _ibound_(long lower, long middle, long upper) {

return _imin_(_imax_(lower, middle), upper);

}

static int _itimediff(long later, long earlier) {

return ((int) (later - earlier));

}

private class Segment {

protected long conv = 0;

protected long cmd = 0;

protected long frg = 0;

protected long wnd = 0;

protected long ts = 0;

protected long sn = 0;

protected long una = 0;

protected long resendts = 0;

protected long rto = 0;

protected long fastack = 0;

protected long xmit = 0;

protected byte[] data;

protected Segment(int size) {

this.data = new byte[size];

}

//---------------------------------------------------------------------

// ikcp_encode_seg

//---------------------------------------------------------------------

// encode a segment into buffer

protected int encode(byte[] ptr, int offset) {

int offset_ = offset;

ikcp_encode32u(ptr, offset, conv);

offset += 4;

ikcp_encode8u(ptr, offset, (byte) cmd);

offset += 1;

ikcp_encode8u(ptr, offset, (byte) frg);

offset += 1;

ikcp_encode16u(ptr, offset, (int) wnd);

offset += 2;

ikcp_encode32u(ptr, offset, ts);

offset += 4;

ikcp_encode32u(ptr, offset, sn);

offset += 4;

ikcp_encode32u(ptr, offset, una);

offset += 4;

ikcp_encode32u(ptr, offset, (long) data.length);

offset += 4;

return offset - offset_;

}

}

long conv = 0;

//long user = user;

long snd_una = 0;

long snd_nxt = 0;

long rcv_nxt = 0;

long ts_recent = 0;

long ts_lastack = 0;

long ts_probe = 0;

long probe_wait = 0;

long snd_wnd = IKCP_WND_SND;

long rcv_wnd = IKCP_WND_RCV;

long rmt_wnd = IKCP_WND_RCV;

long cwnd = 0;

long incr = 0;

long probe = 0;

long mtu = IKCP_MTU_DEF;

long mss = this.mtu - IKCP_OVERHEAD;

byte[] buffer = new byte[(int) (mtu + IKCP_OVERHEAD) * 3];

ArrayList nrcv_buf = new ArrayList<>(128);

ArrayList nsnd_buf = new ArrayList<>(128);

ArrayList nrcv_que = new ArrayList<>(128);

ArrayList nsnd_que = new ArrayList<>(128);

long state = 0;

ArrayList acklist = new ArrayList<>(128);

//long ackblock = 0;

//long ackcount = 0;

long rx_srtt = 0;

long rx_rttval = 0;

long rx_rto = IKCP_RTO_DEF;

long rx_minrto = IKCP_RTO_MIN;

long current = 0;

long interval = IKCP_INTERVAL;

long ts_flush = IKCP_INTERVAL;

long nodelay = 0;

long updated = 0;

long logmask = 0;

long ssthresh = IKCP_THRESH_INIT;

long fastresend = 0;

long nocwnd = 0;

long xmit = 0;

long dead_link = IKCP_DEADLINK;

//long output = NULL;

//long writelog = NULL;

public KCP(long conv_) {

conv = conv_;

}

//---------------------------------------------------------------------

// user/upper level recv: returns size, returns below zero for EAGAIN

//---------------------------------------------------------------------

// 将接收队列中的数据传递给上层引用

public int Recv(byte[] buffer) {

if (0 == nrcv_que.size()) {

return -1;

}

int peekSize = PeekSize();

if (0 > peekSize) {

return -2;

}

if (peekSize > buffer.length) {

return -3;

}

boolean recover = false;

if (nrcv_que.size() >= rcv_wnd) {

recover = true;

}

// merge fragment.

int count = 0;

int n = 0;

for (Segment seg : nrcv_que) {

System.arraycopy(seg.data, 0, buffer, n, seg.data.length);

n += seg.data.length;

count++;

if (0 == seg.frg) {

break;

}

}

if (0 < count) {

slice(nrcv_que, count, nrcv_que.size());

}

// move available data from rcv_buf -> nrcv_que

count = 0;

for (Segment seg : nrcv_buf) {

if (seg.sn == rcv_nxt && nrcv_que.size() < rcv_wnd) {

nrcv_que.add(seg);

rcv_nxt++;

count++;

} else {

break;

}

}

if (0 < count) {

slice(nrcv_buf, count, nrcv_buf.size());

}

// fast recover

if (nrcv_que.size() < rcv_wnd && recover) {

// ready to send back IKCP_CMD_WINS in ikcp_flush

// tell remote my window size

probe |= IKCP_ASK_TELL;

}

return n;

}

//---------------------------------------------------------------------

// peek data size

//---------------------------------------------------------------------

// check the size of next message in the recv queue

// 计算接收队列中有多少可用的数据

public int PeekSize() {

if (0 == nrcv_que.size()) {

return -1;

}

Segment seq = nrcv_que.get(0);

if (0 == seq.frg) {

return seq.data.length;

}

if (nrcv_que.size() < seq.frg + 1) {

return -1;

}

int length = 0;

for (Segment item : nrcv_que) {

length += item.data.length;

if (0 == item.frg) {

break;

}

}

return length;

}

//---------------------------------------------------------------------

// user/upper level send, returns below zero for error

//---------------------------------------------------------------------

// 上层要发送的数据丢给发送队列,发送队列会根据mtu大小分片

public int Send(byte[] buffer) {

if (0 == buffer.length) {

return -1;

}

int count;

// 根据mss大小分片

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;

}

int offset = 0;

// 分片后加入到发送队列

int length = buffer.length;

for (int i = 0; i < count; i++) {

int size = (int) (length > mss ? mss : length);

Segment seg = new Segment(size);

System.arraycopy(buffer, offset, seg.data, 0, size);

offset += size;

seg.frg = count - i - 1;

nsnd_que.add(seg);

length -= size;

}

return 0;

}

//---------------------------------------------------------------------

// parse ack

//---------------------------------------------------------------------

void update_ack(int rtt) {

if (0 == rx_srtt) {

rx_srtt = rtt;

rx_rttval = rtt / 2;

} else {

int delta = (int) (rtt - rx_srtt);

if (0 > delta) {

delta = -delta;

}

rx_rttval = (3 * rx_rttval + delta) / 4;

rx_srtt = (7 * rx_srtt + rtt) / 8;

if (rx_srtt < 1) {

rx_srtt = 1;

}

}

int rto = (int) (rx_srtt + _imax_(1, 4 * rx_rttval));

rx_rto = _ibound_(rx_minrto, rto, IKCP_RTO_MAX);

}

// 计算本地真实snd_una

void shrink_buf() {

if (nsnd_buf.size() > 0) {

snd_una = nsnd_buf.get(0).sn;

} else {

snd_una = snd_nxt;

}

}

// 对端返回的ack, 确认发送成功时,对应包从发送缓存中移除

void parse_ack(long sn) {

if (_itimediff(sn, snd_una) < 0 || _itimediff(sn, snd_nxt) >= 0) {

return;

}

int index = 0;

for (Segment seg : nsnd_buf) {

if (_itimediff(sn, seg.sn) < 0) {

break;

}

// 原版ikcp_parse_fastack&ikcp_parse_ack逻辑重复

seg.fastack++;

if (sn == seg.sn) {

nsnd_buf.remove(index);

break;

}

index++;

}

}

// 通过对端传回的una将已经确认发送成功包从发送缓存中移除

void parse_una(long una) {

int count = 0;

for (Segment seg : nsnd_buf) {

if (_itimediff(una, seg.sn) > 0) {

count++;

} else {

break;

}

}

if (0 < count) {

slice(nsnd_buf, count, nsnd_buf.size());

}

}

//---------------------------------------------------------------------

// ack append

//---------------------------------------------------------------------

// 收数据包后需要给对端回ack,flush时发送出去

void ack_push(long sn, long ts) {

// c原版实现中按*2扩大容量

acklist.add(sn);

acklist.add(ts);

}

//---------------------------------------------------------------------

// parse data

//---------------------------------------------------------------------

// 用户数据包解析

void parse_data(Segment newseg) {

long sn = newseg.sn;

boolean repeat = false;

if (_itimediff(sn, rcv_nxt + rcv_wnd) >= 0 || _itimediff(sn, rcv_nxt) < 0) {

return;

}

int n = nrcv_buf.size() - 1;

int after_idx = -1;

// 判断是否是重复包,并且计算插入位置

for (int i = n; i >= 0; i--) {

Segment seg = nrcv_buf.get(i);

if (seg.sn == sn) {

repeat = true;

break;

}

if (_itimediff(sn, seg.sn) > 0) {

after_idx = i;

break;

}

}

// 如果不是重复包,则插入

if (!repeat) {

if (after_idx == -1) {

nrcv_buf.add(0, newseg);

} else {

nrcv_buf.add(after_idx + 1, newseg);

}

}

// move available data from nrcv_buf -> nrcv_que

// 将连续包加入到接收队列

int count = 0;

for (Segment seg : nrcv_buf) {

if (seg.sn == rcv_nxt && nrcv_que.size() < rcv_wnd) {

nrcv_que.add(seg);

rcv_nxt++;

count++;

} else {

break;

}

}

// 从接收缓存中移除

if (0 < count) {

slice(nrcv_buf, count, nrcv_buf.size());

}

}

// when you received a low level packet (eg. UDP packet), call it

//---------------------------------------------------------------------

// input data

//---------------------------------------------------------------------

// 底层收包后调用,再由上层通过Recv获得处理后的数据

public int Input(byte[] data) {

long s_una = snd_una;

if (data.length < IKCP_OVERHEAD) {

return 0;

}

int offset = 0;

while (true) {

long ts, sn, length, una, conv_;

int wnd;

byte cmd, frg;

if (data.length - offset < IKCP_OVERHEAD) {

break;

}

conv_ = ikcp_decode32u(data, offset);

offset += 4;

if (conv != conv_) {

return -1;

}

cmd = ikcp_decode8u(data, offset);

offset += 1;

frg = ikcp_decode8u(data, offset);

offset += 1;

wnd = ikcp_decode16u(data, offset);

offset += 2;

ts = ikcp_decode32u(data, offset);

offset += 4;

sn = ikcp_decode32u(data, offset);

offset += 4;

una = ikcp_decode32u(data, offset);

offset += 4;

length = ikcp_decode32u(data, offset);

offset += 4;

if (data.length - offset < length) {

return -2;

}

if (cmd != IKCP_CMD_PUSH && cmd != IKCP_CMD_ACK && cmd != IKCP_CMD_WASK && cmd != IKCP_CMD_WINS) {

return -3;

}

rmt_wnd = (long) 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) {

Segment seg = new Segment((int) length);

seg.conv = conv_;

seg.cmd = cmd;

seg.frg = frg;

seg.wnd = wnd;

seg.ts = ts;

seg.sn = sn;

seg.una = una;

if (length > 0) {

System.arraycopy(data, offset, seg.data, 0, (int) 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;

} 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) {

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

}

// 接收窗口可用大小

int wnd_unused() {

if (nrcv_que.size() < rcv_wnd) {

return (int) (int) rcv_wnd - nrcv_que.size();

}

return 0;

}

//---------------------------------------------------------------------

// ikcp_flush

//---------------------------------------------------------------------

void flush() {

long current_ = current;

byte[] buffer_ = buffer;

int change = 0;

int lost = 0;

// 'ikcp_update' haven't been called.

if (0 == updated) {

return;

}

Segment seg = new Segment(0);

seg.conv = conv;

seg.cmd = IKCP_CMD_ACK;

seg.wnd = (long) wnd_unused();

seg.una = rcv_nxt;

// flush acknowledges

// 将acklist中的ack发送出去

int count = acklist.size() / 2;

int offset = 0;

for (int i = 0; i < count; i++) {

if (offset + IKCP_OVERHEAD > mtu) {

output(buffer, offset);

offset = 0;

}

// ikcp_ack_get

seg.sn = acklist.get(i * 2 + 0);

seg.ts = acklist.get(i * 2 + 1);

offset += seg.encode(buffer, offset);

}

acklist.clear();

// probe window size (if remote window size equals zero)

// rmt_wnd=0时,判断是否需要请求对端接收窗口

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 > mtu) {

output(buffer, offset);

offset = 0;

}

offset += seg.encode(buffer, offset);

}

// flush window probing commands(c#)

// 告诉对端自己的接收窗口

if ((probe & IKCP_ASK_TELL) != 0) {

seg.cmd = IKCP_CMD_WINS;

if (offset + IKCP_OVERHEAD > mtu) {

output(buffer, offset);

offset = 0;

}

offset += seg.encode(buffer, offset);

}

probe = 0;

// calculate window size

long cwnd_ = _imin_(snd_wnd, rmt_wnd);

// 如果采用拥塞控制

if (0 == nocwnd) {

cwnd_ = _imin_(cwnd, cwnd_);

}

count = 0;

// move data from snd_queue to snd_buf

for (Segment nsnd_que1 : nsnd_que) {

if (_itimediff(snd_nxt, snd_una + cwnd_) >= 0) {

break;

}

Segment newseg = nsnd_que1;

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;

nsnd_buf.add(newseg);

snd_nxt++;

count++;

}

if (0 < count) {

slice(nsnd_que, count, nsnd_que.size());

}

// calculate resent

long resent = (fastresend > 0) ? fastresend : 0xffffffff;

long rtomin = (nodelay == 0) ? (rx_rto >> 3) : 0;

// flush data segments

for (Segment segment : nsnd_buf) {

boolean needsend = false;

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;

int need = IKCP_OVERHEAD + segment.data.length;

if (offset + need >= mtu) {

output(buffer, offset);

offset = 0;

}

offset += segment.encode(buffer, offset);

if (segment.data.length > 0) {

System.arraycopy(segment.data, 0, buffer, offset, segment.data.length);

offset += segment.data.length;

}

if (segment.xmit >= dead_link) {

state = -1; // state = 0(c#)

}

}

}

// flash remain segments

if (offset > 0) {

output(buffer, offset);

}

// update ssthresh

// 拥塞避免

if (change != 0) {

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

}

}

//---------------------------------------------------------------------

// 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(long current_) {

current = current_;

// 首次调用Update

if (0 == updated) {

updated = 1;

ts_flush = current;

}

// 两次更新间隔

int slap = _itimediff(current, ts_flush);

// interval设置过大或者Update调用间隔太久

if (slap >= 10000 || slap < -10000) {

ts_flush = current;

slap = 0;

}

// flush同时设置下一次更新时间

if (slap >= 0) {

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 long Check(long current_) {

long ts_flush_ = ts_flush;

long tm_flush = 0x7fffffff;

long tm_packet = 0x7fffffff;

long minimal;

if (0 == updated) {

return current_;

}

if (_itimediff(current_, ts_flush_) >= 10000 || _itimediff(current_, ts_flush_) < -10000) {

ts_flush_ = current_;

}

if (_itimediff(current_, ts_flush_) >= 0) {

return current_;

}

tm_flush = _itimediff(ts_flush_, current_);

for (Segment seg : nsnd_buf) {

int diff = _itimediff(seg.resendts, current_);

if (diff <= 0) {

return current_;

}

if (diff < tm_packet) {

tm_packet = diff;

}

}

minimal = tm_packet < tm_flush ? tm_packet : tm_flush;

if (minimal >= interval) {

minimal = interval;

}

return current_ + minimal;

}

// change MTU size, default is 1400

public int SetMtu(int mtu_) {

if (mtu_ < 50 || mtu_ < (int) IKCP_OVERHEAD) {

return -1;

}

byte[] buffer_ = new byte[(mtu_ + IKCP_OVERHEAD) * 3];

if (null == buffer_) {

return -2;

}

mtu = (long) mtu_;

mss = mtu - IKCP_OVERHEAD;

buffer = buffer_;

return 0;

}

public int Interval(int interval_) {

if (interval_ > 5000) {

interval_ = 5000;

} else if (interval_ < 10) {

interval_ = 10;

}

interval = (long) 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 = 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 = 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 = (long) sndwnd;

}

if (rcvwnd > 0) {

rcv_wnd = (long) rcvwnd;

}

return 0;

}

// get how many packet is waiting to be sent

public int WaitSnd() {

return nsnd_buf.size() + nsnd_que.size();

}

}

一键复制

编辑

Web IDE

原始数据

按行查看

历史

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值