#Basic Socket Helpers
#define FIO_SOCK
#include "fio-stl.h"Portable socket helpers for POSIX and Windows: open, bind, connect, listen, wait, read, write, close, and a few sharp socket-shaped knives. Implemented in ./004 sock.h.
FIO_SOCK depends on URL parsing internally and pulls in what it needs.
#Configuration Macros
#FIO_SOCK_DEFAULT_MAXIMIZE_LIMIT
#ifndef FIO_SOCK_DEFAULT_MAXIMIZE_LIMIT
#define FIO_SOCK_DEFAULT_MAXIMIZE_LIMIT (1ULL << 24)
#endifDefault target used by fio_sock_maximize_limits(0).
#FIO_SOCK_AVOID_UMASK
/* optional compile-time flag */
#define FIO_SOCK_AVOID_UMASKIf defined before including the implementation, Unix socket creation avoids the temporary umask call used when making public Unix sockets. This avoids umask's process-global race, but may affect permissions on systems where chmod on Unix socket files is not enough.
Windows behaves as if this concern does not apply.
#Types and Flags
#fio_socket_i
#if FIO_OS_WIN
typedef SOCKET fio_socket_i;
#else
typedef int fio_socket_i;
#endifNative socket handle type. Use this instead of int when writing portable code.
#FIO_SOCKET_INVALID
#if FIO_OS_WIN
#define FIO_SOCKET_INVALID INVALID_SOCKET
#else
#define FIO_SOCKET_INVALID (-1)
#endifInvalid socket sentinel.
#FIO_SOCK_FD_ISVALID
#define FIO_SOCK_FD_ISVALID(fd) ((fio_socket_i)(fd) != FIO_SOCKET_INVALID)Returns non-zero if fd is not the invalid sentinel.
#fio_sock_open_flags_e
typedef enum {
FIO_SOCK_SERVER = 0,
FIO_SOCK_CLIENT = 1,
FIO_SOCK_NONBLOCK = 2,
FIO_SOCK_TCP = 4,
FIO_SOCK_UDP = 8,
#ifdef AF_UNIX
FIO_SOCK_UNIX = 16,
FIO_SOCK_UNIX_PRIVATE = (16 | 32),
#else
#define FIO_SOCK_UNIX 0
#define FIO_SOCK_UNIX_PRIVATE 0
#endif
} fio_sock_open_flags_e;Flags for fio_sock_open, fio_sock_open2, and fio_sock_open_unix.
Values:
FIO_SOCK_SERVER- bind a local socket; TCP / stream sockets also calllistenFIO_SOCK_CLIENT- connect a remote socketFIO_SOCK_NONBLOCK- set non-blocking modeFIO_SOCK_TCP- stream socketFIO_SOCK_UDP- datagram socketFIO_SOCK_UNIX- Unix domain socket where supported, otherwise0FIO_SOCK_UNIX_PRIVATE- Unix socket with private permissions where supported, otherwise0
FIO_SOCK_TCP and FIO_SOCK_UDP are exclusive. If neither is set, network sockets default to TCP.
#Opening Sockets
#fio_sock_open
FIO_IFUNC fio_socket_i fio_sock_open(const char *restrict address,
const char *restrict port,
uint16_t flags);Creates a socket from an address / port pair and flags.
For FIO_SOCK_UNIX, port is ignored and address is treated as the Unix socket path. For server TCP sockets, address == NULL binds to all interfaces.
Returns: a socket handle, or FIO_SOCKET_INVALID on error.
#fio_sock_open2
SFUNC fio_socket_i fio_sock_open2(const char *url, uint16_t flags);Creates a socket from a URL-ish string. If no port is present, the URL scheme is used as the port / service name. Schemes such as tcp, udp, http, and https can also imply socket type when flags omit it.
Unix path URLs are detected when supported. A priv scheme selects FIO_SOCK_UNIX_PRIVATE; other path-only Unix forms select FIO_SOCK_UNIX.
Returns: a socket handle, or FIO_SOCKET_INVALID on error.
#fio_sock_address_new
FIO_IFUNC struct addrinfo *fio_sock_address_new(const char *restrict address,
const char *restrict port,
int sock_type);Resolves address / port into an addrinfo list for sock_type, usually SOCK_STREAM or SOCK_DGRAM.
Common service names are normalized before getaddrinfo: ws / http / sse to port 80, and wss / https / sses to port 443.
Ownership: free the result with fio_sock_address_free.
#fio_sock_address_free
FIO_IFUNC void fio_sock_address_free(struct addrinfo *a);Frees an address list returned by fio_sock_address_new.
#fio_sock_open_local
SFUNC fio_socket_i fio_sock_open_local(struct addrinfo *addr, int nonblock);Creates a network socket from an addrinfo list and binds it locally. SO_REUSEADDR is enabled. If nonblock is non-zero, non-blocking mode is requested before binding.
Returns: a socket handle, or FIO_SOCKET_INVALID.
#fio_sock_open_remote
SFUNC fio_socket_i fio_sock_open_remote(struct addrinfo *addr, int nonblock);Creates a network socket from an addrinfo list and connects it remotely. Non-blocking connect progress (EINPROGRESS / Windows equivalents) returns the socket.
Returns: a socket handle, or FIO_SOCKET_INVALID.
#fio_sock_open_unix
SFUNC fio_socket_i fio_sock_open_unix(const char *address, uint16_t flags);Creates a Unix domain socket where supported. Server sockets unlink an existing path before binding. Non-UDP server sockets call listen.
If Unix sockets are unsupported, the function asserts and returns FIO_SOCKET_INVALID.
#Portable Socket Operations
Use these wrappers when code should run on both POSIX and WinSock2.
#fio_sock_write
FIO_IFUNC ssize_t fio_sock_write(fio_socket_i fd, const void *buf, size_t len);Acts like POSIX write on POSIX and uses send on Windows.
#fio_sock_read
FIO_IFUNC ssize_t fio_sock_read(fio_socket_i fd, void *buf, size_t len);Acts like POSIX read on POSIX and uses recv on Windows.
#fio_sock_sendto
FIO_IFUNC ssize_t fio_sock_sendto(fio_socket_i fd,
const void *buf,
size_t len,
int flags,
const struct sockaddr *addr,
socklen_t addrlen);Portable sendto wrapper.
#fio_sock_recvfrom
FIO_IFUNC ssize_t fio_sock_recvfrom(fio_socket_i fd,
void *buf,
size_t len,
int flags,
struct sockaddr *addr,
socklen_t *addrlen);Portable recvfrom wrapper.
#fio_sock_close
FIO_IFUNC int fio_sock_close(fio_socket_i fd);Portable close wrapper: close on POSIX, closesocket on Windows.
#fio_sock_accept
#if FIO_OS_WIN
IFUNC fio_socket_i fio_sock_accept(fio_socket_i s,
struct sockaddr *addr,
int *addrlen);
#else
#define fio_sock_accept(fd, addr, addrlen) accept(fd, addr, addrlen)
#endifPortable accept wrapper.
#fio_sock_dup
FIO_IFUNC fio_socket_i fio_sock_dup(fio_socket_i fd);Duplicates a socket handle. POSIX uses dup and sets FD_CLOEXEC; Windows uses WSADuplicateSocket and WSASocket.
#fio_sock_bind
FIO_IFUNC int fio_sock_bind(fio_socket_i fd,
const struct sockaddr *addr,
socklen_t addrlen);Portable bind wrapper.
#fio_sock_connect
FIO_IFUNC int fio_sock_connect(fio_socket_i fd,
const struct sockaddr *addr,
socklen_t addrlen);Portable connect wrapper.
#fio_sock_listen
FIO_IFUNC int fio_sock_listen(fio_socket_i fd, int backlog);Portable listen wrapper.
#fio_sock_setsockopt
FIO_IFUNC int fio_sock_setsockopt(fio_socket_i fd,
int level,
int optname,
const void *optval,
socklen_t optlen);Portable setsockopt wrapper. Pointer and length type differences are handled internally.
#fio_sock_socketpair
FIO_IFUNC int fio_sock_socketpair(fio_socket_i fds[2]);Creates a connected socket pair. POSIX uses socketpair(AF_UNIX, SOCK_STREAM, 0, fds). Windows creates a loopback TCP pair.
#fio_sock_pipe
FIO_IFUNC int fio_sock_pipe(fio_socket_i fds[2]);Creates a pipe-like pair. POSIX uses pipe; Windows delegates to fio_sock_socketpair so the handles are usable with WinSock polling.
#Socket State and Waiting
#fio_sock_set_non_block
SFUNC int fio_sock_set_non_block(fio_socket_i fd);Sets a socket to non-blocking mode.
Returns: 0 on success, -1 on error.
#fio_sock_maximize_limits
SFUNC size_t fio_sock_maximize_limits(size_t maximum_limit);Attempts to raise the open-file limit up to maximum_limit. Passing 0 uses FIO_SOCK_DEFAULT_MAXIMIZE_LIMIT.
On Windows this returns the advisory cap because there is no RLIMIT_NOFILE equivalent for sockets.
#fio_sock_wait_io
SFUNC short fio_sock_wait_io(fio_socket_i fd, short events, int timeout);Waits for events on one socket using poll / WSAPoll. timeout is in milliseconds; 0 returns immediately.
Returns: 0 on timeout, -1 on error, or event bits such as POLLIN, POLLOUT, POLLHUP, and POLLNVAL.
#FIO_SOCK_WAIT_RW
#define FIO_SOCK_WAIT_RW(fd, timeout_) \
fio_sock_wait_io(fd, POLLIN | POLLOUT, timeout_)Waits for read or write readiness.
#FIO_SOCK_WAIT_R
#define FIO_SOCK_WAIT_R(fd, timeout_) fio_sock_wait_io(fd, POLLIN, timeout_)Waits for read readiness.
#FIO_SOCK_WAIT_W
#define FIO_SOCK_WAIT_W(fd, timeout_) fio_sock_wait_io(fd, POLLOUT, timeout_)Waits for write readiness.
#FIO_SOCK_IS_OPEN
#ifdef POLLRDHUP
#define FIO_SOCK_IS_OPEN(fd) \
(!(fio_sock_wait_io(fd, (POLLOUT | POLLRDHUP), 0) & \
(POLLRDHUP | POLLHUP | POLLNVAL)))
#else
#define FIO_SOCK_IS_OPEN(fd) \
(!(fio_sock_wait_io(fd, POLLOUT, 0) & (POLLHUP | POLLNVAL)))
#endifBest-effort test for whether a socket still appears open.
#fio_sock_peer_addr
SFUNC fio_buf_info_s fio_sock_peer_addr(fio_socket_i s);Returns a numeric, human-readable peer address.
On error, returns {NULL, 0}. The returned buffer is internal static storage, limited to 63 bytes. Thread-safety is limited to 128 threads / calls.
#Windows Notes
On Windows, the module initializes WinSock in a constructor and stores direct function pointers loaded from Ws2_32.dll. Wrapper functions translate common WinSock errors into errno values.
The fio___sock_wsa* helpers are internal implementation details, not regular application API. Tiny dragons; leave them napping.
#Ownership and Thread-Safety
Socket handles are owned by the caller and should be closed with fio_sock_close. fio_sock_address_new results are owned by the caller and should be freed with fio_sock_address_free.
Socket operations are as thread-safe as the operating system calls they wrap. fio_sock_peer_addr uses shared static buffers and has the 128-call/thread caveat described above. Unix socket creation may touch process-global umask unless FIO_SOCK_AVOID_UMASK is defined.
#Example
#define FIO_SOCK
#include "fio-stl.h"
#include <stdio.h>
#include <string.h>
int main(void) {
fio_socket_i fds[2];
char buf[8] = {0};
if (fio_sock_pipe(fds))
return 1;
fio_sock_write(fds[1], "ping", 4);
fio_sock_read(fds[0], buf, sizeof(buf) - 1);
printf("%s\n", buf);
fio_sock_close(fds[0]);
fio_sock_close(fds[1]);
return 0;
}#TCP Server Sketch
#define FIO_SOCK
#include "fio-stl.h"
int main(void) {
fio_socket_i s = fio_sock_open(NULL, "8080",
FIO_SOCK_SERVER | FIO_SOCK_TCP |
FIO_SOCK_NONBLOCK);
if (!FIO_SOCK_FD_ISVALID(s))
return 1;
if (FIO_SOCK_WAIT_R(s, 1000) & POLLIN) {
fio_socket_i c = fio_sock_accept(s, NULL, NULL);
if (FIO_SOCK_FD_ISVALID(c))
fio_sock_close(c);
}
fio_sock_close(s);
return 0;
}