This episode covers networking basics in C++: TCP and UDP socket programming with BSD sockets, client-server communication and simple protocol design, byte order and address structures, error handling, as well as cross-platform considerations on Windows and Linux.

Almost every modern system communicates over a network — HTTP, databases, message queues, even multiplayer games. In C++, network communication uses sockets: an interface managed by the operating system for sending and receiving data. The most basic sockets come from BSD sockets, the standard used on Linux and adopted by Windows through Winsock.
Episode 13 builds your networking foundation: the socket concept and the client-server model, simple TCP server and client, the importance of byte order, address structures, error handling, and the API differences between Windows and Linux.
A socket is a communication endpoint identified by an IP address and a port. TCP provides a reliable, ordered connection, suitable for data that must not be lost. UDP is connectionless, fast, but can lose packets, suitable for streaming and games. The most common model is client-server: the server listens on a port, the client connects to that port.
The full workflow:
socket() -> bind() -> listen() -> accept() -> recv()/send() [server]
socket() -> connect() -> send()/recv() [client]The flow socket() -> bind() -> listen() -> accept() is the server socket's lifecycle, while a client only needs socket() -> connect() and then exchanges data. Both use the headers <sys/socket.h>, <netinet/in.h>, and <arpa/inet.h> on Linux.
The server creates a socket with socket(), binds it to a port with bind(), listens with listen(), then accepts connections with accept():
cat > server.cpp <<'EOF'
#include <iostream>
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>
int main() {
int fd = socket(AF_INET, SOCK_STREAM, 0);
if (fd < 0) {
std::cerr << "gagal membuat socket\n";
return 1;
}
sockaddr_in addr{};
addr.sin_family = AF_INET;
addr.sin_port = htons(8080);
addr.sin_addr.s_addr = INADDR_ANY;
if (bind(fd, reinterpret_cast<sockaddr*>(&addr), sizeof(addr)) < 0) {
std::cerr << "gagal bind\n";
return 1;
}
listen(fd, 5);
sockaddr_in client{};
socklen_t len = sizeof(client);
int cfd = accept(fd, reinterpret_cast<sockaddr*>(&client), &len);
std::cout << "Client terhubung\n";
char buf[256];
int n = recv(cfd, buf, sizeof(buf), 0);
buf[n] = '\0';
std::cout << "Terima: " << buf << "\n";
send(cfd, "pong", 4, 0);
close(cfd);
close(fd);
}
EOF
g++ -std=c++20 server.cpp -o serversocket(AF_INET, SOCK_STREAM, 0) creates a TCP socket. htons(8080) converts the port to network byte order. bind(fd, ...) binds the socket to port 8080, listen(fd, 5) allows 5 connections in the queue, and accept() waits for an incoming connection. The <unistd.h> header provides close.
The client creates a socket then connects to the server's address and port:
cat > client.cpp <<'EOF'
#include <iostream>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
int main() {
int fd = socket(AF_INET, SOCK_STREAM, 0);
sockaddr_in addr{};
addr.sin_family = AF_INET;
addr.sin_port = htons(8080);
inet_pton(AF_INET, "127.0.0.1", &addr.sin_addr);
if (connect(fd, reinterpret_cast<sockaddr*>(&addr), sizeof(addr)) < 0) {
std::cerr << "koneksi gagal\n";
return 1;
}
send(fd, "ping", 4, 0);
char buf[256];
int n = recv(fd, buf, sizeof(buf), 0);
buf[n] = '\0';
std::cout << "Server: " << buf << "\n";
close(fd);
}
EOF
g++ -std=c++20 client.cpp -o clientinet_pton(AF_INET, "127.0.0.1", &addr.sin_addr) converts an address string to binary, and connect(fd, ...) connects to the server on port 8080. The address 127.0.0.1 is loopback — the server and client are on the same machine.
Run the server, then test it with netcat without writing a client: the command nc 127.0.0.1 8080 uses netcat to test the server. Type text, press Enter, and the server receives it. Netcat is a network debugging tool you must master.
Machines store numbers in different byte orders: little-endian (common on x86) stores the least significant byte first, big-endian (network order) the opposite. Networks use big-endian, so all values are converted when sent and received with htons and htonl (host to network) and ntohs and ntohl (the reverse). Without the htons(port) conversion, the port is read incorrectly on machines with a different byte order — a classic bug in inter-architecture communication.
sockaddr_in stores an IPv4 address with three important fields: sin_family (AF_INET), sin_port (the port in network order), and sin_addr (the binary IP address). For IPv6, use sockaddr_in6. Production applications usually wrap all of this in an abstraction so they don't touch per-OS details.
Every socket function returns a value that must be checked. errno explains the cause of the failure, and the pattern strerror(errno) turns it into a descriptive message like Address already in use. Common errors: the port is already used by another process, or binding without privileges to a port below 1024.
On Windows, sockets use Winsock: the <winsock2.h> header, a WSAStartup call before using sockets, closesocket instead of close, and linking the ws2_32 library. A cross-platform abstraction picks the header during preprocessing:
#ifdef _WIN32
#include <winsock2.h>
#else
#include <sys/socket.h>
#include <unistd.h>
#endifThe #ifdef _WIN32 block selects the header according to the platform during preprocessing. This is the conditional compilation pattern you'll learn more deeply in episode 20.
Tip
For production applications, consider a networking library like Boost.Asio or the standalone asio that unify Windows and Linux.
Here's what to take away:
htons and htonl convert values to network byte order.strerror(errno).In the next episode, episode 14, we'll discuss secure coding and safety — memory safety and buffer overflow mitigation, safe use of C string APIs versus std::string, input validation and sanitization with boundary checks, as well as cybersecurity best practices for C++ code.