You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
125 lines
7.2 KiB
125 lines
7.2 KiB
// tcp_io.h — управление TCP-соединением через uasync + ll_queue |
|
// |
|
// Один tcp_conn = одно TCP-соединение. Две очереди: read_queue (сокет → данные) и |
|
// write_queue (данные → сокет). Обе работают через автозабор ll_queue (deferred). |
|
// |
|
// Пул-аллокация: |
|
// entry_pool — только struct ll_entry (без inline data) |
|
// data_pool — буферы данных чтения/записи (max(entry_data_size, write_chunk_size)) |
|
// Данные и структуры аллоцируются раздельно — не копируются при recv/send. |
|
// |
|
// Чтение: |
|
// read_cb → memory_pool_alloc(read_pool) → recv() прямо в буфер → entry в read_queue |
|
// read_queue автозабор(deferred) → внешний коллбэк (например, отправка в ETCP) |
|
// high_water → пауза чтения (убираем EPOLLIN), low_water/waiter → возобновление |
|
// FIN: если read_queue не пуст → empty_callback откладывает on_fin; иначе сразу |
|
// |
|
// Запись: |
|
// Внешний код: tcp_conn_push_write(data, len) → аллокация через write_pool → entry в write_queue |
|
// write_queue автозабор(deferred): fetch_cb → send() в сокет |
|
// EAGAIN → write_buf (из data_pool) + EPOLLOUT ON → write_cb досылает → resume автозабора |
|
// Всё отправлено → EPOLLOUT OFF + on_flushed (если установлен) |
|
// |
|
// FIN / Close через очередь отправки: |
|
// tcp_conn_push_fin(tc) — ставит FIN в write_queue (dgram=&sentinel, len=0) |
|
// tcp_conn_push_close(tc) — ставит CLOSE в write_queue (dgram=NULL, len=0) |
|
// Оба сигнала обрабатываются после всех предшествующих данных в очереди: |
|
// FIN → shutdown(SHUT_WR), стоп чтения, on_fin_sent |
|
// CLOSE → close сокета, on_closed |
|
// После FIN входящие данные отбрасываются (read_cb → discard). |
|
// После CLOSE сокет закрыт, tc->closed=1, дальнейшая отправка невозможна. |
|
// |
|
// Connect: |
|
// tcp_conn_create регистрирует сокет с read_cb + write_cb в uasync |
|
// После create вызывается connect() (неблокирующий, EINPROGRESS) |
|
// write_cb детектит завершение connect через getsockopt(SO_ERROR) |
|
// getpeername() сразу после create проверяет pre-connected сокеты (socketpair) |
|
// До connect данные копятся в write_queue (автозабор не пытается send на unconnected сокет) |
|
|
|
#ifndef TCP_IO_H |
|
#define TCP_IO_H |
|
|
|
#include "u_async.h" |
|
#include "socket_compat.h" |
|
#include "ll_queue.h" |
|
#include "memory_pool.h" |
|
|
|
struct tcp_conn { |
|
socket_t sock; |
|
struct UASYNC* ua; |
|
void* socket_id; |
|
|
|
struct ll_queue* read_queue; // сокет → данные (блоки до entry_data_size) |
|
struct ll_queue* write_queue; // данные → сокет (блоки до write_chunk_size) |
|
|
|
int read_high_water; |
|
int read_low_water; |
|
uint8_t read_paused; |
|
|
|
uint8_t write_monitor; // 1 = EPOLLOUT активен |
|
uint8_t connected; |
|
uint8_t error; |
|
uint8_t fin_remote; // FIN получен от удалённой стороны (recv == 0) |
|
uint8_t fin_local; // FIN отправлен удалённой стороне (shutdown SHUT_WR) |
|
uint8_t closed; // сокет полностью закрыт (close) |
|
|
|
// Частичная отправка (из write_pool, не в очереди — досылается первой) |
|
uint8_t* write_buf; |
|
size_t write_len; |
|
size_t write_offset; |
|
size_t write_chunk_size; |
|
|
|
// Пулы памяти |
|
struct memory_pool* entry_pool; // sizeof(struct ll_entry) |
|
struct memory_pool* data_pool; // max(entry_data_size, write_chunk_size) |
|
size_t entry_data_size; |
|
|
|
struct queue_waiter_handle read_waiter; |
|
|
|
// Коллбэки |
|
void (*on_fin)(struct tcp_conn* tc, void* arg); // FIN получен от удалённой стороны (fin_remote=1) |
|
void (*on_fin_sent)(struct tcp_conn* tc, void* arg); // FIN отправлен удалённой стороне (fin_local=1) |
|
void (*on_error)(struct tcp_conn* tc, int err, void* arg); // ошибка сокета |
|
void (*on_flushed)(struct tcp_conn* tc, void* arg); // все данные записи отправлены (write_queue + write_buf пусты) |
|
void (*on_closed)(struct tcp_conn* tc, void* arg); // сокет закрыт через очередь (после close) |
|
void* arg; |
|
}; |
|
|
|
struct tcp_conn* tcp_conn_create( |
|
struct UASYNC* ua, |
|
socket_t sock, |
|
size_t entry_data_size, |
|
size_t write_chunk_size, |
|
int read_high_water, |
|
int read_low_water, |
|
void (*on_fin)(struct tcp_conn* tc, void* arg), |
|
void (*on_error)(struct tcp_conn* tc, int err, void* arg), |
|
void* arg); |
|
|
|
void tcp_conn_destroy(struct tcp_conn* tc); |
|
|
|
// Внешний код пишет данные в tc->write_queue напрямую (queue_data_put). |
|
// Автозабор write_queue (deferred) сам отправляет когда сокет готов. |
|
// Перед push проверять порог: queue_set_threshold в tcp_conn_create (32 entries). |
|
// При заполнении — queue_waiter_wait на освобождение. |
|
|
|
// Поставить сигналы в очередь отправки. Все данные в очереди перед сигналом |
|
// будут отправлены до его обработки. Кодирование: dgram=NULL → close, len=0+dgram!=NULL → FIN. |
|
// tcp_conn_push_fin(tc) — FIN: после отправки предшествующих данных вызывает |
|
// shutdown(SHUT_WR), останавливает чтение, вызывает on_fin_sent. |
|
// Повторный вызов игнорируется (fin_sent уже установлен). |
|
// tcp_conn_push_close(tc) — CLOSE: после отправки предшествующих данных закрывает |
|
// сокет (close), вызывает on_closed. |
|
// После close сокет удалён из uasync, tc->closed=1. |
|
// Повторный вызов игнорируется. |
|
int tcp_conn_push_fin(struct tcp_conn* tc); |
|
int tcp_conn_push_close(struct tcp_conn* tc); |
|
|
|
// Одноразовый коллбэк: вызывается когда write_queue + write_buf полностью опустели. |
|
// После вызова сбрасывается. Установить повторно можно в любой момент. |
|
void tcp_conn_set_flushed(struct tcp_conn* tc, void (*on_flushed)(struct tcp_conn* tc, void* arg)); |
|
|
|
// Принудительная остановка чтения: убирает EPOLLIN и отменяет read_waiter. |
|
void tcp_conn_pause_read(struct tcp_conn* tc); |
|
|
|
#endif
|
|
|