Потоковые сервисы TCP: три колбэка в мире голых байтов
Игровые шлюзы, приём IoT, приватные протоколы — они не говорят по HTTP. Класс tcp/tcps отдаёт вам голый поток; всё остальное определяете вы.
Обзор
23.1 Введение
Ценность HTTP — «договорились, как выглядит запрос»; ценность TCP — «ни о чём не договорились». Поэтому обязанности хоста по TCP сжимаются до трёх: построить поток, отдать данные, доложить терминальное состояние — право проектировать протокол остаётся целиком за вами. TLS (tcps) лишь подключает слой шифрования к потоку; поверхность колбэков та же.
23.2 Представление и колбэки
/* XS_StreamConn: ровно один из tcp/tls не пуст (под tcps запрещено писать в tcp в обход tls) */
static void EventOpen(XS_HostInfo* pHost, XS_StreamConn* pConn);
static void EventData(XS_HostInfo* pHost, XS_StreamConn* pConn, xnetbuf* pBuffer);
static void EventClose(XS_HostInfo* pHost, XS_StreamConn* pConn,
xnetresult iResult, const xerror* pError);
- EventOpen: соединение установлено — момент для приветственного баннера и контекста сеанса
- EventData: пришли данные.
pBuffer— цепочка буферов без копирования (заимствована в колбэке): размер черезxrtNetBufSize(pBuffer), данные потребляются итерационными API буферов xrt — это не сплошной блок памяти, не трактуйте его как char* - EventClose: терминал — iResult даёт вердикт сетевого слоя, pError — детали (может быть NULL)
- Отправка:
xrtNetStreamSend(pConn->tcp, ...)/xrtTlsStreamSend(pConn->tls, ...)по непустой стороне
23.3 Баннер и эхо
static void EventOpen(XS_HostInfo* pHost, XS_StreamConn* pConn)
{
if ( pConn->tcp != NULL )
(void)xrtNetStreamSend(pConn->tcp, "hello\n", 6);
/* tcps: проверять pConn->tls + xrtTlsStreamSend (сверять written) */
}
static void EventData(XS_HostInfo* pHost, XS_StreamConn* pConn, xnetbuf* pBuffer)
{
/* эхо: отправить полученное тем же путём обратно.
* реальная реализация: обходить каждый блок цепочки и Send по сегментам;
* размер: xrtNetBufSize(pBuffer) */
}
$ telnet 127.0.0.1 9000 hello (каждая введённая строка эхом возвращается как есть)
Что только что произошло: баннер при подключении — конвенция многих текстовых протоколов (SMTP/FTP/IRC); эхо — «hello world» потокового сервиса. Реальный протокол (заголовок кадра + длина + тело) добавляет поверх автомат состояний — XTP2 из гл. 33 и есть готовая кадровая библиотека.
23.4 Жизненный цикл и лимиты
- Жизненным циклом соединения владеет хост: перехват Accept → реестр → деструкция на Close — приложение не вызывает Destroy у соединения (в отличие от класса custom, где всё вручную)
- SNI: под tcps сертификаты хостов строят таблицу SNI; каждому соединению сертификат по SNI
- idle_timeout: простаивающие соединения закрываются сервером по истечении (в func_test есть замеренный кейс закрытия на 4,5 с)
- recv_limit: меньше блока чтения по умолчанию — размер чтения синхронно сжимается (конфиг не вызывает генерализованных отказов)
- Контекст сеанса: изоморфен WS — собственный реестр (соединение → контекст), вставка в Open, удаление в Close
23.5 Контракт
- Заимствование: pConn/pBuffer/pError действительны в колбэке; данные цепочки потребляются как кадры, не хранятся навсегда.
- Направление: под tcps запрещено писать в tcp в обход tls — открытый текст на той стороне читается как шум TLS-записей.
- Жизненный цикл: соединения у хоста; контекст сеанса у вас, чистка в терминале Close.
Частые ошибки
- Симптом
- Перепутанные данные / выход за границы.
- Причина
- Это цепочка буферов (структура с полями Head/Tail/Size) — потребление только через итерационные API буферов xrt поблочно; единого «указателя на всё» нет.
- Симптом
- Отправка/приём по указателю соединения из чужого потока — падение или перепутанный порядок.
- Причина
- Активный ввод-вывод вне колбэков — режим «перехвата» (XS_TAKEOVER у HTTP / собственные потоки требуют осторожности); в обычном режиме логика событийная и укладывается в три такта Open/Data/Close.
Упражнения
Реализуйте сервис 23.3 и проверьте telnet.
Кадрируйте по \n между раундами EventData (буфер полустроки) и соберите мини-KV с командами GET/SET/QUIT.
Включите TLS-порт с сертификатом; одни и те же колбэки обслуживают оба эндпоинта — открытый и шифрованный.
Итоги
| Тезис | Вывод |
|---|---|
| Поверхность колбэков | три такта Open/Data/Close; проектирование протокола целиком ваше |
| Форма данных | xnetbuf — цепочка без копий, заимствуется в колбэке, потребляется поблочно |
| tcps | одно из двух в представлении; обход tls запрещён; SNI выдаёт сертификат на соединение |
| Жизненный цикл | соединения у хоста; контекст у вас (реестр + чистка в Close) |
| Лимиты | idle_timeout закрывает простои; recv_limit сжимает блоки чтения |
- справочник xrt · net — итерация цепочек буферов и API потоковой отправки
- справочник xrt · tcp_server — режимы прослушивания и семантика Accept