Networking in C++
Network programming is file I/O with timing, failure, and protocol rules added. A socket is an endpoint; a protocol defines what bytes mean; the operating system handles the lower-level packet movement. Your C++ program is responsible for creating sockets, checking errors, sending and receiving bytes, and closing resources correctly.
Think of every network operation as unreliable by default. Connections can fail, reads can return partial data, writes can send fewer bytes than requested, and the other side can disappear at any time.
Table of Contents
- Network Programming Fundamentals
- Socket Basics
- TCP Client
- TCP Server
- UDP Communication
- Non-Blocking I/O
- Best Practices
Network Programming Fundamentals
The OSI and TCP/IP Models
┌──────────────────────────────────────────────────────────────────────────────┐
│ NETWORK LAYERS OVERVIEW │
├──────────────────────────────────────────────────────────────────────────────┤
│ │
│ OSI Model TCP/IP Model What C++ Sockets Use │
│ ───────── ──────────── ──────────────────── │
│ │
│ ┌─────────────┐ ┌─────────────┐ │
│ │ Application │ │ │ ◄── Your C++ Application │
│ ├─────────────┤ │ Application │ HTTP, FTP, Custom Protocol │
│ │ Presentation│ │ │ │
│ ├─────────────┤ └─────────────┘ │
│ │ Session │ │
│ ├─────────────┤ ┌─────────────┐ ◄── Socket API (TCP/UDP) │
│ │ Transport │ │ Transport │ SOCK_STREAM, SOCK_DGRAM │
│ ├─────────────┤ └─────────────┘ │
│ │ Network │ ┌─────────────┐ ◄── IP Addresses │
│ ├─────────────┤ │ Network │ AF_INET, AF_INET6 │
│ │ Data Link │ └─────────────┘ │
│ ├─────────────┤ ┌─────────────┐ ◄── Handled by OS │
│ │ Physical │ │Network Iface│ │
│ └─────────────┘ └─────────────┘ │
│ │
└──────────────────────────────────────────────────────────────────────────────┘
TCP vs UDP Comparison
┌─────────────────────────────────────────────────────────────────────────────┐
│ TCP vs UDP COMPARISON │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ TCP (Transmission Control Protocol) UDP (User Datagram Protocol) │
│ ──────────────────────────────────── ──────────────────────────── │
│ │
│ ┌───────────┐ Connection? ┌───────────┐ │
│ │ Client │◄────────────────────│ Server │ │
│ └─────┬─────┘ Established! └─────┬─────┘ │
│ │ │ │
│ │──── Data packet 1 ─────────────►│ │
│ │◄─── ACK ────────────────────────│ │
│ │──── Data packet 2 ─────────────►│ │
│ │◄─── ACK ────────────────────────│ │
│ │
│ ✅ Reliable (guaranteed delivery) ❌ No guarantee │
│ ✅ Ordered (packets arrive in order) ❌ May arrive out of order │
│ ✅ Error checked ✅ Error checked │
│ ❌ Slower (overhead) ✅ Faster (minimal overhead) │
│ ❌ Connection required ✅ Connectionless │
│ │
│ Use for: Use for: │
│ • Web (HTTP/HTTPS) • Video streaming │
│ • Email (SMTP) • Online gaming │
│ • File transfer (FTP) • DNS queries │
│ • Database connections • VoIP │
│ │
└─────────────────────────────────────────────────────────────────────────────┘
Client-Server Architecture
┌─────────────────────────────────────────────────────────────────────────────┐
│ CLIENT-SERVER COMMUNICATION FLOW │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ CLIENT SERVER │
│ ┌─────────────────┐ ┌─────────────────┐ │
│ │ │ │ │ │
│ │ 1. socket() │ │ 1. socket() │ │
│ │ Create │ │ Create │ │
│ │ │ │ │ │
│ └────────┬────────┘ │ 2. bind() │ │
│ │ │ Assign port │ │
│ │ │ │ │
│ │ │ 3. listen() │ │
│ │ │ Wait for │ │
│ │ │ connections │ │
│ │ └────────┬────────┘ │
│ │ │ │
│ ┌────────┴────────┐ ┌────────┴────────┐ │
│ │ 2. connect() │ ─── TCP Handshake ────► │ 4. accept() │ │
│ │ Connect to │ ◄────────────────────── │ Accept │ │
│ │ server │ │ connection │ │
│ └────────┬────────┘ └────────┬────────┘ │
│ │ │ │
│ ┌────────┴────────┐ ┌────────┴────────┐ │
│ │ 3. send() │ ──── Request ─────────► │ 5. recv() │ │
│ │ Send data │ │ Receive │ │
│ └────────┬────────┘ └────────┬────────┘ │
│ │ │ │
│ ┌────────┴────────┐ ┌────────┴────────┐ │
│ │ 4. recv() │ ◄─── Response ────────── │ 6. send() │ │
│ │ Receive │ │ Send reply │ │
│ └────────┬────────┘ └────────┬────────┘ │
│ │ │ │
│ ┌────────┴────────┐ ┌────────┴────────┐ │
│ │ 5. close() │ ◄── Connection Close ──► │ 7. close() │ │
│ │ Close │ │ Close │ │
│ └─────────────────┘ └─────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────────┘
Socket Basics
A socket is an endpoint for communication - like a phone number for your program.
Required Headers (Linux/Unix)
#include <sys/socket.h> // socket(), bind(), listen(), accept(), connect()
#include <netinet/in.h> // sockaddr_in, INADDR_ANY
#include <arpa/inet.h> // inet_pton(), inet_ntop(), htons()
#include <unistd.h> // close()
#include <netdb.h> // getaddrinfo(), gethostbyname()
#include <cstring> // memset()
Socket Types
// AF = Address Family, SOCK = Socket type
// TCP Socket (Stream) - Reliable, ordered, connection-based
int tcpSocket = socket(AF_INET, SOCK_STREAM, 0);
// UDP Socket (Datagram) - Fast, connectionless, no guarantees
int udpSocket = socket(AF_INET, SOCK_DGRAM, 0);
// IPv6 variants
int ipv6TcpSocket = socket(AF_INET6, SOCK_STREAM, 0);
int ipv6UdpSocket = socket(AF_INET6, SOCK_DGRAM, 0);
Socket Creation Diagram
┌─────────────────────────────────────────────────────────────────────────────┐
│ SOCKET CREATION PARAMETERS │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ socket(domain, type, protocol) │
│ │ │ │ │
│ │ │ └── Usually 0 (auto-select based on type) │
│ │ │ │
│ │ └── SOCK_STREAM ─── TCP (reliable, ordered) │
│ │ SOCK_DGRAM ──── UDP (fast, no guarantees) │
│ │ SOCK_RAW ────── Raw IP (advanced) │
│ │ │
│ └── AF_INET ─────── IPv4 (192.168.1.1) │
│ AF_INET6 ────── IPv6 (2001:db8::1) │
│ AF_UNIX ─────── Local (file-based IPC) │
│ │
│ Returns: Socket file descriptor (int) or -1 on error │
│ │
└─────────────────────────────────────────────────────────────────────────────┘
Address Structure
// IPv4 Address Structure
sockaddr_in addr;
memset(&addr, 0, sizeof(addr)); // Zero out the structure
addr.sin_family = AF_INET; // IPv4
addr.sin_port = htons(8080); // Port (host to network byte order)
addr.sin_addr.s_addr = INADDR_ANY; // Accept on any interface
// For specific IP address
inet_pton(AF_INET, "127.0.0.1", &addr.sin_addr);
// IPv6 Address Structure
sockaddr_in6 addr6;
memset(&addr6, 0, sizeof(addr6));
addr6.sin6_family = AF_INET6;
addr6.sin6_port = htons(8080);
addr6.sin6_addr = in6addr_any;
Byte Order Functions
// Network byte order = Big Endian (most significant byte first)
// Host byte order = May be Big or Little Endian
htons(port); // Host TO Network Short (16-bit, for ports)
htonl(addr); // Host TO Network Long (32-bit, for addresses)
ntohs(port); // Network TO Host Short
ntohl(addr); // Network TO Host Long
┌─────────────────────────────────────────────────────────────────────────────┐
│ BYTE ORDER MATTERS! │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ Port 8080 (0x1F90) in different byte orders: │
│ │
│ Little Endian (x86): Big Endian (Network): │
│ ┌─────┬─────┐ ┌─────┬─────┐ │
│ │ 90 │ 1F │ ──► │ 1F │ 90 │ │
│ └─────┴─────┘ htons() └─────┴─────┘ │
│ │
│ ALWAYS use htons() for ports and htonl() for addresses! │
│ │
└─────────────────────────────────────────────────────────────────────────────┘
TCP Client
Connect to Server
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <cstring>
int connectToServer(const char* host, int port) {
// Create socket
int sock = socket(AF_INET, SOCK_STREAM, 0);
if (sock < 0) return -1;
// Server address
sockaddr_in serverAddr{};
serverAddr.sin_family = AF_INET;
serverAddr.sin_port = htons(port);
inet_pton(AF_INET, host, &serverAddr.sin_addr);
// Connect
if (connect(sock, (sockaddr*)&serverAddr, sizeof(serverAddr)) < 0) {
close(sock);
return -1;
}
return sock;
}
void sendMessage(int sock, const string& msg) {
send(sock, msg.c_str(), msg.length(), 0);
}
string receiveMessage(int sock) {
char buffer[1024];
ssize_t bytes = recv(sock, buffer, sizeof(buffer) - 1, 0);
if (bytes > 0) {
buffer[bytes] = '\0';
return string(buffer);
}
return "";
}
TCP Server
Basic Server
int createServer(int port) {
// Create socket
int serverSock = socket(AF_INET, SOCK_STREAM, 0);
// Allow reuse
int opt = 1;
setsockopt(serverSock, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
// Bind
sockaddr_in addr{};
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = INADDR_ANY;
addr.sin_port = htons(port);
bind(serverSock, (sockaddr*)&addr, sizeof(addr));
// Listen
listen(serverSock, 10); // Backlog of 10
return serverSock;
}
void acceptClients(int serverSock) {
while (true) {
sockaddr_in clientAddr;
socklen_t clientLen = sizeof(clientAddr);
// Accept (blocks)
int clientSock = accept(serverSock,
(sockaddr*)&clientAddr,
&clientLen);
if (clientSock >= 0) {
// Handle client
handleClient(clientSock);
close(clientSock);
}
}
}
Multi-Threaded Server
void handleClient(int clientSock) {
char buffer[1024];
while (true) {
ssize_t bytes = recv(clientSock, buffer, sizeof(buffer), 0);
if (bytes <= 0) break;
// Echo back
send(clientSock, buffer, bytes, 0);
}
}
void acceptClientsThreaded(int serverSock) {
while (true) {
sockaddr_in clientAddr;
socklen_t clientLen = sizeof(clientAddr);
int clientSock = accept(serverSock,
(sockaddr*)&clientAddr, &clientLen);
if (clientSock >= 0) {
thread t(handleClient, clientSock);
t.detach(); // Run independently
}
}
}
UDP Communication
UDP Server
int createUDPServer(int port) {
int sock = socket(AF_INET, SOCK_DGRAM, 0);
sockaddr_in addr{};
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = INADDR_ANY;
addr.sin_port = htons(port);
bind(sock, (sockaddr*)&addr, sizeof(addr));
return sock;
}
void receiveUDP(int sock) {
char buffer[1024];
sockaddr_in clientAddr;
socklen_t clientLen = sizeof(clientAddr);
ssize_t bytes = recvfrom(sock, buffer, sizeof(buffer), 0,
(sockaddr*)&clientAddr, &clientLen);
if (bytes > 0) {
buffer[bytes] = '\0';
cout << "Received: " << buffer << endl;
// Reply
sendto(sock, "ACK", 3, 0,
(sockaddr*)&clientAddr, clientLen);
}
}
UDP Client
void sendUDP(const char* host, int port, const string& msg) {
int sock = socket(AF_INET, SOCK_DGRAM, 0);
sockaddr_in serverAddr{};
serverAddr.sin_family = AF_INET;
serverAddr.sin_port = htons(port);
inet_pton(AF_INET, host, &serverAddr.sin_addr);
sendto(sock, msg.c_str(), msg.length(), 0,
(sockaddr*)&serverAddr, sizeof(serverAddr));
close(sock);
}
Non-Blocking I/O
select()
#include <sys/select.h>
void selectLoop(int serverSock, vector<int>& clients) {
fd_set readSet;
while (true) {
FD_ZERO(&readSet);
FD_SET(serverSock, &readSet);
int maxFd = serverSock;
for (int fd : clients) {
FD_SET(fd, &readSet);
maxFd = max(maxFd, fd);
}
timeval timeout = {1, 0}; // 1 second
int ready = select(maxFd + 1, &readSet, nullptr, nullptr, &timeout);
if (ready > 0) {
// Check server socket
if (FD_ISSET(serverSock, &readSet)) {
int newClient = accept(serverSock, nullptr, nullptr);
clients.push_back(newClient);
}
// Check client sockets
for (int fd : clients) {
if (FD_ISSET(fd, &readSet)) {
// Read from client
}
}
}
}
}
poll()
#include <poll.h>
void pollLoop(int serverSock) {
vector<pollfd> fds;
fds.push_back({serverSock, POLLIN, 0});
while (true) {
int ready = poll(fds.data(), fds.size(), 1000); // 1s timeout
if (ready > 0) {
for (auto& pfd : fds) {
if (pfd.revents & POLLIN) {
if (pfd.fd == serverSock) {
// Accept new connection
} else {
// Read from client
}
}
}
}
}
}
Best Practices
Learner Notes
The first design decision is usually TCP versus UDP. Choose TCP when correctness, ordering, and reliability matter more than latency. Choose UDP when the newest packet matters more than guaranteed delivery, such as games, streaming, or telemetry. After that, define the application protocol: how a message starts, how its length is known, how errors are represented, and how the connection closes.
Most beginner bugs come from assuming one recv() call equals one full message.
TCP is a byte stream, not a message queue. You may receive half a message, one
message, or several messages together. Real programs keep a buffer and parse
complete frames from it.
Practice by writing a tiny echo protocol where each message starts with a 4-byte length followed by UTF-8 text. Then test what happens when the client sends two messages quickly or closes the connection mid-message.
✅ Do
// 1. Check return values
if (connect(sock, addr, len) < 0) {
perror("connect failed");
}
// 2. Set SO_REUSEADDR
int opt = 1;
setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
// 3. Close sockets properly
close(sock);
// 4. Handle partial reads/writes
ssize_t sendAll(int sock, const char* data, size_t len) {
size_t sent = 0;
while (sent < len) {
ssize_t n = send(sock, data + sent, len - sent, 0);
if (n <= 0) return -1;
sent += n;
}
return sent;
}
❌ Don't
// 1. Don't ignore byte order
addr.sin_port = 8080; // WRONG
addr.sin_port = htons(8080); // Correct
// 2. Don't assume recv fills buffer
char buffer[1024];
recv(sock, buffer, 1024, 0);
buffer[1023] = '\0'; // May not be null-terminated!
// 3. Don't forget to close
// Memory/resource leak if socket not closed
Quick Reference
// Create socket
socket(AF_INET, SOCK_STREAM, 0); // TCP
socket(AF_INET, SOCK_DGRAM, 0); // UDP
// Address setup
sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = htons(port);
inet_pton(AF_INET, "ip", &addr.sin_addr);
// TCP Server
bind(sock, addr, sizeof(addr));
listen(sock, backlog);
accept(sock, clientAddr, &len);
// TCP Client
connect(sock, addr, sizeof(addr));
// Send/Receive
send(sock, data, len, 0);
recv(sock, buffer, buflen, 0);
// UDP
sendto(sock, data, len, 0, addr, addrlen);
recvfrom(sock, buf, len, 0, addr, &addrlen);
// Cleanup
close(sock);
Compile & Run
g++ -std=c++17 -Wall examples.cpp -o examples && ./examples