Skip to content

C++ Network Programming

Overview

Network programming is an important part of modern applications. Although the C++ standard library currently does not have built-in network support, network programming can be done through Socket API, third-party libraries like Boost.Asio, etc. This chapter introduces Socket basics, TCP/UDP programming, asynchronous network programming, etc.

🔌 Socket Basics

TCP Client and Server

cpp
#include <iostream>
#include <string>
#include <vector>
#include <thread>
#include <chrono>

#ifdef _WIN32
    #include <winsock2.h>
    #include <ws2tcpip.h>
    #pragma comment(lib, "ws2_32.lib")
    typedef int socklen_t;
    #define close closesocket
#else
    #include <sys/socket.h>
    #include <netinet/in.h>
    #include <arpa/inet.h>
    #include <unistd.h>
    #include <netdb.h>
#endif

class SocketUtils {
public:
    static bool initializeWinsock() {
#ifdef _WIN32
        WSADATA wsaData;
        int result = WSAStartup(MAKEWORD(2, 2), &wsaData);
        if (result != 0) {
            std::cerr << "WSAStartup failed: " << result << std::endl;
            return false;
        }
#endif
        return true;
    }
    
    static void cleanupWinsock() {
#ifdef _WIN32
        WSACleanup();
#endif
    }
    
    static std::string getLastErrorString() {
#ifdef _WIN32
        int error = WSAGetLastError();
        char* msg = nullptr;
        FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
                      nullptr, error, 0, (LPSTR)&msg, 0, nullptr);
        std::string result = msg ? msg : "Unknown error";
        LocalFree(msg);
        return result;
#else
        return std::string(strerror(errno));
#endif
    }
};

class TCPServer {
private:
    int server_socket_;
    int port_;
    bool running_;
    
public:
    TCPServer(int port) : port_(port), running_(false), server_socket_(-1) {}
    
    ~TCPServer() {
        stop();
    }
    
    bool start() {
        if (!SocketUtils::initializeWinsock()) {
            return false;
        }
        
        // Create socket
        server_socket_ = socket(AF_INET, SOCK_STREAM, 0);
        if (server_socket_ < 0) {
            std::cerr << "Failed to create socket: " << SocketUtils::getLastErrorString() << std::endl;
            return false;
        }
        
        // Set address reuse
        int opt = 1;
        setsockopt(server_socket_, SOL_SOCKET, SO_REUSEADDR, (char*)&opt, sizeof(opt));
        
        // Bind address
        sockaddr_in server_addr{};
        server_addr.sin_family = AF_INET;
        server_addr.sin_addr.s_addr = INADDR_ANY;
        server_addr.sin_port = htons(port_);
        
        if (bind(server_socket_, (sockaddr*)&server_addr, sizeof(server_addr)) < 0) {
            std::cerr << "Bind failed: " << SocketUtils::getLastErrorString() << std::endl;
            close(server_socket_);
            return false;
        }
        
        // Listen
        if (listen(server_socket_, 5) < 0) {
            std::cerr << "Listen failed: " << SocketUtils::getLastErrorString() << std::endl;
            close(server_socket_);
            return false;
        }
        
        running_ = true;
        std::cout << "Server started on port " << port_ << std::endl;
        return true;
    }
    
    void run() {
        while (running_) {
            sockaddr_in client_addr{};
            socklen_t client_len = sizeof(client_addr);
            
            int client_socket = accept(server_socket_, (sockaddr*)&client_addr, &client_len);
            if (client_socket < 0) {
                if (running_) {
                    std::cerr << "Accept failed: " << SocketUtils::getLastErrorString() << std::endl;
                }
                continue;
            }
            
            // Get client address
            char client_ip[INET_ADDRSTRLEN];
            inet_ntop(AF_INET, &client_addr.sin_addr, client_ip, INET_ADDRSTRLEN);
            std::cout << "Client connected: " << client_ip << ":" << ntohs(client_addr.sin_port) << std::endl;
            
            // Handle client in new thread
            std::thread client_thread(&TCPServer::handleClient, this, client_socket);
            client_thread.detach();
        }
    }
    
    void stop() {
        running_ = false;
        if (server_socket_ >= 0) {
            close(server_socket_);
            server_socket_ = -1;
        }
        SocketUtils::cleanupWinsock();
    }
    
private:
    void handleClient(int client_socket) {
        char buffer[1024];
        
        while (running_) {
            int bytes_received = recv(client_socket, buffer, sizeof(buffer) - 1, 0);
            if (bytes_received <= 0) {
                break;
            }
            
            buffer[bytes_received] = '\0';
            std::string message(buffer);
            
            std::cout << "Received message: " << message << std::endl;
            
            // Echo server: send same message back
            std::string response = "Echo: " + message;
            send(client_socket, response.c_str(), response.length(), 0);
        }
        
        close(client_socket);
        std::cout << "Client disconnected" << std::endl;
    }
};

