TCP/IP Networking
This chapter introduces the networking concepts you need to understand before using Corosio. If you’re already comfortable with TCP/IP, sockets, and the client-server model, you can skip to I/O Context.
The Internet Protocol Suite
The Internet uses a layered protocol architecture. Data passes through these layers when sent across a network:
| Layer | Purpose | Examples |
|---|---|---|
Application |
Your program’s logic |
HTTP, DNS, SMTP |
Transport |
Reliable delivery between processes |
TCP, UDP |
Internet |
Routing packets across networks |
IP (IPv4, IPv6) |
Link |
Physical transmission |
Ethernet, Wi-Fi |
Corosio operates at the Transport layer, providing TCP sockets. Your application logic sits above, sending and receiving data through Corosio’s abstractions.
TCP: The Transmission Control Protocol
TCP provides reliable, ordered, byte-stream delivery between two endpoints.
Reliable
TCP guarantees that data arrives. If a packet is lost, TCP automatically retransmits it. Your application never sees missing data.
Ordered
Data arrives in the order it was sent. If packets arrive out of order, TCP reassembles them before delivering to your application.
Byte Stream
TCP delivers a continuous stream of bytes with no inherent message boundaries. If you send "Hello" followed by "World", the receiver might read "HelloWorld" in one operation, or "Hel" then "loWorld", or any other split.
This has important implications for protocol design. You must define your own message framing—typically using:
-
Length prefixes: Send the message size before the message body
-
Delimiters: End each message with a special character (like
\n) -
Fixed-size messages: All messages have the same length
The Client-Server Model
Most network applications follow the client-server pattern:
┌────────┐ connect ┌────────┐
│ Client │ ───────────────→│ Server │
└────────┘ └────────┘
│
listening
on a port
Server: Listens on a well-known port, waiting for clients to connect. When a client connects, the server creates a new socket for that specific connection.
Client: Initiates a connection to a server’s IP address and port. Once connected, the client and server communicate as peers.
Connection Lifecycle
A TCP connection goes through these stages:
-
Server Setup
-
Create a socket
-
Bind to a local address and port
-
Begin listening for connections
-
-
Client Connects
-
Create a socket
-
Connect to the server’s address and port
-
TCP performs a three-way handshake
-
-
Data Exchange
-
Both sides can send and receive data
-
Data flows as a bidirectional byte stream
-
-
Connection Close
-
Either side initiates close
-
TCP performs a four-way handshake
-
Resources are released
-
Addresses and Ports
Every TCP connection is identified by four values:
-
Source IP address
-
Source port
-
Destination IP address
-
Destination port
IP Addresses
An IP address identifies a host on the network.
IPv4 addresses are 32 bits, written as four decimal numbers separated by
dots: 192.168.1.100. There are about 4 billion possible addresses.
IPv6 addresses are 128 bits, written as eight groups of hexadecimal digits
separated by colons: 2001:0db8:0000:0000:0000:0000:0000:0001 (often shortened
to 2001:db8::1).
Endpoints in Corosio
Corosio represents addresses and ports using the endpoint class:
// Port only (all interfaces)
corosio::endpoint server_ep(8080);
// IPv4 address and port
corosio::endpoint client_ep(
boost::urls::ipv4_address::loopback(), 8080);
// IPv6 address and port
corosio::endpoint ipv6_ep(
boost::urls::ipv6_address::loopback(), 8080);
Name Resolution
Humans use domain names like www.example.com, but TCP requires IP addresses.
The Domain Name System (DNS) translates names to addresses.
Corosio provides the resolver class for asynchronous DNS lookups:
corosio::resolver r(ioc);
auto [ec, results] = co_await r.resolve("www.example.com", "https");
for (auto const& entry : results)
{
auto ep = entry.get_endpoint();
// Try connecting to ep...
}
A single hostname may resolve to multiple addresses (for load balancing or redundancy). Your application should try each address until one succeeds.
TCP vs UDP
Corosio currently supports only TCP. Here’s why TCP is often the right choice:
| Property | TCP | UDP |
|---|---|---|
Reliability |
Guaranteed delivery |
Best effort |
Ordering |
Preserved |
Not guaranteed |
Connection |
Connection-oriented |
Connectionless |
Use cases |
Web, email, file transfer |
Video streaming, gaming, DNS |
TCP’s reliability and ordering come at the cost of latency (retransmissions take time) and overhead (connection setup, acknowledgments). For most applications—especially those involving requests and responses—TCP’s guarantees outweigh these costs.
What Corosio Provides
Corosio wraps the complexity of TCP programming in a coroutine-friendly API:
-
socket — Connect to servers, send and receive data
-
acceptor — Listen for and accept incoming connections
-
resolver — Translate hostnames to IP addresses
-
endpoint — Represent addresses and ports
All operations are asynchronous and return awaitables. You don’t manage raw socket handles or deal with platform-specific APIs directly.
Common Pitfalls
Partial Reads and Writes
TCP’s byte-stream nature means read_some() may return fewer bytes than you
requested, and write_some() may send fewer bytes than you provided.
Always loop or use composed operations (read(), write()) when you need
exact amounts:
// Wrong: might read less than buffer size
auto [ec, n] = co_await sock.read_some(buf);
// Right: reads until buffer is full or EOF
auto [ec, n] = co_await corosio::read(sock, buf);
Connection Refused
If no server is listening on the target port, connect fails with
connection_refused. Always handle this error—it’s common during development
and when servers restart.
Further Reading
For a deeper understanding of TCP/IP:
-
TCP/IP Illustrated, Volume 1 by W. Richard Stevens—the classic reference
-
Unix Network Programming by W. Richard Stevens—practical socket programming
Next Steps
-
Concurrent Programming — Coroutines and strands
-
I/O Context — The event loop
-
Sockets — Socket operations in detail