Ch11.10: Socket Files
Overview
fast_io treats sockets as files. Once you have a socket file
object, you use the familiar print, println, and
scan APIs to send and receive data — no separate socket
library needed. This section covers the two main socket types and the free
functions that create them.
::fast_io::native_socket_file
::fast_io::native_socket_file is a thin wrapper around the
platform's raw socket handle:
-
POSIX (Linux, macOS, FreeBSD, …): a regular file
descriptor. On POSIX systems, sockets are file descriptors, so
this is just an
intwrapper. Important: On POSIX systems (including Cygwin and MSYS2),::fast_io::native_socket_fileis actually the same type as::fast_io::native_file. This is because POSIX treats sockets as file descriptors, so there is no distinction between a "socket file" and a "regular file" at the type level. -
Windows: a
SOCKEThandle wrapper. Windows sockets are not file descriptors, so this type carries the nativeSOCKETvalue instead. Only on native Windows (Win32) doesnative_socket_filediffer fromnative_file.
native_socket_file gives you unbuffered, raw access. For
typical TCP I/O you will want the buffered variant below.
::fast_io::iobuf_socket_file
::fast_io::iobuf_socket_file wraps a
native_socket_file in a basic_iobuf, adding
user-space buffering. This is the standard type for TCP
I/O.
Important: On non-Windows platforms (including POSIX, Cygwin, and MSYS2),
::fast_io::iobuf_socket_file is actually the same type as
::fast_io::iobuf_file. This mirrors the relationship between
native_socket_file and native_file — on POSIX systems,
sockets are just file descriptors, so there is no type-level distinction.
#include <fast_io.h>
int main() {
using namespace ::fast_io::iomnp;
// Initialize networking on Windows (no-op on POSIX)
::fast_io::net_service net_svc;
// Resolve hostname and connect
auto dns = ::fast_io::native_dns_file(u8"example.com");
::fast_io::iobuf_socket_file sock{::fast_io::tcp_connect(::fast_io::to_ip(dns, 80u16))};
// Send an HTTP GET request using the normal print API
print(sock,
"GET / HTTP/1.1\r\n"
"Host: example.com\r\n"
"Connection: close\r\n"
"\r\n");
// Read the response line by line using the normal scan API
::fast_io::string line;
while (scan<::fast_io::getline>(sock, line)) {
println(line);
}
}
Because the socket is buffered, many small print calls are
coalesced into fewer system calls, just like with
iobuf_file.
::fast_io::u8http_header_buffer
::fast_io::u8http_header_buffer is a specialised buffer for
parsing HTTP response (or request) headers. It is scannable
via scan_context_define, so you can feed it directly into the
normal scan pipeline:
header_length— total number of bytes consumed by the header.request()— the request method (for request headers).code()— the HTTP status code (for response headers).reason()— the reason phrase (e.g."OK","Not Found").
The default internal buffer size is 4096 bytes, which is enough for the vast majority of real-world HTTP headers.
#include <fast_io.h>
int main() {
using namespace ::fast_io::iomnp;
::fast_io::net_service net_svc;
auto dns = ::fast_io::native_dns_file(u8"example.com");
::fast_io::iobuf_socket_file sock{::fast_io::tcp_connect(::fast_io::to_ip(dns, 80u16))};
print(sock,
"GET / HTTP/1.1\r\n"
"Host: example.com\r\n"
"Connection: close\r\n"
"\r\n");
// Parse the HTTP response header
::fast_io::u8http_header_buffer hdr;
scan(sock, hdr);
println("Status code: ", hdr.code());
println("Reason: ", hdr.reason());
println("Header len: ", hdr.header_length);
// The socket is now positioned at the start of the body.
// Continue reading the body with normal scan / print calls.
}
Free Functions for Sockets
The following free functions create and configure socket connections. They
return handles that can be used to construct
native_socket_file or iobuf_socket_file objects.
| Function | Description |
|---|---|
tcp_connect(host, port) |
Open a TCP connection to host:port. Returns a
native_socket_file.
|
tcp_listen(port) |
Create a TCP listening socket on the given port.
|
posix_bind(address) |
Bind a POSIX socket to the specified address. |
posix_listen(sockfd, backlog) |
Mark a bound POSIX socket as listening, with the given
backlog queue size.
|
posix_accept(sockfd) |
Accept an incoming connection on a listening POSIX socket. Returns a
new native_socket_file for the accepted connection.
|
TCP Server Example
Putting it all together, here is a minimal TCP server that listens on port 8080, accepts one connection, sends a greeting, and closes:
#include <fast_io.h>
int main() {
using namespace ::fast_io::iomnp;
::fast_io::net_service net_svc;
// Listen on port 8080
auto listener = ::fast_io::tcp_listen(8080u16);
// Accept one incoming connection
::fast_io::native_socket_file conn{::fast_io::posix_accept(listener)};
// Wrap in a buffered socket for convenient I/O
::fast_io::iobuf_socket_file buf_conn{::fast_io::io::transmit(conn)};
// Send a greeting using the normal print API
println(buf_conn, "Hello from fast_io server!\n");
}
::fast_io::net_service
On Windows, the networking subsystem must be initialized before any socket operations
can be performed. This is done by calling WSAStartup() at program start
and WSACleanup() at program exit. ::fast_io::net_service is
a RAII wrapper that handles this automatically:
-
Windows (Win32): Constructs by calling
WSAStartup()and destructs by callingWSACleanup(). Create one instance at the beginning ofmain()to ensure the networking subsystem is initialized for the lifetime of your program. -
POSIX and other systems:
::fast_io::net_serviceis a no-op type with no constructor or destructor overhead. You can still create an instance for portability, but it does nothing.
#include <fast_io.h>
#include <fast_io_driver/tcp.h>
int main() {
using namespace ::fast_io::iomnp;
// Initialize networking on Windows (no-op on POSIX)
::fast_io::net_service net_svc;
// Now safe to use socket operations
::fast_io::iobuf_socket_file sock{::fast_io::tcp_connect("example.com", 80u16)};
print(sock,
"GET / HTTP/1.1\r\n"
"Host: example.com\r\n"
"Connection: close\r\n"
"\r\n");
::fast_io::string line;
while (scan<::fast_io::getline>(sock, line)) {
println(line);
}
}
Always create a ::fast_io::net_service object at the start of main()
if your program uses sockets. This ensures portability across platforms — on Windows it
initializes the networking subsystem, and on POSIX it is a harmless no-op.
DNS Resolution
When you need to connect to a host by name (rather than by IP address), you must first
resolve the hostname to an IP address. fast_io provides
::fast_io::native_dns_file for this purpose. It wraps the OS’s DNS
resolution facilities (getaddrinfo() on POSIX, GetAddrInfoW() on
Windows NT).
native_dns_file is iterable — it may resolve to multiple IP addresses
(both IPv4 and IPv6). You can iterate over all of them, or use the first result with
::fast_io::to_ip() to get an IP address with a port number, ready for
tcp_connect().
#include <fast_io.h>
int main() {
using namespace ::fast_io::iomnp;
::fast_io::net_service net_svc;
// Resolve hostname to IP addresses
::fast_io::native_dns_file dns(u8"example.com");
// Option 1: Use the first resolved address with a port
::fast_io::iobuf_socket_file sock{::fast_io::tcp_connect(::fast_io::to_ip(dns, 80u16))};
print(sock, "GET / HTTP/1.1\r\nHost: example.com\r\n\r\n");
// Option 2: Iterate over all resolved addresses
for (auto const& ent : dns) {
println("Resolved IP: ", ::fast_io::to_ip_address(ent));
}
}
The key functions are:
::fast_io::native_dns_file(hostname)— resolve a hostname to IP addresses::fast_io::to_ip(dns_entry, port)— convert a DNS entry to an IP address with port::fast_io::to_ip_address(dns_entry)— convert a DNS entry to just an IP address (no port)
Appendices
The following appendices provide additional reference material:
- Ch11.10.1: Network Basics — OSI model, IP addresses, common ports
- Ch11.10.2: DNS — DNS records, DoH, DoT
- Ch11.10.3: Modern Network Protocols — HTTP/1.1/2/3, QUIC, WebSocket
- Ch11.10.4: Protocol Support in fast_io — TCP, UDP, Unix sockets, etc.
Key Takeaways
-
::fast_io::native_socket_filewraps the raw socket handle — a file descriptor on POSIX, aSOCKETon Windows. On non-Windows platforms (including Cygwin/MSYS2), it is the same type as::fast_io::native_file. -
::fast_io::iobuf_socket_fileadds user-space buffering and is the standard type for TCP I/O. On non-Windows platforms, it is the same type as::fast_io::iobuf_file. -
Use
::fast_io::native_dns_fileto resolve hostnames to IP addresses. Combine with::fast_io::to_ip(dns, port)andtcp_connect()to connect by hostname. -
::fast_io::u8http_header_bufferparses HTTP headers viascan. It exposesheader_length,request(),code(), andreason(). Default buffer size is 4096 bytes. -
Free functions:
tcp_connect(),tcp_listen(),posix_bind(),posix_listen(),posix_accept(). -
::fast_io::net_serviceis a RAII type that initializes the Windows networking subsystem (WSAStartup/WSACleanup). On POSIX systems it is a no-op. Always create one at the start ofmain()for portability. -
Sockets are files in
fast_io— the sameprint,scan, and customisation points you already know work for sockets too.