class TCPClient {
private:
    int client_socket_;
    
public:
    TCPClient() : client_socket_(-1) {}
    
    ~TCPClient() {
        disconnect();
    }
    
    bool connect(const std::string& host, int port) {
        if (!SocketUtils::initializeWinsock()) {
            return false;
        }
        
        // Create socket
        client_socket_ = socket(AF_INET, SOCK_STREAM, 0);
        if (client_socket_ < 0) {
            std::cerr << "Failed to create socket: " << SocketUtils::getLastErrorString() << std::endl;
            return false;
        }
        
        // Resolve host address
        sockaddr_in server_addr{};
        server_addr.sin_family = AF_INET;
        server_addr.sin_port = htons(port);
        
        if (inet_pton(AF_INET, host.c_str(), &server_addr.sin_addr) <= 0) {
            // Try to resolve by hostname
            hostent* he = gethostbyname(host.c_str());
            if (!he) {
                std::cerr << "Hostname resolution failed: " << host << std::endl;
                close(client_socket_);
                return false;
            }
            memcpy(&server_addr.sin_addr, he->h_addr_list[0], he->h_length);
        }
        
        // Connect to server
        if (::connect(client_socket_, (sockaddr*)&server_addr, sizeof(server_addr)) < 0) {
            std::cerr << "Connection failed: " << SocketUtils::getLastErrorString() << std::endl;
            close(client_socket_);
            return false;
        }
        
        std::cout << "Connected to " << host << ":" << port << std::endl;
        return true;
    }
    
    bool sendMessage(const std::string& message) {
        if (client_socket_ < 0) {
            return false;
        }
        
        int bytes_sent = send(client_socket_, message.c_str(), message.length(), 0);
        return bytes_sent > 0;
    }
    
    std::string receiveMessage() {
        if (client_socket_ < 0) {
            return "";
        }
        
        char buffer[1024];
        int bytes_received = recv(client_socket_, buffer, sizeof(buffer) - 1, 0);
        
        if (bytes_received > 0) {
            buffer[bytes_received] = '\0';
            return std::string(buffer);
        }
        
        return "";
    }
    
    void disconnect() {
        if (client_socket_ >= 0) {
            close(client_socket_);
            client_socket_ = -1;
            SocketUtils::cleanupWinsock();
        }
    }
};

UDP Programming

cpp
#include <iostream>
#include <string>
#include <vector>

class UDPServer {
private:
    int socket_;
    int port_;
    bool running_;
    
public:
    UDPServer(int port) : port_(port), running_(false), socket_(-1) {}
    
    ~UDPServer() {
        stop();
    }
    
    bool start() {
        if (!SocketUtils::initializeWinsock()) {
            return false;
        }
        
        // Create UDP socket
        socket_ = socket(AF_INET, SOCK_DGRAM, 0);
        if (socket_ < 0) {
            std::cerr << "Failed to create UDP socket: " << SocketUtils::getLastErrorString() << std::endl;
            return false;
        }
        
        // Bind address
        sockaddr_in server_addr{};
        server_addr.sin_family = AF_INET;
        server_addr.sin_addr.s_addr = INADDR_ANY;
        server_addr.sin_port = htons(port_);
        
        if (bind(socket_, (sockaddr*)&server_addr, sizeof(server_addr)) < 0) {
            std::cerr << "UDP bind failed: " << SocketUtils::getLastErrorString() << std::endl;
            close(socket_);
            return false;
        }
        
        running_ = true;
        std::cout << "UDP server started on port " << port_ << std::endl;
        return true;
    }
    
    void run() {
        char buffer[1024];
        sockaddr_in client_addr{};
        socklen_t client_len = sizeof(client_addr);
        
        while (running_) {
            int bytes_received = recvfrom(socket_, buffer, sizeof(buffer) - 1, 0,
                                        (sockaddr*)&client_addr, &client_len);
            
            if (bytes_received < 0) {
                if (running_) {
                    std::cerr << "UDP receive failed: " << SocketUtils::getLastErrorString() << std::endl;
                }
                continue;
            }
            
            buffer[bytes_received] = '\0';
            
            char client_ip[INET_ADDRSTRLEN];
            inet_ntop(AF_INET, &client_addr.sin_addr, client_ip, INET_ADDRSTRLEN);
            
            std::cout << "Received UDP message from " << client_ip << ":" << ntohs(client_addr.sin_port)
                      << " - " << buffer << std::endl;
            
            // Send reply
            std::string response = "UDP Echo: " + std::string(buffer);
            sendto(socket_, response.c_str(), response.length(), 0,
                   (sockaddr*)&client_addr, client_len);
        }
    }
    
