// 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