diff --git a/src/etcp_connections.c b/src/etcp_connections.c index 6474294e..e8f5d84e 100644 --- a/src/etcp_connections.c +++ b/src/etcp_connections.c @@ -239,15 +239,16 @@ static void etcp_link_send_keepalive(struct ETCP_LINK* link) { } dgram->link = link; - dgram->data_len = 0; // Empty packet - only timestamp in header + dgram->data[0] = ETCP_KEEPALIVE; + dgram->data[1] = link->ka_period_ms & 0xFF; + dgram->data[2] = link->ka_period_ms >> 8; + dgram->data_len = 3; dgram->noencrypt_len = 0; dgram->timestamp = get_current_timestamp(); - - DEBUG_DEBUG(DEBUG_CATEGORY_KEEPALIVE, "[%s] Sending keepalive on link %p (local_id=%d)", - link->etcp->log_name, link, link->local_link_id); + dgram->flag_up = link->recv_keepalive; link->keepalive_sent_count++; - + etcp_encrypt_send(dgram); u_free(dgram); } @@ -320,17 +321,21 @@ static void keepalive_timer_cb(void* arg) { } } - // Send keepalive only if no packets were sent since last tick - if (!link->pkt_sent_since_keepalive) { - if (link->is_server) { - if (link->recv_keepalive) etcp_link_send_keepalive(link);// сервер прекращает слать keepalive если линк потерян (ждём keepalive клиента) - } - else etcp_link_send_keepalive(link); - } + // Adaptive keepalive period + if (link->pkt_sent_since_keepalive) + link->ka_period_ms = KA_PERIOD_MIN_MS; // data flowing → reset to 200ms + else { + uint32_t next = (uint32_t)link->ka_period_ms * 105 / 100 + 1; + link->ka_period_ms = next > KA_PERIOD_MAX_MS ? KA_PERIOD_MAX_MS : (uint16_t)next; + } link->pkt_sent_since_keepalive = 0; + // Send keepalive (server stops if link lost, client always sends) + if (!link->is_server || link->recv_keepalive) + etcp_link_send_keepalive(link); + restart_timer: - link->keepalive_timer = uasync_set_timeout(link->etcp->instance->ua, link->keepalive_interval * 10, link, keepalive_timer_cb, "link_keepalive"); + link->keepalive_timer = uasync_set_timeout(link->etcp->instance->ua, link->ka_period_ms * 10, link, keepalive_timer_cb, "link_keepalive"); } static uint32_t sockaddr_hash(struct sockaddr_storage* addr) { @@ -781,6 +786,7 @@ struct ETCP_LINK* etcp_link_new(struct ETCP_CONN* etcp, struct ETCP_SOCKET* conn if (link->keepalive_interval < 10) link->keepalive_interval = 10; link->keepalive_sent_count = 0; link->keepalive_recv_count = 0; + link->ka_period_ms = KA_PERIOD_MIN_MS; link->inflight_lim_bytes = link->mtu * 4; // BBR init_cwnd (~4 packets) link->bandwidth = 10000; // начальная оценка 10 Mbps для шейпера link->burst_id = 0; @@ -1589,17 +1595,20 @@ process_decrypted: // Count decrypted bytes link->total_decrypted += pkt->data_len; - - // Count received keepalive packets (empty packets with no payload) - if (pkt->data_len == 0) { - link->keepalive_recv_count++; - DEBUG_DEBUG(DEBUG_CATEGORY_KEEPALIVE, "[%s] Received keepalive on link %p (local_id=%d)", - link->etcp->log_name, link, link->local_link_id); - } - + size_t offset = 0; uint8_t code = pkt->data[offset++]; + if (code == ETCP_KEEPALIVE) { + if (pkt->data_len >= 3) { + uint16_t peer_period = pkt->data[1] | ((uint16_t)pkt->data[2] << 8); + link->keepalive_timeout = (uint32_t)peer_period * KA_TIMEOUT_MULT; + } + link->keepalive_recv_count++; + memory_pool_free(e_sock->instance->pkt_pool, pkt); + return; // KA handled, nothing more to process + } + if (code == ETCP_INIT_RESPONSE || code == ETCP_INIT_RESPONSE_NOINIT) { if (pkt_len < 22) { errorcode = 46; DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "INIT_RESPONSE too short: pkt_len=%zu", pkt_len); goto ec_fr; } // ETCP_INIT_RESPONSE (0x03) - reset entire ETCP_CONN diff --git a/src/etcp_connections.h b/src/etcp_connections.h index e62de545..4bb68cf6 100644 --- a/src/etcp_connections.h +++ b/src/etcp_connections.h @@ -26,6 +26,12 @@ #define ETCP_INIT_RESPONSE_NOINIT 0x05 #define ETCP_PING 0x06 #define ETCP_PONG 0x07 +#define ETCP_KEEPALIVE 0x08 + +/* Адаптивный keepalive */ +#define KA_PERIOD_MIN_MS 200 +#define KA_PERIOD_MAX_MS 10000 +#define KA_TIMEOUT_MULT 10 /* timeout = period * mult */ #pragma pack(push, 1) @@ -194,6 +200,7 @@ struct ETCP_LINK { // Keepalive state void* keepalive_timer; // Таймер для отправки keepalive пакетов uint32_t keepalive_timeout; // таймаут (ms) + uint16_t ka_period_ms; // адаптивный период отправки keepalive (200→10000->200) uint8_t pkt_sent_since_keepalive; // Флаг: был ли отправлен пакет с последнего keepalive тика uint32_t keepalive_sent_count; // Счётчик отправленных keepalive uint32_t keepalive_recv_count; // Счётчик полученных keepalive