# socket_compat — Cross-platform socket abstraction ## 1. Назначение Унифицирует работу с UDP-сокетами между POSIX и Windows (MSYS2 UCRT64). Скрывает различия в типах дескрипторов (`int` vs `SOCKET`), инициализации подсистемы (WSAStartup/WSACleanup), кодах ошибок и сигнатурах системных вызовов. ## 2. Как пользоваться ### Инициализация (однократно при старте) ```c if (socket_platform_init() != 0) { /* фатальная ошибка */ } // ... работа с сокетами ... socket_platform_cleanup(); // при завершении ``` На Windows WSAStartup/WSACleanup с refcount — можно вызывать init/cleanup вложенно. ### Создание и настройка сокета ```c socket_t sock = socket_create_udp(AF_INET); // или AF_INET6 if (sock == SOCKET_INVALID) { /* ошибка */ } socket_set_nonblocking(sock); // обязательно для u_async socket_set_reuseaddr(sock, 1); socket_set_buffers(sock, 256*1024, 256*1024); // SO_SNDBUF/SO_RCVBUF socket_bind_to_device(sock, "eth0"); // только Linux, SO_BINDTODEVICE socket_set_mark(sock, 42); // только Linux, SO_MARK ``` ### Отправка и приём ```c ssize_t sent = socket_sendto(sock, buf, len, (struct sockaddr*)&addr, addr_len); ssize_t recv = socket_recvfrom(sock, buf, sizeof(buf), (struct sockaddr*)&src, &src_len); uint16_t port = ss_get_port(&src); // порт из sockaddr_storage ``` ### Обработка ошибок ```c if (sent == SOCKET_ERROR_CODE) { int err = socket_get_error(); if (err == ERR_WOULDBLOCK) { /* нормально для неблокирующего */ } // на POSIX ERR_WOULDBLOCK == EWOULDBLOCK (может совпадать с EAGAIN) DEBUG_ERROR(..., "%s", socket_strerror(err)); } ``` ### Закрытие ```c socket_close_wrapper(sock); // на POSIX: close() с защитой от fd 0 ``` ### Ключевые нюансы - **Всегда вызывать `socket_platform_init()` перед работой** — иначе на Windows сокеты не будут работать - **Всегда `socket_set_nonblocking()`** — u_async требует неблокирующих сокетов - `socket_t` — на POSIX `int`, на Windows `SOCKET` (unsigned) - `SOCKET_INVALID` — константа для невалидного сокета (`-1` на POSIX, `INVALID_SOCKET` на Windows) - `SOCKET_ERROR_CODE` — код ошибки сокетных вызовов (`-1` на POSIX, `SOCKET_ERROR` на Windows) - `socket_strerror()` на Windows использует статический буфер — не thread-safe для параллельного использования - `socket_bind_to_device` и `socket_set_mark` — только Linux, на остальных платформах возвращают -1 с DEBUG-сообщением - `socket_close_wrapper` на POSIX не закроет fd 0 (защита) ## 3. API ### Типы и константы | Имя | Назначение | |-----|------------| | `socket_t` | Кроссплатформенный тип дескриптора сокета (`int` / `SOCKET`) | | `SOCKET_INVALID` | Невалидный сокет (`-1` / `INVALID_SOCKET`) | | `SOCKET_ERROR_CODE` | Код ошибки сокетного вызова (`-1` / `SOCKET_ERROR`) | | `ERR_WOULDBLOCK` / `ERR_AGAIN` / `ERR_INTR` | Кроссплатформенные коды ошибок | ### Инициализация | Функция | Назначение | |---------|------------| | `socket_platform_init()` | Инициализация сокетной подсистемы (WSAStartup на Windows). 0 — успех | | `socket_platform_cleanup()` | Деинициализация (WSACleanup на Windows), refcount | ### Создание и настройка | Функция | Назначение | |---------|------------| | `socket_create_udp(family)` | Создать UDP-сокет (AF_INET или AF_INET6) | | `socket_set_nonblocking(sock)` | Перевести в неблокирующий режим | | `socket_set_buffers(sock, snd, rcv)` | Установить SO_SNDBUF и SO_RCVBUF | | `socket_set_reuseaddr(sock, reuse)` | Установить SO_REUSEADDR | | `socket_bind_to_device(sock, ifname)` | Привязать к интерфейсу (Linux SO_BINDTODEVICE) | | `socket_set_mark(sock, mark)` | Установить SO_MARK (Linux) | ### I/O | Функция | Назначение | |---------|------------| | `socket_sendto(sock, buf, len, dest, dest_len)` | Отправить UDP-датаграмму | | `socket_recvfrom(sock, buf, len, src, src_len)` | Принять UDP-датаграмму | ### Утилиты | Функция | Назначение | |---------|------------| | `socket_get_error()` | Текущий код ошибки (inline: `WSAGetLastError()` / `errno`) | | `socket_strerror(err)` | Текстовое описание ошибки (Windows: статический буфер, не thread-safe) | | `socket_close_wrapper(sock)` | Закрыть сокет (POSIX: защита от закрытия fd 0) | | `ss_get_port(addr)` | Извлечь порт в host byte order из `sockaddr_storage` |