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 <cstring>7
#include <string>8
#include <vector>10
#if defined(_WIN32)11
// Windows: the BSD socket API lives in Winsock2 (closesocket, WSAStartup, SOCKET type).12
#include <winsock2.h>13
#include <ws2tcpip.h>14
#pragma comment(lib, "ws2_32.lib")15
#else16
#include <netdb.h>17
#include <netinet/in.h>18
#include <netinet/tcp.h> // TCP_NODELAY, TCP_QUICKACK — the throughput-tuning options19
#include <sys/socket.h>20
#include <unistd.h>21
#endif23
// Suppress SIGPIPE on send() to a closed peer. Linux passes MSG_NOSIGNAL per-call; macOS/24
// BSD have no such flag and instead use the SO_NOSIGPIPE socket option (set in socket()).25
// Define MSG_NOSIGNAL to 0 where it's absent (macOS, Windows) so the send() calls stay portable.26
#ifndef MSG_NOSIGNAL27
#define MSG_NOSIGNAL 028
#endif30
namespace cheatah::socket {31
namespace {33
#if defined(_WIN32)34
// Winsock must be initialized once per process before any socket call. A function-local35
// static does it lazily and tears it down at exit.36
void ensure_winsock() {37
struct WinsockInit {38
WinsockInit() { WSADATA d; WSAStartup(MAKEWORD(2, 2), &d); }39
~WinsockInit() { WSACleanup(); }40
};41
static WinsockInit init;42
}43
SOCKET as_fd(long long fd) { return static_cast<SOCKET>(fd); }44
#else45
void ensure_winsock() {}46
int as_fd(long long fd) { return static_cast<int>(fd); }47
#endif49
// Resolve host:port to an IPv4 TCP address. Returns true and fills `out`/`len` on50
// success. Used by bind/connect so "localhost", dotted IPs, and DNS names all work.51
bool resolve(const std::string& host, long long port, sockaddr_storage& out, socklen_t& len) {52
ensure_winsock();53
addrinfo hints{};54
hints.ai_family = AF_INET;55
hints.ai_socktype = SOCK_STREAM;56
hints.ai_protocol = IPPROTO_TCP;57
const std::string service = std::to_string(port);58
addrinfo* res = nullptr;59
if (::getaddrinfo(host.c_str(), service.c_str(), &hints, &res) != 0 || res == nullptr) {60
errno = EADDRNOTAVAIL;61
return false;62
}63
std::memcpy(&out, res->ai_addr, res->ai_addrlen);64
len = static_cast<socklen_t>(res->ai_addrlen);65
::freeaddrinfo(res);66
return true;67
}69
// The receive-buffer / send-buffer size we request on every connected stream socket. Bumping the70
// window off the kernel default is what lets a bulk download stay in flight instead of crawling one71
// TLS record per round-trip. 4 MiB is clamped down by the kernel to net.core.rmem_max, and is far72
// larger than any realistic bandwidth-delay product for the downloads this serves. See NOTES —73
// "TLS download throughput".74
constexpr int kStreamBufBytes = 4 * 1024 * 1024;76
// Apply the high-throughput options to a CONNECTED stream socket (best-effort — tuning never77
// changes correctness, so a failed setsockopt is silently ignored). Called from connect() and78
// accept() so every connected socket is tuned no matter the entry point:79
// TCP_NODELAY — disable Nagle; small control writes (the HTTP request, TLS records) go at once.80
// TCP_QUICKACK — send ACKs immediately instead of delaying them ~40 ms; without this a pure81
// download stalls in delayed-ACK slow start (the server's cwnd never ramps). It is82
// one-shot on Linux, so recv() re-arms it after every read.83
// SO_RCVBUF/SNDBUF — open the window so the peer can keep the pipe full.84
void tune_stream_socket(long long fd) {85
#if !defined(_WIN32)86
int one = 1;87
#ifdef TCP_NODELAY88
::setsockopt(as_fd(fd), IPPROTO_TCP, TCP_NODELAY, &one, sizeof(one));89
#endif90
#ifdef TCP_QUICKACK91
::setsockopt(as_fd(fd), IPPROTO_TCP, TCP_QUICKACK, &one, sizeof(one));92
#endif93
int buf = kStreamBufBytes;94
::setsockopt(as_fd(fd), SOL_SOCKET, SO_RCVBUF, &buf, sizeof(buf));95
::setsockopt(as_fd(fd), SOL_SOCKET, SO_SNDBUF, &buf, sizeof(buf));96
#else97
BOOL one = TRUE;98
::setsockopt(as_fd(fd), IPPROTO_TCP, TCP_NODELAY,99
reinterpret_cast<const char*>(&one), sizeof(one));100
int buf = kStreamBufBytes;101
::setsockopt(as_fd(fd), SOL_SOCKET, SO_RCVBUF,102
reinterpret_cast<const char*>(&buf), sizeof(buf));103
::setsockopt(as_fd(fd), SOL_SOCKET, SO_SNDBUF,104
reinterpret_cast<const char*>(&buf), sizeof(buf));105
#endif106
}108
} // namespace110
long long socket() {111
ensure_winsock();112
const auto fd = ::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);113
#if defined(_WIN32)114
if (fd == INVALID_SOCKET) return -1; // Winsock signals failure with INVALID_SOCKET115
#else116
#ifdef SO_NOSIGPIPE117
// macOS/BSD: ask the kernel not to raise SIGPIPE on this socket (Linux uses118
// MSG_NOSIGNAL per send() instead — SO_NOSIGPIPE isn't defined there).119
if (fd >= 0) {120
int on = 1;121
::setsockopt(fd, SOL_SOCKET, SO_NOSIGPIPE, &on, sizeof(on));122
}123
#endif124
#endif125
return static_cast<long long>(fd);126
}128
long long set_reuseaddr(long long fd) {129
int yes = 1;130
// optval is const void* on POSIX, const char* on Winsock — char* converts to both.131
return ::setsockopt(as_fd(fd), SOL_SOCKET, SO_REUSEADDR,132
reinterpret_cast<const char*>(&yes), sizeof(yes));133
}135
long long bind(long long fd, const std::string& host, long long port) {136
sockaddr_storage addr{};137
socklen_t len = 0;138
if (!resolve(host, port, addr, len)) return -1;139
return ::bind(as_fd(fd), reinterpret_cast<sockaddr*>(&addr), len);140
}142
long long listen(long long fd, long long backlog) {143
return ::listen(as_fd(fd), static_cast<int>(backlog));144
}146
long long connect(long long fd, const std::string& host, long long port) {147
sockaddr_storage addr{};148
socklen_t len = 0;149
if (!resolve(host, port, addr, len)) return -1;150
const int rc = ::connect(as_fd(fd), reinterpret_cast<sockaddr*>(&addr), len);151
if (rc == 0) {152
tune_stream_socket(fd); // every connected client socket gets the throughput options153
}154
return rc;155
}157
long long accept(long long fd) {158
const auto c = ::accept(as_fd(fd), nullptr, nullptr);159
#if defined(_WIN32)160
if (c == INVALID_SOCKET) return -1;161
#endif162
if (static_cast<long long>(c) >= 0) {163
tune_stream_socket(static_cast<long long>(c)); // and every accepted server socket164
}165
return static_cast<long long>(c);166
}168
long long local_port(long long fd) {169
sockaddr_storage addr{};170
socklen_t len = sizeof(addr);171
if (::getsockname(as_fd(fd), reinterpret_cast<sockaddr*>(&addr), &len) != 0) return -1;172
// sin_port sits at the same offset for IPv4/IPv6, and we only ever make173
// AF_INET sockets, so reading it back is safe.174
return ntohs(reinterpret_cast<sockaddr_in*>(&addr)->sin_port);175
}177
long long send(long long fd, const std::string& data) {178
// send() returns ssize_t on POSIX, int on Winsock — long long holds both.179
const long long n = ::send(as_fd(fd), data.data(),180
static_cast<int>(data.size()), MSG_NOSIGNAL);181
return n;182
}184
long long sendall(long long fd, const std::string& data) {185
std::size_t sent = 0;186
while (sent < data.size()) {187
const long long n = ::send(as_fd(fd), data.data() + sent,188
static_cast<int>(data.size() - sent), MSG_NOSIGNAL);189
if (n <= 0) return -1;190
sent += static_cast<std::size_t>(n);191
}192
return 0;193
}195
std::string recv(long long fd, long long bufsize) {196
if (bufsize <= 0) return std::string();197
// Read into a REUSED per-thread scratch buffer so we don't allocate + zero-fill a fresh198
// `bufsize` string on every call (a 64 KiB memset per recv on the download hot path). Only the199
// n bytes actually received are copied into the returned string.200
static thread_local std::vector<char> scratch;201
if (scratch.size() < static_cast<std::size_t>(bufsize)) {202
scratch.resize(static_cast<std::size_t>(bufsize));203
}204
const long long n = ::recv(as_fd(fd), scratch.data(), static_cast<int>(bufsize), 0);205
if (n <= 0) return std::string();206
std::string buf(scratch.data(), static_cast<std::size_t>(n));207
#if !defined(_WIN32) && defined(TCP_QUICKACK)208
// Re-arm quick-ACK: Linux clears it after each read, so without this the delayed-ACK stall209
// creeps back mid-transfer and the server's window stops growing.210
int one = 1;211
::setsockopt(as_fd(fd), IPPROTO_TCP, TCP_QUICKACK, &one, sizeof(one));212
#endif213
return buf;214
}216
long long close(long long fd) {217
#if defined(_WIN32)218
return ::closesocket(as_fd(fd));219
#else220
return ::close(as_fd(fd));221
#endif222
}224
long long shutdown(long long fd) {225
// Half-close both directions WITHOUT releasing the fd: a blocking recv() on226
// ANOTHER thread returns immediately (EOF). Used to wake a reader for a clean227
// shutdown; the fd is still owned by the caller and must be close()d afterwards.228
#if defined(_WIN32)229
return ::shutdown(as_fd(fd), SD_BOTH);230
#else231
return ::shutdown(as_fd(fd), SHUT_RDWR);232
#endif233
}235
long long tcp_listen(const std::string& host, long long port, long long backlog) {236
// If socket() fails (-1), the bind below fails too and we fall through to the237
// error path — no separate early return needed.238
long long fd = socket();239
set_reuseaddr(fd);240
if (bind(fd, host, port) != 0 || listen(fd, backlog) != 0) {241
close(fd);242
return -1;243
}244
return fd;245
}247
long long tcp_connect(const std::string& host, long long port) {248
long long fd = socket();249
if (connect(fd, host, port) != 0) {250
close(fd);251
return -1;252
}253
return fd;254
}256
long long set_timeout(long long fd, long long timeout_ms) {257
ensure_winsock();258
#if defined(_WIN32)259
const DWORD ms = timeout_ms > 0 ? static_cast<DWORD>(timeout_ms) : 0;260
if (::setsockopt(as_fd(fd), SOL_SOCKET, SO_RCVTIMEO,261
reinterpret_cast<const char*>(&ms), sizeof ms) != 0) return -1;262
if (::setsockopt(as_fd(fd), SOL_SOCKET, SO_SNDTIMEO,263
reinterpret_cast<const char*>(&ms), sizeof ms) != 0) return -1;264
#else265
timeval tv{};266
if (timeout_ms > 0) {267
tv.tv_sec = timeout_ms / 1000;268
tv.tv_usec = static_cast<suseconds_t>((timeout_ms % 1000) * 1000);269
}270
if (::setsockopt(as_fd(fd), SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof tv) != 0) return -1;271
if (::setsockopt(as_fd(fd), SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof tv) != 0) return -1;272
#endif273
return 0;274
}276
std::string last_error() {277
#if defined(_WIN32)278
return "Winsock error " + std::to_string(::WSAGetLastError());279
#else280
return std::strerror(errno);281
#endif282
}284
// ---- owning RAII connections ----285
// Each method forwards to the fd-based free function above; the value the guards add is286
// deterministic close() on scope exit, so a `with` block cannot leak the fd.288
Conn& Conn::operator=(Conn&& other) noexcept {289
if (this != &other) {290
if (fd_ >= 0) cheatah::socket::close(fd_);291
fd_ = other.fd_;292
other.fd_ = -1;293
}294
return *this;295
}296
Conn::~Conn() {297
if (fd_ >= 0) cheatah::socket::close(fd_);298
}299
long long Conn::send(const std::string& data) { return cheatah::socket::send(fd_, data); }300
long long Conn::sendall(const std::string& data) { return cheatah::socket::sendall(fd_, data); }301
std::string Conn::recv(long long bufsize) { return cheatah::socket::recv(fd_, bufsize); }302
long long Conn::set_timeout(long long timeout_ms) {303
return cheatah::socket::set_timeout(fd_, timeout_ms);304
}305
long long Conn::local_port() const { return cheatah::socket::local_port(fd_); }306
long long Conn::shutdown() { return cheatah::socket::shutdown(fd_); }307
long long Conn::close() {308
if (fd_ < 0) return -1;309
const long long rc = cheatah::socket::close(fd_);310
fd_ = -1;311
return rc;312
}314
Listener& Listener::operator=(Listener&& other) noexcept {315
if (this != &other) {316
if (fd_ >= 0) cheatah::socket::close(fd_);317
fd_ = other.fd_;318
other.fd_ = -1;319
}320
return *this;321
}322
Listener::~Listener() {323
if (fd_ >= 0) cheatah::socket::close(fd_);324
}325
Conn Listener::accept() { return Conn(cheatah::socket::accept(fd_)); }326
long long Listener::local_port() const { return cheatah::socket::local_port(fd_); }327
long long Listener::close() {328
if (fd_ < 0) return -1;329
const long long rc = cheatah::socket::close(fd_);330
fd_ = -1;331
return rc;332
}334
Conn open(const std::string& host, long long port) { return Conn(tcp_connect(host, port)); }335
Listener serve(const std::string& host, long long port, long long backlog) {336
return Listener(tcp_listen(host, port, backlog));337
}339
} // namespace cheatah::socket