Docs
README
Networking in C++
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
✅ 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