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:

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:

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:


#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:

Appendices

The following appendices provide additional reference material:

Key Takeaways