congestion control: basically finish

This commit is contained in:
skywind3000
2026-05-14 11:34:05 +08:00
parent 8b32a64326
commit d13de3b19d

114
ikcp.c
View File

@@ -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;
}