C++ Network Programming Basics
C++ network programming is the foundation for building high-performance network applications, involving core concepts such as socket programming, protocol implementation, and concurrency handling.
Socket Basics
TCP socket example:
cpp#include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <unistd.h> #include <cstring> class TCPServer { private: int serverSocket; int port; bool running; public: TCPServer(int p) : port(p), running(false) { serverSocket = socket(AF_INET, SOCK_STREAM, 0); if (serverSocket < 0) { throw std::runtime_error("Failed to create socket"); } // Set address reuse int opt = 1; setsockopt(serverSocket, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)); } void start() { struct sockaddr_in serverAddr; memset(&serverAddr, 0, sizeof(serverAddr)); serverAddr.sin_family = AF_INET; serverAddr.sin_addr.s_addr = INADDR_ANY; serverAddr.sin_port = htons(port); if (bind(serverSocket, (struct sockaddr*)&serverAddr, sizeof(serverAddr)) < 0) { throw std::runtime_error("Failed to bind socket"); } if (listen(serverSocket, 5) < 0) { throw std::runtime_error("Failed to listen on socket"); } running = true; std::cout << "Server listening on port " << port << std::endl; while (running) { struct sockaddr_in clientAddr; socklen_t clientLen = sizeof(clientAddr); int clientSocket = accept(serverSocket, (struct sockaddr*)&clientAddr, &clientLen); if (clientSocket < 0) { if (running) { std::cerr << "Failed to accept connection" << std::endl; } continue; } char clientIP[INET_ADDRSTRLEN]; inet_ntop(AF_INET, &clientAddr.sin_addr, clientIP, INET_ADDRSTRLEN); std::cout << "Client connected: " << clientIP << std::endl; handleClient(clientSocket); } } void stop() { running = false; close(serverSocket); } private: void handleClient(int clientSocket) { char buffer[1024]; while (true) { ssize_t bytesRead = recv(clientSocket, buffer, sizeof(buffer) - 1, 0); if (bytesRead <= 0) { break; } buffer[bytesRead] = '\0'; std::cout << "Received: " << buffer << std::endl; // Send response const char* response = "Message received"; send(clientSocket, response, strlen(response), 0); } close(clientSocket); } }; // Usage int main() { try { TCPServer server(8080); server.start(); } catch (const std::exception& e) { std::cerr << "Error: " << e.what() << std::endl; } return 0; }
UDP socket example:
cppclass UDPServer { private: int serverSocket; int port; public: UDPServer(int p) : port(p) { serverSocket = socket(AF_INET, SOCK_DGRAM, 0); if (serverSocket < 0) { throw std::runtime_error("Failed to create UDP socket"); } struct sockaddr_in serverAddr; memset(&serverAddr, 0, sizeof(serverAddr)); serverAddr.sin_family = AF_INET; serverAddr.sin_addr.s_addr = INADDR_ANY; serverAddr.sin_port = htons(port); if (bind(serverSocket, (struct sockaddr*)&serverAddr, sizeof(serverAddr)) < 0) { throw std::runtime_error("Failed to bind UDP socket"); } } void receive() { char buffer[1024]; struct sockaddr_in clientAddr; socklen_t clientLen = sizeof(clientAddr); while (true) { ssize_t bytesRead = recvfrom(serverSocket, buffer, sizeof(buffer) - 1, 0, (struct sockaddr*)&clientAddr, &clientLen); if (bytesRead < 0) { std::cerr << "Failed to receive data" << std::endl; continue; } buffer[bytesRead] = '\0'; char clientIP[INET_ADDRSTRLEN]; inet_ntop(AF_INET, &clientAddr.sin_addr, clientIP, INET_ADDRSTRLEN); std::cout << "Received from " << clientIP << ": " << buffer << std::endl; // Send response const char* response = "UDP Response"; sendto(serverSocket, response, strlen(response), 0, (struct sockaddr*)&clientAddr, clientLen); } } ~UDPServer() { close(serverSocket); } };
Non-blocking I/O
Set non-blocking socket:
cpp#include <fcntl.h> void setNonBlocking(int socket) { int flags = fcntl(socket, F_GETFL, 0); if (flags < 0) { throw std::runtime_error("Failed to get socket flags"); } if (fcntl(socket, F_SETFL, flags | O_NONBLOCK) < 0) { throw std::runtime_error("Failed to set non-blocking mode"); } } // Usage int sock = socket(AF_INET, SOCK_STREAM, 0); setNonBlocking(sock);
Use select for multiplexing:
cpp#include <sys/select.h> class SelectServer { private: int serverSocket; int maxFd; fd_set readFds; public: SelectServer(int port) { serverSocket = socket(AF_INET, SOCK_STREAM, 0); setNonBlocking(serverSocket); struct sockaddr_in serverAddr; memset(&serverAddr, 0, sizeof(serverAddr)); serverAddr.sin_family = AF_INET; serverAddr.sin_addr.s_addr = INADDR_ANY; serverAddr.sin_port = htons(port); bind(serverSocket, (struct sockaddr*)&serverAddr, sizeof(serverAddr)); listen(serverSocket, 5); maxFd = serverSocket; FD_ZERO(&readFds); FD_SET(serverSocket, &readFds); } void run() { while (true) { fd_set tempFds = readFds; int activity = select(maxFd + 1, &tempFds, nullptr, nullptr, nullptr); if (activity < 0) { std::cerr << "Select error" << std::endl; continue; } // Check new connections if (FD_ISSET(serverSocket, &tempFds)) { handleNewConnection(); } // Check existing connections for (int fd = 0; fd <= maxFd; ++fd) { if (fd != serverSocket && FD_ISSET(fd, &tempFds)) { handleClientData(fd); } } } } private: void handleNewConnection() { struct sockaddr_in clientAddr; socklen_t clientLen = sizeof(clientAddr); int clientSocket = accept(serverSocket, (struct sockaddr*)&clientAddr, &clientLen); if (clientSocket < 0) { return; } setNonBlocking(clientSocket); FD_SET(clientSocket, &readFds); if (clientSocket > maxFd) { maxFd = clientSocket; } } void handleClientData(int fd) { char buffer[1024]; ssize_t bytesRead = recv(fd, buffer, sizeof(buffer) - 1, 0); if (bytesRead <= 0) { close(fd); FD_CLR(fd, &readFds); return; } buffer[bytesRead] = '\0'; std::cout << "Received: " << buffer << std::endl; } };
epoll High-performance I/O
epoll server example:
cpp#include <sys/epoll.h> class EpollServer { private: int serverSocket; int epollFd; static constexpr int MAX_EVENTS = 64; public: EpollServer(int port) { serverSocket = socket(AF_INET, SOCK_STREAM, 0); setNonBlocking(serverSocket); struct sockaddr_in serverAddr; memset(&serverAddr, 0, sizeof(serverAddr)); serverAddr.sin_family = AF_INET; serverAddr.sin_addr.s_addr = INADDR_ANY; serverAddr.sin_port = htons(port); bind(serverSocket, (struct sockaddr*)&serverAddr, sizeof(serverAddr)); listen(serverSocket, 5); epollFd = epoll_create1(0); if (epollFd < 0) { throw std::runtime_error("Failed to create epoll instance"); } struct epoll_event event; event.events = EPOLLIN | EPOLLET; // Edge-triggered event.data.fd = serverSocket; if (epoll_ctl(epollFd, EPOLL_CTL_ADD, serverSocket, &event) < 0) { throw std::runtime_error("Failed to add socket to epoll"); } } void run() { struct epoll_event events[MAX_EVENTS]; while (true) { int numEvents = epoll_wait(epollFd, events, MAX_EVENTS, -1); if (numEvents < 0) { std::cerr << "Epoll wait error" << std::endl; continue; } for (int i = 0; i < numEvents; ++i) { if (events[i].data.fd == serverSocket) { handleNewConnection(); } else { handleClientData(events[i].data.fd); } } } } private: void handleNewConnection() { struct sockaddr_in clientAddr; socklen_t clientLen = sizeof(clientAddr); int clientSocket = accept(serverSocket, (struct sockaddr*)&clientAddr, &clientLen); if (clientSocket < 0) { return; } setNonBlocking(clientSocket); struct epoll_event event; event.events = EPOLLIN | EPOLLET; event.data.fd = clientSocket; epoll_ctl(epollFd, EPOLL_CTL_ADD, clientSocket, &event); } void handleClientData(int fd) { char buffer[1024]; ssize_t bytesRead = recv(fd, buffer, sizeof(buffer) - 1, 0); if (bytesRead <= 0) { epoll_ctl(epollFd, EPOLL_CTL_DEL, fd, nullptr); close(fd); return; } buffer[bytesRead] = '\0'; std::cout << "Received: " << buffer << std::endl; const char* response = "HTTP/1.1 200 OK\r\nContent-Length: 13\r\n\r\nHello, World!"; send(fd, response, strlen(response), 0); } ~EpollServer() { close(serverSocket); close(epollFd); } };
HTTP Server Implementation
Simple HTTP server:
cppclass HTTPServer { private: int serverSocket; public: HTTPServer(int port) { serverSocket = socket(AF_INET, SOCK_STREAM, 0); int opt = 1; setsockopt(serverSocket, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)); struct sockaddr_in serverAddr; memset(&serverAddr, 0, sizeof(serverAddr)); serverAddr.sin_family = AF_INET; serverAddr.sin_addr.s_addr = INADDR_ANY; serverAddr.sin_port = htons(port); bind(serverSocket, (struct sockaddr*)&serverAddr, sizeof(serverAddr)); listen(serverSocket, 5); } void run() { while (true) { struct sockaddr_in clientAddr; socklen_t clientLen = sizeof(clientAddr); int clientSocket = accept(serverSocket, (struct sockaddr*)&clientAddr, &clientLen); if (clientSocket < 0) { continue; } handleRequest(clientSocket); close(clientSocket); } } private: void handleRequest(int clientSocket) { char buffer[4096]; ssize_t bytesRead = recv(clientSocket, buffer, sizeof(buffer) - 1, 0); if (bytesRead > 0) { buffer[bytesRead] = '\0'; std::cout << "Request:\n" << buffer << std::endl; // Parse HTTP request std::string request(buffer); std::string response = generateResponse(request); send(clientSocket, response.c_str(), response.length(), 0); } } std::string generateResponse(const std::string& request) { std::string response = "HTTP/1.1 200 OK\r\n"; response += "Content-Type: text/html\r\n"; response += "Connection: close\r\n"; std::string body = "<html><body><h1>Hello, World!</h1></body></html>"; response += "Content-Length: " + std::to_string(body.length()) + "\r\n"; response += "\r\n"; response += body; return response; } };
Best Practices
1. Use RAII to manage sockets
cppclass Socket { private: int fd; public: Socket(int domain, int type, int protocol) { fd = socket(domain, type, protocol); if (fd < 0) { throw std::runtime_error("Failed to create socket"); } } ~Socket() { if (fd >= 0) { close(fd); } } int getFd() const { return fd; } // Disable copy Socket(const Socket&) = delete; Socket& operator=(const Socket&) = delete; // Allow move Socket(Socket&& other) noexcept : fd(other.fd) { other.fd = -1; } };
2. Error handling
cppvoid checkError(int result, const std::string& message) { if (result < 0) { throw std::runtime_error(message + ": " + strerror(errno)); } } // Usage int sock = socket(AF_INET, SOCK_STREAM, 0); checkError(sock, "Failed to create socket");
3. Timeout settings
cppvoid setSocketTimeout(int socket, int seconds) { struct timeval timeout; timeout.tv_sec = seconds; timeout.tv_usec = 0; setsockopt(socket, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout)); setsockopt(socket, SOL_SOCKET, SO_SNDTIMEO, &timeout, sizeof(timeout)); }
4. Use thread pool to handle connections
cppclass ThreadPoolServer { private: TCPServer server; ThreadPool pool; public: ThreadPoolServer(int port, size_t threadCount) : server(port), pool(threadCount) {} void run() { server.setOnClientHandler([this](int clientSocket) { pool.enqueue([clientSocket]() { handleClient(clientSocket); }); }); server.start(); } };
Notes
- Always check return values of system calls
- Handle partial read/write situations
- Pay attention to byte order conversion (htons, ntohs, etc.)
- Consider using higher-level network libraries (like Boost.Asio)
- Be aware of thread safety in multithreaded environments
- Handle signal interrupts (EINTR)
- Consider using TLS/SSL for encrypted communication
- Watch out for resource leaks, ensure sockets are properly closed