From d13de3b19d99ed09561baa349071e7c57cad9ec7 Mon Sep 17 00:00:00 2001 From: skywind3000 Date: Thu, 14 May 2026 11:34:05 +0800 Subject: [PATCH] congestion control: basically finish --- ikcp.c | 114 +++++++++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 90 insertions(+), 24 deletions(-) diff --git a/ikcp.c b/ikcp.c index 6f23c70..b455317 100644 --- a/ikcp.c +++ b/ikcp.c @@ -973,14 +973,22 @@ void ikcp_flush(ikcpcb *kcp) int count, size, i; IUINT32 resent, cwnd; IUINT32 rtomin; + IUINT32 prior_cwnd; + IUINT32 eff_cwnd, cur_inflight; struct IQUEUEHEAD *p; int change = 0; int lost = 0; IKCPSEG seg; - // 'ikcp_update' haven't been called. + // 'ikcp_update' hasn't been called yet. if (kcp->updated == 0) return; + if (kcp->ccops && kcp->ccops->on_tick) { + kcp->ccops->on_tick(kcp); + } + + prior_cwnd = kcp->cwnd; + seg.conv = kcp->conv; seg.cmd = IKCP_CMD_ACK; seg.frg = 0; @@ -1052,7 +1060,7 @@ void ikcp_flush(ikcpcb *kcp) // calculate window size cwnd = _imin_(kcp->snd_wnd, kcp->rmt_wnd); - if (kcp->nocwnd == 0) cwnd = _imin_(kcp->cwnd, cwnd); + if (kcp->ccops != NULL || kcp->nocwnd == 0) cwnd = _imin_(kcp->cwnd, cwnd); // move data from snd_queue to snd_buf while (_itimediff(kcp->snd_nxt, kcp->snd_una + cwnd) < 0) { @@ -1076,6 +1084,22 @@ void ikcp_flush(ikcpcb *kcp) newseg->rto = kcp->rx_rto; newseg->fastack = 0; newseg->xmit = 0; + + if (kcp->ccops && kcp->ccops->on_pkt_sent) { + kcp->ccops->on_pkt_sent(kcp, newseg->sn, current, newseg->len, kcp->nsnd_buf - 1); + } + } + + // check on_app_limited + if (kcp->ccops && kcp->ccops->on_app_limited) { + if (iqueue_is_empty(&kcp->snd_queue)) { + eff_cwnd = _imin_(kcp->snd_wnd, kcp->rmt_wnd); + eff_cwnd = _imin_(kcp->cwnd, eff_cwnd); + cur_inflight = kcp->nsnd_buf; + if (cur_inflight < eff_cwnd) { + kcp->ccops->on_app_limited(kcp, cur_inflight); + } + } } // calculate resent @@ -1144,7 +1168,7 @@ void ikcp_flush(ikcpcb *kcp) } } - // flash remain segments + // flash remaining segments size = (int)(ptr - buffer); if (size > 0) { ikcp_output(kcp, buffer, size); @@ -1152,20 +1176,30 @@ void ikcp_flush(ikcpcb *kcp) // update ssthresh if (change) { - IUINT32 inflight = kcp->snd_nxt - kcp->snd_una; - kcp->ssthresh = inflight / 2; - if (kcp->ssthresh < IKCP_THRESH_MIN) - kcp->ssthresh = IKCP_THRESH_MIN; - kcp->cwnd = kcp->ssthresh + resent; - kcp->incr = kcp->cwnd * kcp->mss; + if (kcp->ccops && kcp->ccops->on_fast_retransmit) { + kcp->ccops->on_fast_retransmit(kcp, (IUINT32)change, kcp->nsnd_buf, prior_cwnd); + } + else { + IUINT32 inflight = kcp->snd_nxt - kcp->snd_una; + kcp->ssthresh = inflight / 2; + if (kcp->ssthresh < IKCP_THRESH_MIN) + kcp->ssthresh = IKCP_THRESH_MIN; + kcp->cwnd = kcp->ssthresh + resent; + kcp->incr = kcp->cwnd * kcp->mss; + } } if (lost) { - kcp->ssthresh = cwnd / 2; - if (kcp->ssthresh < IKCP_THRESH_MIN) - kcp->ssthresh = IKCP_THRESH_MIN; - kcp->cwnd = 1; - kcp->incr = kcp->mss; + if (kcp->ccops && kcp->ccops->on_timeout) { + kcp->ccops->on_timeout(kcp, prior_cwnd); + } + else { + kcp->ssthresh = cwnd / 2; + if (kcp->ssthresh < IKCP_THRESH_MIN) + kcp->ssthresh = IKCP_THRESH_MIN; + kcp->cwnd = 1; + kcp->incr = kcp->mss; + } } if (kcp->cwnd < 1) { @@ -1176,9 +1210,9 @@ void ikcp_flush(ikcpcb *kcp) //--------------------------------------------------------------------- -// 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. +// update state (call it repeatedly, every 10ms-100ms), or you can ask +// ikcp_check when to call it again (if no ikcp_input/_send calls occur). +// 'current' - current timestamp in milliseconds. //--------------------------------------------------------------------- void ikcp_update(ikcpcb *kcp, IUINT32 current) { @@ -1209,13 +1243,13 @@ void ikcp_update(ikcpcb *kcp, IUINT32 current) //--------------------------------------------------------------------- -// 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) +// Determines when you should invoke ikcp_update next: +// returns the timestamp (in milliseconds) at which you should call +// ikcp_update, assuming no ikcp_input/_send calls occur in between. +// You can call ikcp_update at that time instead of calling it repeatedly. +// Important for reducing unnecessary ikcp_update invocations. Use it to +// schedule ikcp_update (e.g., implementing an epoll-like mechanism, +// or optimizing ikcp_update when handling massive kcp connections). //--------------------------------------------------------------------- IUINT32 ikcp_check(const ikcpcb *kcp, IUINT32 current) { @@ -1334,3 +1368,35 @@ IUINT32 ikcp_getconv(const void *ptr) } +//--------------------------------------------------------------------- +// install congestion control +//--------------------------------------------------------------------- +int ikcp_setcc(ikcpcb *kcp, const struct IKCPOPS *ops) +{ + assert(kcp); + if (kcp->ccops && kcp->ccops->release) { + kcp->ccops->release(kcp); + } + kcp->congest = NULL; + kcp->ccops = ops; + if (ops) { + if (ops->init) { + if (ops->init(kcp) < 0) { + kcp->ccops = NULL; + kcp->congest = NULL; + if (kcp->cwnd < 1) kcp->cwnd = 1; + kcp->incr = kcp->cwnd * kcp->mss; + return -1; + } + } + } + else { + if (kcp->cwnd < 1) kcp->cwnd = 1; + kcp->incr = kcp->cwnd * kcp->mss; + if (kcp->incr < kcp->mss) kcp->incr = kcp->mss; + } + return 0; +} + + +