Source
stdlib/socket/socket.cpp
1
// Copyright (c) 2026 BigBrain LLC. MIT-licensed (see LICENSE).2
// Original work; see ACKNOWLEDGMENTS.md for the open-source ideas we build upon.3
#include "socket.hpp"5
#include <cerrno>6
#include <cstdlib>7
#include <cstring>8
#include <string>9
#include <system_error>10
#include <vector>12
#if defined(_WIN32)13
// Windows: the BSD socket API lives in Winsock2 (closesocket, WSAStartup, SOCKET type).14
#include <winsock2.h>15
#include <ws2tcpip.h>16
#pragma comment(lib, "ws2_32.lib")17
#else18
#include <netdb.h>19
#include <netinet/in.h>20
#include <netinet/tcp.h> // TCP_NODELAY, TCP_QUICKACK — the throughput-tuning options21
#include <sys/socket.h>22
#include <unistd.h>23
#endif25
// Suppress SIGPIPE on send() to a closed peer. Linux passes MSG_NOSIGNAL per-call; macOS/26
// BSD have no such flag and instead use the SO_NOSIGPIPE socket option (set in socket()).27
// Define MSG_NOSIGNAL to 0 where it's absent (macOS, Windows) so the send() calls stay portable.28
#ifndef MSG_NOSIGNAL29
#define MSG_NOSIGNAL 030
#endif32
namespace cheatah::socket {33
namespace {35
#if defined(_WIN32)36
// Winsock must be initialized once per process before any socket call. A function-local37
// static does it lazily and tears it down at exit.38
void ensure_winsock() {39
struct WinsockInit {40
WinsockInit() { WSADATA d; WSAStartup(MAKEWORD(2, 2), &d); }41
~WinsockInit() { WSACleanup(); }42
};43
static WinsockInit init;44
}45
SOCKET as_fd(long long fd) { return static_cast<SOCKET>(fd); }46
#else47
void ensure_winsock() {}48
int as_fd(long long fd) { return static_cast<int>(fd); }49
#endif51
// Resolve host:port to an IPv4 TCP address. Returns true and fills `out`/`len` on52
// success. Used by bind/connect so "localhost", dotted IPs, and DNS names all work.53
bool resolve(const std::string& host, long long port, sockaddr_storage& out, socklen_t& len) {54
ensure_winsock();55
addrinfo hints{};56
hints.ai_family = AF_INET;57
hints.ai_socktype = SOCK_STREAM;58
hints.ai_protocol = IPPROTO_TCP;59
const std::string service = std::to_string(port);60
addrinfo* res = nullptr;61
if (::getaddrinfo(host.c_str(), service.c_str(), &hints, &res) != 0 || res == nullptr) {62
errno = EADDRNOTAVAIL;63
return false;64
}65
std::memcpy(&out, res->ai_addr, res->ai_addrlen);66
len = res->ai_addrlen;67
::freeaddrinfo(res);68
return true;69
}71
// The receive-buffer / send-buffer size we request on every connected stream socket. Bumping the72
// window off the kernel default is what lets a bulk download stay in flight instead of crawling one73
// TLS record per round-trip. 4 MiB is clamped down by the kernel to net.core.rmem_max, and is far74
// larger than any realistic bandwidth-delay product for the downloads this serves. See NOTES —75
// "TLS download throughput".76
constexpr int kStreamBufBytes = 4 * 1024 * 1024;78
// Apply the high-throughput options to a CONNECTED stream socket (best-effort — tuning never79
// changes correctness, so a failed setsockopt is silently ignored). Called from connect() and80
// accept() so every connected socket is tuned no matter the entry point:81
// TCP_NODELAY — disable Nagle; small control writes (the HTTP request, TLS records) go at once.82
// TCP_QUICKACK — send ACKs immediately instead of delaying them ~40 ms; without this a pure83
// download stalls in delayed-ACK slow start (the server's cwnd never ramps). It is84
// one-shot on Linux, so recv() re-arms it after every read.85
// SO_RCVBUF/SNDBUF — open the window so the peer can keep the pipe full.86
void tune_stream_socket(long long fd) {87
#if !defined(_WIN32)88
int one = 1;89
#ifdef TCP_NODELAY90
::setsockopt(as_fd(fd), IPPROTO_TCP, TCP_NODELAY, &one, sizeof(one));91
#endif92
#ifdef TCP_QUICKACK93
::setsockopt(as_fd(fd), IPPROTO_TCP, TCP_QUICKACK, &one, sizeof(one));94
#endif95
int buf = kStreamBufBytes;96
::setsockopt(as_fd(fd), SOL_SOCKET, SO_RCVBUF, &buf, sizeof(buf));97
::setsockopt(as_fd(fd), SOL_SOCKET, SO_SNDBUF, &buf, sizeof(buf));98
#else99
BOOL one = TRUE;100
::setsockopt(as_fd(fd), IPPROTO_TCP, TCP_NODELAY,101
reinterpret_cast<const char*>(&one), sizeof(one));102
int buf = kStreamBufBytes;103
::setsockopt(as_fd(fd), SOL_SOCKET, SO_RCVBUF,104
reinterpret_cast<const char*>(&buf), sizeof(buf));105
::setsockopt(as_fd(fd), SOL_SOCKET, SO_SNDBUF,106
reinterpret_cast<const char*>(&buf), sizeof(buf));107
#endif108
}110
} // namespace112
long long socket() {113
ensure_winsock();114
const auto fd = ::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);115
#if defined(_WIN32)116
if (fd == INVALID_SOCKET) return -1; // Winsock signals failure with INVALID_SOCKET117
#else118
#ifdef SO_NOSIGPIPE119
// macOS/BSD: ask the kernel not to raise SIGPIPE on this socket (Linux uses120
// MSG_NOSIGNAL per send() instead — SO_NOSIGPIPE isn't defined there).121
if (fd >= 0) {122
int on = 1;123
::setsockopt(fd, SOL_SOCKET, SO_NOSIGPIPE, &on, sizeof(on));124
}125
#endif126
#endif127
return static_cast<long long>(fd);128
}130
long long set_reuseaddr(long long fd) {131
int yes = 1;132
// optval is const void* on POSIX, const char* on Winsock — char* converts to both.133
return ::setsockopt(as_fd(fd), SOL_SOCKET, SO_REUSEADDR,134
reinterpret_cast<const char*>(&yes), sizeof(yes));135
}137
long long bind(long long fd, const std::string& host, long long port) {138
sockaddr_storage addr{};139
socklen_t len = 0;140
if (!resolve(host, port, addr, len)) return -1;141
return ::bind(as_fd(fd), reinterpret_cast<sockaddr*>(&addr), len);142
}144
long long listen(long long fd, long long backlog) {145
return ::listen(as_fd(fd), static_cast<int>(backlog));146
}148
long long connect(long long fd, const std::string& host, long long port) {149
sockaddr_storage addr{};150
socklen_t len = 0;151
if (!resolve(host, port, addr, len)) return -1;152
const int rc = ::connect(as_fd(fd), reinterpret_cast<sockaddr*>(&addr), len);153
if (rc == 0) {154
tune_stream_socket(fd); // every connected client socket gets the throughput options155
}156
return rc;157
}159
long long accept(long long fd) {160
const auto c = ::accept(as_fd(fd), nullptr, nullptr);161
#if defined(_WIN32)162
if (c == INVALID_SOCKET) return -1;163
#endif164
if (static_cast<long long>(c) >= 0) {165
tune_stream_socket(static_cast<long long>(c)); // and every accepted server socket166
}167
return static_cast<long long>(c);168
}170
long long local_port(long long fd) {171
sockaddr_storage addr{};172
socklen_t len = sizeof(addr);173
if (::getsockname(as_fd(fd), reinterpret_cast<sockaddr*>(&addr), &len) != 0) return -1;174
// sin_port sits at the same offset for IPv4/IPv6, and we only ever make175
// AF_INET sockets, so reading it back is safe.176
return ntohs(reinterpret_cast<sockaddr_in*>(&addr)->sin_port);177
}179
long long send(long long fd, const std::string& data) {180
// send() returns ssize_t on POSIX, int on Winsock — long long holds both.181
const long long n = ::send(as_fd(fd), data.data(),182
static_cast<int>(data.size()), MSG_NOSIGNAL);183
return n;184
}186
long long sendall(long long fd, const std::string& data) {187
std::size_t sent = 0;188
while (sent < data.size()) {189
const long long n = ::send(as_fd(fd), data.data() + sent,190
static_cast<int>(data.size() - sent), MSG_NOSIGNAL);191
if (n <= 0) return -1;192
sent += static_cast<std::size_t>(n);193
}194
return 0;195
}197
std::string recv(long long fd, long long bufsize) {198
if (bufsize <= 0) return {};199
// Read into a REUSED per-thread scratch buffer so we don't allocate + zero-fill a fresh200
// `bufsize` string on every call (a 64 KiB memset per recv on the download hot path). Only the201
// n bytes actually received are copied into the returned string.202
static thread_local std::vector<char> scratch;203
if (scratch.size() < static_cast<std::size_t>(bufsize)) {204
scratch.resize(static_cast<std::size_t>(bufsize));205
}206
const long long n = ::recv(as_fd(fd), scratch.data(), static_cast<int>(bufsize), 0);207
if (n <= 0) return {};208
std::string buf(scratch.data(), static_cast<std::size_t>(n));209
#if !defined(_WIN32) && defined(TCP_QUICKACK)210
// Re-arm quick-ACK: Linux clears it after each read, so without this the delayed-ACK stall211
// creeps back mid-transfer and the server's window stops growing.212
int one = 1;213
::setsockopt(as_fd(fd), IPPROTO_TCP, TCP_QUICKACK, &one, sizeof(one));214
#endif215
return buf;216
}218
long long close(long long fd) {219
#if defined(_WIN32)220
return ::closesocket(as_fd(fd));221
#else222
return ::close(as_fd(fd));223
#endif224
}226
long long shutdown(long long fd) {227
// Half-close both directions WITHOUT releasing the fd: a blocking recv() on228
// ANOTHER thread returns immediately (EOF). Used to wake a reader for a clean229
// shutdown; the fd is still owned by the caller and must be close()d afterwards.230
#if defined(_WIN32)231
return ::shutdown(as_fd(fd), SD_BOTH);232
#else233
return ::shutdown(as_fd(fd), SHUT_RDWR);234
#endif235
}237
long long tcp_listen(const std::string& host, long long port, long long backlog) {238
// If socket() fails (-1), the bind below fails too and we fall through to the239
// error path — no separate early return needed.240
long long fd = socket();241
set_reuseaddr(fd);242
if (bind(fd, host, port) != 0 || listen(fd, backlog) != 0) {243
close(fd);244
return -1;245
}246
return fd;247
}249
long long tcp_connect(const std::string& host, long long port) {250
long long fd = socket();251
if (connect(fd, host, port) != 0) {252
close(fd);253
return -1;254
}255
return fd;256
}258
long long udp_socket() {259
ensure_winsock();260
const auto fd = ::socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);261
#if defined(_WIN32)262
if (fd == INVALID_SOCKET) return -1;263
#else264
if (fd < 0) return -1;265
#endif266
return static_cast<long long>(fd);267
}269
long long sendto(long long fd, const std::string& host, long long port, const std::string& data) {270
sockaddr_storage addr{};271
socklen_t len = 0;272
if (!resolve(host, port, addr, len)) return -1;273
#if defined(_WIN32)274
const int flags = 0;275
#else276
#ifdef MSG_NOSIGNAL277
const int flags = MSG_NOSIGNAL;278
#else279
const int flags = 0;280
#endif281
#endif282
const auto n = ::sendto(as_fd(fd), data.data(), static_cast<int>(data.size()), flags,283
reinterpret_cast<const sockaddr*>(&addr), len);284
return n < 0 ? -1 : static_cast<long long>(n);285
}287
std::string recvfrom(long long fd, long long bufsize, std::string& out_host, long long& out_port) {288
out_host.clear();289
out_port = 0;290
if (bufsize <= 0) return {};291
static thread_local std::vector<char> scratch;292
if (scratch.size() < static_cast<std::size_t>(bufsize)) {293
scratch.resize(static_cast<std::size_t>(bufsize));294
}295
sockaddr_storage from{};296
socklen_t from_len = sizeof from;297
const auto n = ::recvfrom(as_fd(fd), scratch.data(), static_cast<int>(bufsize), 0,298
reinterpret_cast<sockaddr*>(&from), &from_len);299
if (n <= 0) return {};300
char host[NI_MAXHOST] = {0};301
char serv[NI_MAXSERV] = {0};302
if (::getnameinfo(reinterpret_cast<const sockaddr*>(&from), from_len, host, sizeof host, serv,303
sizeof serv, NI_NUMERICHOST | NI_NUMERICSERV) == 0) {304
out_host = host;305
char* end = nullptr;306
const long long port = std::strtoll(serv, &end, 10);307
out_port = (end != nullptr && *end == '\0') ? port : 0; // NI_NUMERICSERV is digits; be sure308
}309
return {scratch.data(), static_cast<std::size_t>(n)};310
}312
long long set_timeout(long long fd, long long timeout_ms) {313
ensure_winsock();314
#if defined(_WIN32)315
const DWORD ms = timeout_ms > 0 ? static_cast<DWORD>(timeout_ms) : 0;316
if (::setsockopt(as_fd(fd), SOL_SOCKET, SO_RCVTIMEO,317
reinterpret_cast<const char*>(&ms), sizeof ms) != 0) return -1;318
if (::setsockopt(as_fd(fd), SOL_SOCKET, SO_SNDTIMEO,319
reinterpret_cast<const char*>(&ms), sizeof ms) != 0) return -1;320
#else321
timeval tv{};322
if (timeout_ms > 0) {323
tv.tv_sec = timeout_ms / 1000;324
tv.tv_usec = static_cast<suseconds_t>((timeout_ms % 1000) * 1000);325
}326
if (::setsockopt(as_fd(fd), SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof tv) != 0) return -1;327
if (::setsockopt(as_fd(fd), SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof tv) != 0) return -1;328
#endif329
return 0;330
}332
std::string last_error() {333
#if defined(_WIN32)334
return "Winsock error " + std::to_string(::WSAGetLastError());335
#else336
return std::generic_category().message(errno); // thread-safe strerror337
#endif338
}340
// ---- owning RAII connections ----341
// Each method forwards to the fd-based free function above; the value the guards add is342
// deterministic close() on scope exit, so a `with` block cannot leak the fd.344
Conn& Conn::operator=(Conn&& other) noexcept {345
if (this != &other) {346
if (fd_ >= 0) cheatah::socket::close(fd_);347
fd_ = other.fd_;348
other.fd_ = -1;349
}350
return *this;351
}352
Conn::~Conn() {353
if (fd_ >= 0) cheatah::socket::close(fd_);354
}355
long long Conn::send(const std::string& data) const { return cheatah::socket::send(fd_, data); }356
long long Conn::sendall(const std::string& data) const { return cheatah::socket::sendall(fd_, data); }357
std::string Conn::recv(long long bufsize) const { return cheatah::socket::recv(fd_, bufsize); }358
long long Conn::set_timeout(long long timeout_ms) const {359
return cheatah::socket::set_timeout(fd_, timeout_ms);360
}361
long long Conn::local_port() const { return cheatah::socket::local_port(fd_); }362
long long Conn::shutdown() const { return cheatah::socket::shutdown(fd_); }363
long long Conn::close() {364
if (fd_ < 0) return -1;365
const long long rc = cheatah::socket::close(fd_);366
fd_ = -1;367
return rc;368
}370
Listener& Listener::operator=(Listener&& other) noexcept {371
if (this != &other) {372
if (fd_ >= 0) cheatah::socket::close(fd_);373
fd_ = other.fd_;374
other.fd_ = -1;375
}376
return *this;377
}378
Listener::~Listener() {379
if (fd_ >= 0) cheatah::socket::close(fd_);380
}381
Conn Listener::accept() const { return Conn(cheatah::socket::accept(fd_)); }382
long long Listener::local_port() const { return cheatah::socket::local_port(fd_); }383
long long Listener::close() {384
if (fd_ < 0) return -1;385
const long long rc = cheatah::socket::close(fd_);386
fd_ = -1;387
return rc;388
}390
Conn open(const std::string& host, long long port) { return Conn(tcp_connect(host, port)); }391
Listener serve(const std::string& host, long long port, long long backlog) {392
return Listener(tcp_listen(host, port, backlog));393
}395
} // namespace cheatah::socket