socket_compat — Cross-platform socket abstraction
1. Назначение
Унифицирует работу с UDP-сокетами между POSIX и Windows (MSYS2 UCRT64). Скрывает различия в типах дескрипторов (int vs SOCKET), инициализации подсистемы (WSAStartup/WSACleanup), кодах ошибок и сигнатурах системных вызовов.
2. Как пользоваться
Инициализация (однократно при старте)
if (socket_platform_init() != 0) { /* фатальная ошибка */ }
// ... работа с сокетами ...
socket_platform_cleanup(); // при завершении
На Windows WSAStartup/WSACleanup с refcount — можно вызывать init/cleanup вложенно.
Создание и настройка сокета
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
Отправка и приём
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
Обработка ошибок
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));
}
Закрытие
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 |