diff --git a/src/transport_layer/etcp.h b/src/transport_layer/etcp.h index fcbf0728..0734f9eb 100644 --- a/src/transport_layer/etcp.h +++ b/src/transport_layer/etcp.h @@ -77,7 +77,8 @@ struct INFLIGHT_PACKET {// выделяется из etcp->inflight_pool uint32_t seq; // packet seq (ID по документации) struct ETCP_LINK* last_link; // Заимствованный курсор выбора следующего пути; очищается перед free линка uint64_t last_timestamp; // Время последней принятой отправки, 0.1мс; только диагностика, не RTO/BBR - uint64_t last_attempt_id; // Последняя принятая транспортом попытка; ненулевой ID отличает retry + uint64_t last_attempt_id; // Внутренний ключ последней принятой попытки (seq<<16)|номер; 0 — отправок не было + uint16_t attempt_count; // Выданные номера этого seq, 1..65535, общие для всех линков; исчерпание меняет эпоху uint32_t send_count; // Статистика отправок с насыщением, без wrap uint32_t retrans_req_count; // Число запросов повтора с насыщением uint32_t active_attempts; // Попытки этого seq, ещё занимающие окна каналов @@ -94,7 +95,7 @@ struct ETCP_FRAGMENT {// выделяется из пула etcp->rx_pool struct ACK_PACKET { struct ll_entry ll; - uint64_t attempt_id; // Индекс очереди конкретного линка + uint64_t attempt_id; // Внутренний индекс (seq<<16)|номер; по сети передаётся только номер u16 uint32_t seq; // Логический фрагмент uint64_t received_at_us; // Полное локальное время, без modulo-wrap задержки }; @@ -161,8 +162,7 @@ struct ETCP_CONN { // IDs and state - uint32_t next_tx_id; // ID для добавления в очередь отправки (с этим id будет добавлен следующий пакет) - uint64_t next_attempt_id; // Не переиспользуется при reset/пересоздании линков; исчерпание закрывает соединение + uint32_t next_tx_id; // Следующий seq, 1..UINT32_MAX; перед повторным использованием нужна новая эпоха uint32_t active_attempts; // Все физические записи, включая резерв send; максимум 2*MAX_INFLIGHT_SIZE uint32_t last_rx_id; // Last received ID uint32_t last_delivered_id; // Last delivered to output_queue diff --git a/src/transport_layer/etcp_connections.h b/src/transport_layer/etcp_connections.h index 675e575c..62321a36 100644 --- a/src/transport_layer/etcp_connections.h +++ b/src/transport_layer/etcp_connections.h @@ -37,8 +37,8 @@ extern "C" { // Wire-MTU кап: пакет на проводе не должен превышать 1500 (согласовано с TUN_MAX_PACKET_SIZE). // PACKET_DATA_SIZE (2048) — это внутренний буфер аллокации/приёма, а не wire-MTU. #define PACKET_DATA_MAX_MTU 1500 -#define ETCP_PAYLOAD_HEADER_SIZE 13 // type + seq:u32 + attempt_id:u64 -#define ETCP_ACK_ENTRY_SIZE 16 // seq:u32 + attempt_id:u64 + receiver_delay_us:u32 +#define ETCP_PAYLOAD_HEADER_SIZE 7 // type + seq:u32 + attempt_no:u16 (LE, 1..65535, общий номер для всех линков seq) +#define ETCP_ACK_ENTRY_SIZE 10 // seq:u32 + attempt_no:u16 + receiver_delay_us:u32; все числа LE #define ETCP_MAX_PAYLOAD_SIZE (PACKET_DATA_SIZE - ETCP_ACK_BASE_SIZE - ETCP_PAYLOAD_HEADER_SIZE) #define ETCP_RFC791_MIN_MTU 576 @@ -257,8 +257,8 @@ struct ETCP_LINK { uint64_t shaper_sub_nanotime; // sub 0-999999 для 0.1 нс - 0.1 мс uint8_t shaper_state; void* shaper_timer; - struct ll_queue* attempts; // Физические попытки, индекс attempt_id:u64; ll.len учитывается в окне - struct ll_queue* ack_q; // ACK только DATA этого линка, индекс attempt_id:u64 + struct ll_queue* attempts; // Физические попытки, внутренний ключ (seq<<16)|attempt_no; ll.len учитывается в окне + struct ll_queue* ack_q; // ACK только DATA этого линка, тот же составной ключ uint8_t ack_reply_pending; // Ответ на запрос till возвращается тем же путём, даже при пустой ACK-очереди void* retrans_timer; // Срок старейшей принятой транспортом попытки этого канала uint64_t retrans_deadline_us; // Уже запланированная проверка: более ранний таймер можно сохранить diff --git a/src/transport_layer/etcp_tx.h b/src/transport_layer/etcp_tx.h index 145a7ad1..96e326ba 100644 --- a/src/transport_layer/etcp_tx.h +++ b/src/transport_layer/etcp_tx.h @@ -14,10 +14,15 @@ struct ETCP_LINK; struct ETCP_DGRAM; struct INFLIGHT_PACKET; -/* Запись из attempt_pool соединения; индекс очереди линка — attempt_id, payload здесь не хранится. */ +/* Внутренний ключ пары seq/номер без padding. Номер 1..65535; wire содержит seq:u32 и номер:u16 отдельно. */ +static inline uint64_t etcp_tx_attempt_id(uint32_t seq, uint16_t number) { + return ((uint64_t)seq << 16) | number; +} + +/* Запись из attempt_pool соединения; индекс очереди линка — составной attempt_id, payload здесь не хранится. */ struct ETCP_TX_ATTEMPT { struct ll_entry ll; // len — DATA-байты, dgram=NULL; одна очередь, владелец — линк - uint64_t attempt_id; // уникален в пределах жизни соединения, 0 не используется + uint64_t attempt_id; // (seq<<16)|номер; пара не повторяется в одной эпохе, младшие 16 бит ненулевые uint32_t seq; // логический фрагмент; может быть уже освобождён после доставки uint64_t sent_at_us; // время принятия транспортом; 0 — пока зарезервирован uint64_t delivered_at_send; // доставленные DATA-байты этого канала на момент отправки @@ -40,12 +45,14 @@ void etcp_tx_link_destroy(struct ETCP_LINK* link); uint32_t etcp_tx_rto_us(const struct ETCP_LINK* link); /* Резервировать окно для packet; NULL — ошибка с логом, packet остаётся в очереди отправки. * Физические записи ограничены MAX_INFLIGHT_SIZE на линк и 2*MAX_INFLIGHT_SIZE на соединение. - * Исчерпание бюджета блокирует выбор DATA до ACK/RTO/закрытия; ACK-очередь линка ограничена MAX_INFLIGHT_SIZE. */ + * Исчерпание бюджета блокирует выбор DATA до ACK/RTO/закрытия; ACK-очередь линка ограничена MAX_INFLIGHT_SIZE. + * Номер общий для всех линков одного seq; выданный номер не возвращается при отказе send. + * После номера 65535 новый prepare сбрасывает поток со сменой эпохи, не оборачивает счётчик. */ struct ETCP_TX_ATTEMPT* etcp_tx_prepare(struct ETCP_LINK* link, struct INFLIGHT_PACKET* packet); /* Завершить синхронный send: success=1 принят транспортом, 0 — снять резерв без сетевой потери. * Вызвать ровно один раз до освобождения dgram и до следующего poll; dgram->attempt обнуляется. */ void etcp_tx_complete(struct ETCP_DGRAM* dgram, int success); -/* Принятый ACK: seq подтверждает логическую доставку; attempt_id обновляет окно/BBR только своего линка. +/* Принятый ACK: seq подтверждает логическую доставку; внутренний attempt_id=(seq<<16)|номер обновляет только свой линк. * Неизвестный старый ID не создаёт измерение и не вычитает окно. UINT32_MAX delay — RTT недостоверен. * peer_rx_bytes/time_us — согласованная точка приёма пути из ACK; 0/0 — неизвестна. * Достоверная пара ограничивает BW темпом приёма, сохраняя границы send/ACK; RTT и доставка от неё не зависят.