    void stop() {
        running_ = false;
        if (socket_ >= 0) {
            close(socket_);
            socket_ = -1;
        }
        SocketUtils::cleanupWinsock();
    }
};

class UDPClient {
private:
    int socket_;
    sockaddr_in server_addr_;
    
public:
    UDPClient() : socket_(-1) {}
    
    ~UDPClient() {
        disconnect();
    }
    
    bool connect(const std::string& host, int port) {
        if (!SocketUtils::initializeWinsock()) {
            return false;
        }
        
        // Create UDP socket
        socket_ = socket(AF_INET, SOCK_DGRAM, 0);
        if (socket_ < 0) {
            std::cerr << "Failed to create UDP socket: " << SocketUtils::getLastErrorString() << std::endl;
            return false;
        }
        
        // Set server address
        server_addr_.sin_family = AF_INET;
        server_addr_.sin_port = htons(port);
        
        if (inet_pton(AF_INET, host.c_str(), &server_addr_.sin_addr) <= 0) {
            hostent* he = gethostbyname(host.c_str());
            if (!he) {
                std::cerr << "UDP hostname resolution failed: " << host << std::endl;
                close(socket_);
                return false;
            }
            memcpy(&server_addr_.sin_addr, he->h_addr_list[0], he->h_length);
        }
        
        std::cout << "UDP client ready, target: " << host << ":" << port << std::endl;
        return true;
    }
    
    bool sendMessage(const std::string& message) {
        if (socket_ < 0) {
            return false;
        }
        
        int bytes_sent = sendto(socket_, message.c_str(), message.length(), 0,
                               (sockaddr*)&server_addr_, sizeof(server_addr_));
        return bytes_sent > 0;
    }
    
    std::string receiveMessage() {
        if (socket_ < 0) {
            return "";
        }
        
        char buffer[1024];
        sockaddr_in from_addr{};
        socklen_t from_len = sizeof(from_addr);
        
        int bytes_received = recvfrom(socket_, buffer, sizeof(buffer) - 1, 0,
                                     (sockaddr*)&from_addr, &from_len);
        
        if (bytes_received > 0) {
            buffer[bytes_received] = '\0';
            return std::string(buffer);
        }
        
        return "";
    }
    
    void disconnect() {
        if (socket_ >= 0) {
            close(socket_);
            socket_ = -1;
            SocketUtils::cleanupWinsock();
        }
    }
};

🌐 HTTP Client Example

Simple HTTP Request

cpp
#include <iostream>
#include <string>
#include <sstream>
#include <map>

class HTTPClient {
private:
    struct HTTPResponse {
        int status_code;
        std::map<std::string, std::string> headers;
        std::string body;
    };
    
public:
    static HTTPResponse get(const std::string& host, int port, const std::string& path) {
        HTTPResponse response;
        
        TCPClient client;
        if (!client.connect(host, port)) {
            response.status_code = -1;
            return response;
        }
        
        // Construct HTTP GET request
        std::ostringstream request;
        request << "GET " << path << " HTTP/1.1\r\n";
        request << "Host: " << host << "\r\n";
        request << "Connection: close\r\n";
        request << "\r\n";
        
        std::string request_str = request.str();
        std::cout << "Sending HTTP request:\n" << request_str << std::endl;
        
        if (!client.sendMessage(request_str)) {
            response.status_code = -2;
            return response;
        }
        
        // Receive response
        std::string full_response;
        std::string chunk;
        
        while (!(chunk = client.receiveMessage()).empty()) {
            full_response += chunk;
        }
        
        // Parse HTTP response
        parseHTTPResponse(full_response, response);
        
        return response;
    }
    
private:
    static void parseHTTPResponse(const std::string& raw_response, HTTPResponse& response) {
        std::istringstream stream(raw_response);
        std::string line;
        
        // Parse status line
        if (std::getline(stream, line)) {
            std::istringstream status_stream(line);
            std::string version;
            status_stream >> version >> response.status_code;
        }
        
        // Parse headers
        while (std::getline(stream, line) && line != "\r" && !line.empty()) {
            size_t colon_pos = line.find(':');
            if (colon_pos != std::string::npos) {
                std::string key = line.substr(0, colon_pos);
                std::string value = line.substr(colon_pos + 1);
                
                // Trim leading/trailing whitespace
                value.erase(0, value.find_first_not_of(" \t"));
                value.erase(value.find_last_not_of(" \t\r") + 1);
                
                response.headers[key] = value;
            }
        }
        
        // Read body
        std::ostringstream body_stream;
        while (std::getline(stream, line)) {
            body_stream << line << "\n";
        }
        response.body = body_stream.str();
    }
};

