transport: keepalive duty-cycle тайминг (active_remaining+sleep_duration) + серверная фаза троттлинга
This commit is contained in:
1 parent
0a1ba79d6f
commit
9f89fd0f7a
6 files changed
+117
-68
No files matched your search
@@ -400,10 +400,11 @@ void etcp_connection_close(struct ETCP_CONN* etcp) {
|
||||
etcp->state = 2; // deleted — blocks ref_take and etcp_send immediately
|
||||
if (etcp->links_up != 0) etcp->links_up = 0;
|
||||
|
||||
if (etcp->peer_sleeping) {
|
||||
etcp->peer_sleeping = 0;
|
||||
if (etcp->peer_sleep_phase) {
|
||||
etcp->peer_sleep_phase = 0;
|
||||
utun_fire_peer_sleep_cbk(etcp->instance, etcp->peer_node_id, 0);
|
||||
}
|
||||
if (etcp->peer_phase_timer) { uasync_cancel_timeout(etcp->instance->ua, etcp->peer_phase_timer); etcp->peer_phase_timer = NULL; }
|
||||
|
||||
etcp_fire_conn_status(etcp, ETCP_CONN_STATUS_DELETE);
|
||||
DEBUG_DEBUG(DEBUG_CATEGORY_CONNECTION, "[%s] close: state=2 conn=%p peer=%016llx (DELETE fired)", etcp->log_name, (void*)etcp, (unsigned long long)etcp->peer_node_id);
|
||||
|
||||
@@ -125,7 +125,10 @@ struct ETCP_CONN {
|
||||
// Peer info
|
||||
uint64_t peer_node_id; // Peer node ID
|
||||
uint8_t peer_ed25519_pubkey[SC_PUBKEY_SIZE]; // Ed25519 pubkey пира (из INIT)
|
||||
uint8_t peer_sleeping; // 1 = пир в спячке (keepalive sleep-анонс), серверная сторона
|
||||
uint8_t peer_sleep_phase; // 1 = пир в SLEEP-фазе (троттлить), 0 = ACTIVE-окно (обмен)
|
||||
uint16_t peer_active_remaining; // до SLEEP-фазы (x0.1с), из keepalive
|
||||
uint16_t peer_sleep_duration; // длительность SLEEP-фазы (x0.1с), из keepalive
|
||||
void* peer_phase_timer; // таймер смены фазы троттлинга
|
||||
|
||||
// ============ Processing incoming data to be sent by ETCP
|
||||
struct ll_queue* input_queue; // Incoming packets to send (rx_pool -> ETCP_FRAGMENT)
|
||||
|
||||
@@ -1008,8 +1008,8 @@ struct ETCP_LINK* etcp_link_new(struct ETCP_CONN* etcp, struct ETCP_SOCKET* conn
|
||||
link->ka_sleep_period_ms = 0;
|
||||
link->ka_sleeping = 0;
|
||||
link->ka_sleep_acked = 0;
|
||||
link->ka_sleep_time = 0;
|
||||
link->ka_sleep_deadline_tb = 0;
|
||||
link->ka_active_remaining = 0;
|
||||
link->ka_sleep_duration = 0;
|
||||
link->ka_sleep_announce_timer = NULL;
|
||||
link->ka_sleep_announce_end_tb = 0;
|
||||
/* inflight_lim_bytes set above */
|
||||
|
||||
@@ -285,8 +285,8 @@ struct ETCP_LINK {
|
||||
uint16_t ka_sleep_period_ms; // период keepalive в спячке (мс; настройка клиента / ka_period_ms пира)
|
||||
uint8_t ka_sleeping; // 1 = линк в режиме спячки
|
||||
uint8_t ka_sleep_acked; // клиент: получен ack спячки от сервера
|
||||
uint16_t ka_sleep_time; // дедлайн пробуждения (x0.1с), текущий
|
||||
uint64_t ka_sleep_deadline_tb; // абсолютный дедлайн пробуждения (timebase 0.1мс)
|
||||
uint16_t ka_active_remaining; // сколько до SLEEP-фазы (x0.1с), 0 = уже спим
|
||||
uint16_t ka_sleep_duration; // длительность SLEEP-фазы (x0.1с)
|
||||
void* ka_sleep_announce_timer; // таймер ретрая анонса спячки (клиент)
|
||||
uint64_t ka_sleep_announce_end_tb; // конец окна ретрая анонса (4с)
|
||||
uint8_t pkt_sent_since_keepalive; // Флаг: был ли отправлен пакет с последнего keepalive тика
|
||||
|
||||
@@ -34,7 +34,7 @@ uint16_t negotiate_keepalive(uint8_t my_type, uint16_t my_ka,
|
||||
return result;
|
||||
}
|
||||
|
||||
// Send keepalive packet: code(0x08) + flags + ka_period_ms + [sleep_time]
|
||||
// Send keepalive packet: code(0x08) + flags + ka_period_ms + [active_remaining + sleep_duration]
|
||||
void etcp_link_send_keepalive(struct ETCP_LINK* link) {
|
||||
DEBUG_TRACE(DEBUG_CATEGORY_KEEPALIVE, "");
|
||||
if (!link || !link->etcp || !link->etcp->instance) return;
|
||||
@@ -51,9 +51,11 @@ void etcp_link_send_keepalive(struct ETCP_LINK* link) {
|
||||
dgram->data[2] = link->ka_period_ms & 0xFF;
|
||||
dgram->data[3] = link->ka_period_ms >> 8;
|
||||
if (link->ka_sleeping) {
|
||||
dgram->data[4] = link->ka_sleep_time & 0xFF;
|
||||
dgram->data[5] = link->ka_sleep_time >> 8;
|
||||
dgram->data_len = 6;
|
||||
dgram->data[4] = link->ka_active_remaining & 0xFF;
|
||||
dgram->data[5] = link->ka_active_remaining >> 8;
|
||||
dgram->data[6] = link->ka_sleep_duration & 0xFF;
|
||||
dgram->data[7] = link->ka_sleep_duration >> 8;
|
||||
dgram->data_len = 8;
|
||||
} else {
|
||||
dgram->data_len = 4;
|
||||
}
|
||||
@@ -173,20 +175,6 @@ static void keepalive_timer_cb(void* arg) {
|
||||
}
|
||||
}
|
||||
|
||||
// Сервер: дедлайн спячки пира вышел — пир должен был проснуться/переанонсировать,
|
||||
// выходим из спячки в обычный режим (базовый период, probe штатным keepalive).
|
||||
if (link->is_server && link->ka_sleeping && now >= link->ka_sleep_deadline_tb) {
|
||||
link->ka_sleeping = 0;
|
||||
link->ka_period_ms = link->keepalive_interval;
|
||||
link->keepalive_timeout = (uint32_t)link->keepalive_interval * KA_TIMEOUT_MULT;
|
||||
if (link->etcp && link->etcp->peer_sleeping) {
|
||||
link->etcp->peer_sleeping = 0;
|
||||
utun_fire_peer_sleep_cbk(link->etcp->instance, link->etcp->peer_node_id, 0);
|
||||
}
|
||||
DEBUG_INFO(DEBUG_CATEGORY_KEEPALIVE, "[%s] server: sleep deadline expired, back to normal (interval=%dms)",
|
||||
link->etcp->log_name, link->keepalive_interval);
|
||||
}
|
||||
|
||||
// Период: спячка — фиксированный sleep-период; normal — адаптивный от базового.
|
||||
if (link->ka_sleeping)
|
||||
link->ka_period_ms = link->ka_sleep_period_ms;
|
||||
@@ -223,22 +211,32 @@ static uint16_t ka_sleep_period_ms(struct UTUN_INSTANCE* inst) {
|
||||
#endif
|
||||
}
|
||||
|
||||
// Дедлайн пробуждения (x0.1с): остаток SLEEP либо остаток ACTIVE + полная SLEEP.
|
||||
static uint16_t ka_wake_deadline_01s(void) {
|
||||
// Сколько осталось до SLEEP-фазы (x0.1с): 0 = уже спим.
|
||||
static uint16_t ka_active_remaining_01s(void) {
|
||||
#ifdef UTUN_HAVE_STANDBY
|
||||
int deadline_tb = standby_get_wake_deadline_tb();
|
||||
uint32_t v = deadline_tb > 0 ? (uint32_t)(deadline_tb / 1000) : 0;
|
||||
uint32_t v = (uint32_t)(standby_get_active_remaining_tb() / 1000);
|
||||
return v > 65535 ? 65535 : (uint16_t)v;
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
// Отправить keepalive спячки (обновить дедлайн) + при необходимости заармить ретрай.
|
||||
// Длительность SLEEP-фазы (x0.1с): в SLEEP — оставшийся сон, в ACTIVE — полная длительность.
|
||||
static uint16_t ka_sleep_duration_01s(void) {
|
||||
#ifdef UTUN_HAVE_STANDBY
|
||||
int s = standby_get_sleep_tb();
|
||||
uint32_t v = s > 0 ? (uint32_t)(s / 1000)
|
||||
: (uint32_t)(standby_get_sleep_duration_tb() / 1000);
|
||||
return v > 65535 ? 65535 : (uint16_t)v;
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
// Отправить keepalive спячки (обновить поля тайминга) + при необходимости заармить ретрай.
|
||||
static void ka_send_sleep_keepalive(struct ETCP_LINK* link) {
|
||||
uint16_t sleep_time = ka_wake_deadline_01s();
|
||||
if (sleep_time == 0) sleep_time = link->ka_sleep_period_ms / 100;
|
||||
link->ka_sleep_time = sleep_time;
|
||||
link->ka_active_remaining = ka_active_remaining_01s();
|
||||
link->ka_sleep_duration = ka_sleep_duration_01s();
|
||||
etcp_link_send_keepalive(link);
|
||||
if (!link->ka_sleep_acked && link->ka_sleep_announce_timer == NULL) {
|
||||
link->ka_sleep_announce_timer = uasync_set_timeout(link->etcp->instance->ua,
|
||||
@@ -318,17 +316,58 @@ static void ka_for_each_link(struct UTUN_INSTANCE* inst, ka_link_fn fn) {
|
||||
}
|
||||
|
||||
#ifdef UTUN_HAVE_STANDBY
|
||||
// Фаза duty-cycle: вошли в SLEEP → переанонсировать спячку (каждый цикл).
|
||||
// Клиент: вход в ACTIVE-окно — отправить keepalive с active_remaining>0 (флаг стоит).
|
||||
static void ka_client_active_announce(struct ETCP_LINK* link) {
|
||||
if (!link->ka_sleeping) return;
|
||||
ka_send_sleep_keepalive(link);
|
||||
}
|
||||
|
||||
// Фаза duty-cycle: SLEEP → переанонс спячки; ACTIVE → анонс активности (флаг всегда стоит).
|
||||
static void ka_phase_cb(int sleeping, void* arg) {
|
||||
struct UTUN_INSTANCE* inst = (struct UTUN_INSTANCE*)arg;
|
||||
if (!inst || inst->client_type != CLIENT_TYPE_MOBILE) return;
|
||||
if (!sleeping) return;
|
||||
ka_for_each_link(inst, ka_client_reannounce);
|
||||
DEBUG_INFO(DEBUG_CATEGORY_KEEPALIVE, "keepalive: duty-cycle SLEEP phase, re-announce (node=%016llx)",
|
||||
(unsigned long long)inst->node_id);
|
||||
if (sleeping) {
|
||||
ka_for_each_link(inst, ka_client_reannounce);
|
||||
DEBUG_INFO(DEBUG_CATEGORY_KEEPALIVE, "keepalive: duty-cycle SLEEP phase, re-announce (node=%016llx)",
|
||||
(unsigned long long)inst->node_id);
|
||||
} else {
|
||||
ka_for_each_link(inst, ka_client_active_announce);
|
||||
DEBUG_INFO(DEBUG_CATEGORY_KEEPALIVE, "keepalive: duty-cycle ACTIVE phase, announce (node=%016llx)",
|
||||
(unsigned long long)inst->node_id);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/* ── Сервер: машина фаз троттлинга (по active_remaining/sleep_duration) ── */
|
||||
|
||||
static void ka_server_arm_phase(struct ETCP_CONN* etcp);
|
||||
|
||||
static void ka_server_set_phase(struct ETCP_CONN* etcp, int sleeping) {
|
||||
if (!etcp) return;
|
||||
if (etcp->peer_sleep_phase == (uint8_t)(sleeping ? 1 : 0)) return;
|
||||
etcp->peer_sleep_phase = sleeping ? 1 : 0;
|
||||
utun_fire_peer_sleep_cbk(etcp->instance, etcp->peer_node_id, sleeping);
|
||||
}
|
||||
|
||||
static void ka_server_phase_timer_cb(void* arg) {
|
||||
struct ETCP_CONN* etcp = (struct ETCP_CONN*)arg;
|
||||
etcp->peer_phase_timer = NULL;
|
||||
if (!etcp || etcp->state == 2) return;
|
||||
ka_server_set_phase(etcp, etcp->peer_sleep_phase ? 0 : 1);
|
||||
ka_server_arm_phase(etcp);
|
||||
}
|
||||
|
||||
static void ka_server_arm_phase(struct ETCP_CONN* etcp) {
|
||||
if (!etcp || !etcp->instance) return;
|
||||
uint16_t dur_01s = etcp->peer_sleep_phase ? etcp->peer_sleep_duration : etcp->peer_active_remaining;
|
||||
if (etcp->peer_phase_timer) {
|
||||
uasync_cancel_timeout(etcp->instance->ua, etcp->peer_phase_timer);
|
||||
etcp->peer_phase_timer = NULL;
|
||||
}
|
||||
etcp->peer_phase_timer = uasync_set_timeout(etcp->instance->ua, (int)((uint32_t)dur_01s * 1000),
|
||||
etcp, ka_server_phase_timer_cb, "peer_phase");
|
||||
}
|
||||
|
||||
int etcp_keepalive_on_recv(struct ETCP_SOCKET* e_sock, struct ETCP_DGRAM* pkt,
|
||||
struct ETCP_LINK* link, size_t pkt_len) {
|
||||
(void)pkt_len;
|
||||
@@ -346,21 +385,27 @@ int etcp_keepalive_on_recv(struct ETCP_SOCKET* e_sock, struct ETCP_DGRAM* pkt,
|
||||
uint8_t flags = (pkt->data_len >= 2) ? pkt->data[1] : 0;
|
||||
|
||||
if (flags & KA_FLAG_SLEEP) {
|
||||
uint16_t sleep_time = (pkt->data_len >= 6)
|
||||
uint16_t active_remaining = (pkt->data_len >= 6)
|
||||
? (pkt->data[4] | ((uint16_t)pkt->data[5] << 8)) : 0;
|
||||
uint16_t sleep_duration = (pkt->data_len >= 8)
|
||||
? (pkt->data[6] | ((uint16_t)pkt->data[7] << 8)) : 0;
|
||||
if (link->is_server) {
|
||||
/* Сервер: пир заснул — перейти в спячку и ответить ack-эхо. */
|
||||
/* Сервер: пир в спячке — зеркалим флаг/поля для эхо и ведём фазу троттлинга. */
|
||||
link->ka_sleeping = 1;
|
||||
link->ka_sleep_period_ms = peer_period;
|
||||
link->ka_sleep_time = sleep_time;
|
||||
link->ka_sleep_deadline_tb = get_time_tb() + (uint64_t)sleep_time * 1000;
|
||||
link->ka_active_remaining = active_remaining;
|
||||
link->ka_sleep_duration = sleep_duration;
|
||||
link->ka_period_ms = peer_period;
|
||||
if (link->etcp && !link->etcp->peer_sleeping) {
|
||||
link->etcp->peer_sleeping = 1;
|
||||
utun_fire_peer_sleep_cbk(link->etcp->instance, link->etcp->peer_node_id, 1);
|
||||
if (link->etcp) {
|
||||
link->etcp->peer_active_remaining = active_remaining;
|
||||
link->etcp->peer_sleep_duration = sleep_duration;
|
||||
ka_server_set_phase(link->etcp, active_remaining == 0);
|
||||
ka_server_arm_phase(link->etcp);
|
||||
}
|
||||
DEBUG_INFO(DEBUG_CATEGORY_KEEPALIVE, "[%s] server: peer sleeping (period=%dms deadline=%us), echo ack",
|
||||
link->etcp->log_name, peer_period, (unsigned)(sleep_time / 10));
|
||||
DEBUG_INFO(DEBUG_CATEGORY_KEEPALIVE, "[%s] server: peer sleeping (period=%dms active_rem=%d.%ds sleep_dur=%d.%ds), echo ack",
|
||||
link->etcp->log_name, peer_period,
|
||||
(int)(active_remaining / 10), (int)(active_remaining % 10),
|
||||
(int)(sleep_duration / 10), (int)(sleep_duration % 10));
|
||||
etcp_link_send_keepalive(link);
|
||||
restart_keepalive_timer_ms(link, peer_period);
|
||||
} else {
|
||||
@@ -379,9 +424,12 @@ int etcp_keepalive_on_recv(struct ETCP_SOCKET* e_sock, struct ETCP_DGRAM* pkt,
|
||||
link->ka_sleeping = 0;
|
||||
link->ka_period_ms = (uint16_t)link->keepalive_interval;
|
||||
link->keepalive_timeout = (uint32_t)link->keepalive_interval * KA_TIMEOUT_MULT;
|
||||
if (link->etcp && link->etcp->peer_sleeping) {
|
||||
link->etcp->peer_sleeping = 0;
|
||||
utun_fire_peer_sleep_cbk(link->etcp->instance, link->etcp->peer_node_id, 0);
|
||||
if (link->etcp) {
|
||||
if (link->etcp->peer_phase_timer) {
|
||||
uasync_cancel_timeout(link->etcp->instance->ua, link->etcp->peer_phase_timer);
|
||||
link->etcp->peer_phase_timer = NULL;
|
||||
}
|
||||
ka_server_set_phase(link->etcp, 0);
|
||||
}
|
||||
DEBUG_INFO(DEBUG_CATEGORY_KEEPALIVE, "[%s] server: peer woke up, back to normal (interval=%dms)",
|
||||
link->etcp->log_name, link->keepalive_interval);
|
||||
|
||||
@@ -12,30 +12,27 @@ extern "C" {
|
||||
|
||||
/* ─── Протокол спячки поверх keepalive-пакета (code ETCP_KEEPALIVE = 0x08) ───
|
||||
*
|
||||
* Режим спячки передаётся флагом в самом keepalive (отдельные кодограммы
|
||||
* REQ/RESP не используются). Формат keepalive-пакета:
|
||||
* Режим спячки (standby) передаётся флагом в самом keepalive (персистентный,
|
||||
* стоит весь фон). Тайминг duty-cycle (SLEEP 120с / ACTIVE 2с) — двумя полями:
|
||||
*
|
||||
* data[0] = ETCP_KEEPALIVE (0x08)
|
||||
* data[1] = flags (bit0 = KA_FLAG_SLEEP — спячка)
|
||||
* data[1] = flags (bit0 = KA_FLAG_SLEEP — standby)
|
||||
* data[2..3] = ka_period_ms (LE 16-bit) — период keepalive в спячке, мс
|
||||
* data[4..5] = sleep_time (LE 16-bit, x0.1с) — дедлайн пробуждения (при KA_FLAG_SLEEP)
|
||||
* data[4..5] = active_remaining (LE 16-bit, x0.1с) — до SLEEP-фазы (0 = уже спим)
|
||||
* data[6..7] = sleep_duration (LE 16-bit, x0.1с) — длительность SLEEP-фазы
|
||||
*
|
||||
* Засыпание (клиент, перед входом в каждый цикл спячки duty-cycle):
|
||||
* 1) шлёт keepalive с KA_FLAG_SLEEP + sleep_time. sleep_time = остаток ACTIVE +
|
||||
* полная длительность SLEEP (дедлайн, после которого клиент ТОЧНО проснётся;
|
||||
* может и раньше). В спячке при ретраях корректируется вниз до остатка SLEEP.
|
||||
* 2) ретраит каждые KA_SLEEP_RETRY_MS, пока не получит keepalive с KA_FLAG_SLEEP
|
||||
* (ack сервера); суммарно KA_SLEEP_ANNOUNCE_TIMEOUT_MS, после чего спит anyway.
|
||||
* 3) сервер отвечает ответным keepalive с KA_FLAG_SLEEP (ack) и переводит период
|
||||
* keepalive в спячку (ka_period_ms из пакета).
|
||||
* Клиент в standby шлёт ВСЕ keepalive с KA_FLAG_SLEEP. На входе в SLEEP-фазу —
|
||||
* active_remaining=0, sleep_duration=полная; на входе в ACTIVE-окно —
|
||||
* active_remaining=остаток окна; периодические — active_remaining=0,
|
||||
* sleep_duration=оставшийся сон. Сервер по этим полям ведёт фазу троттлинга
|
||||
* (SLEEP-фаза → тормозим протоколы, ACTIVE-окно → обмен) и эхо-подтверждает
|
||||
* флаг как есть.
|
||||
*
|
||||
* В спячке (включая ACTIVE-фазу duty-cycle) клиент продолжает слать keepalive с
|
||||
* KA_FLAG_SLEEP. Сервер отвечает echo на входящие keepalive, либо, если дедлайн
|
||||
* sleep_time вышел — шлёт серию keepalive с обычным таймаутом, пока не получит
|
||||
* keepalive со sleep_time (клиент переанонсировал) или обычный (клиент проснулся).
|
||||
* Засыпание (клиент): анонс + ретраи KA_SLEEP_RETRY_MS до ack (эхо с флагом),
|
||||
* окно KA_SLEEP_ANNOUNCE_TIMEOUT_MS, после чего спит anyway.
|
||||
*
|
||||
* Пробуждение: только выход в форграунд — клиент шлёт обычный keepalive (без
|
||||
* KA_FLAG_SLEEP), сервер возвращается в обычный адаптивный режим с базовым периодом.
|
||||
* Пробуждение (форграунд): клиент шлёт обычный keepalive (без флага) — сервер
|
||||
* снимает standby и фазу троттлинга, возвращается в обычный режим.
|
||||
*/
|
||||
|
||||
// Флаги keepalive-пакета (data[1])
|
||||
|
||||
Reference in new issue
Block a user