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

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