// Network programming example
class NetworkDemo {
public:
    static void tcpEchoDemo() {
        std::cout << "=== TCP Echo Server Demo ===" << std::endl;
        
        // Start server in background thread
        TCPServer server(8080);
        
        std::thread server_thread([&server]() {
            if (server.start()) {
                server.run();
            }
        });
        
        // Wait for server to start
        std::this_thread::sleep_for(std::chrono::milliseconds(500));
        
        // Client connection and testing
        TCPClient client;
        if (client.connect("127.0.0.1", 8080)) {
            for (int i = 0; i < 3; ++i) {
                std::string message = "Test message " + std::to_string(i + 1);
                
                if (client.sendMessage(message)) {
                    std::string response = client.receiveMessage();
                    std::cout << "Sent: " << message << std::endl;
                    std::cout << "Received: " << response << std::endl;
                }
                
                std::this_thread::sleep_for(std::chrono::milliseconds(100));
            }
        }
        
        server.stop();
        if (server_thread.joinable()) {
            server_thread.join();
        }
    }
    
    static void udpEchoDemo() {
        std::cout << "\n=== UDP Echo Server Demo ===" << std::endl;
        
        // Start UDP server
        UDPServer server(8081);
        
        std::thread server_thread([&server]() {
            if (server.start()) {
                server.run();
            }
        });
        
        std::this_thread::sleep_for(std::chrono::milliseconds(500));
        
        // UDP client testing
        UDPClient client;
        if (client.connect("127.0.0.1", 8081)) {
            for (int i = 0; i < 3; ++i) {
                std::string message = "UDP Test " + std::to_string(i + 1);
                
                if (client.sendMessage(message)) {
                    std::string response = client.receiveMessage();
                    std::cout << "Sent: " << message << std::endl;
                    std::cout << "Received: " << response << std::endl;
                }
                
                std::this_thread::sleep_for(std::chrono::milliseconds(100));
            }
        }
        
        server.stop();
        if (server_thread.joinable()) {
            server_thread.join();
        }
    }
    
    static void httpClientDemo() {
        std::cout << "\n=== HTTP Client Demo ===" << std::endl;
        
        // Note: This requires an actual HTTP server
        auto response = HTTPClient::get("httpbin.org", 80, "/get");
        
        std::cout << "HTTP Status Code: " << response.status_code << std::endl;
        std::cout << "Response Headers:" << std::endl;
        for (const auto& header : response.headers) {
            std::cout << "  " << header.first << ": " << header.second << std::endl;
        }
        std::cout << "Response Body:\n" << response.body << std::endl;
    }
};

int main() {
    NetworkDemo::tcpEchoDemo();
    NetworkDemo::udpEchoDemo();
    
    // HTTP demo requires network connection
    // NetworkDemo::httpClientDemo();
    
    return 0;
}

Summary

Although C++ doesn't have direct standard library support for network programming, powerful network functionality can be implemented through the Socket API:

Core Concepts

  • Socket: Network communication endpoint
  • TCP: Reliable connection-oriented protocol
  • UDP: Fast connectionless protocol
  • Client/Server: Network application architecture pattern

Programming Models

ProtocolFeaturesUse Cases
TCPReliable, ordered, connection-orientedWeb services, file transfer
UDPFast, connectionless, unreliableGaming, real-time communication
HTTPApplication layer protocolWeb applications, APIs

Best Practices

  • Error handling: Fully handle network errors
  • Non-blocking I/O: Avoid program blocking
  • Multithreading: Concurrently handle multiple connections
  • Resource management: Promptly close sockets and clean up resources
  • Cross-platform: Handle differences between different systems

Advanced Techniques

  • Asynchronous I/O: Use select/poll/epoll
  • Thread pools: Reuse threads to handle connections
  • Connection pools: Reuse network connections
  • SSL/TLS: Secure communication encryption

Third-party Libraries

  • Boost.Asio: Asynchronous network programming library
  • libcurl: HTTP/HTTPS client library
  • Poco: Network and application framework
  • Qt Network: Qt framework network module

Important Notes

  • Byte order: Network byte order conversion
  • Buffers: Reasonably set buffer sizes
  • Timeouts: Set appropriate timeout values
  • Security: Protect against network attacks
  • Performance: Optimize network I/O performance

Network programming is the foundation for building distributed applications and services, and mastering these technologies is crucial for modern C++ development.

Content is for learning and research only.