Building a Simple C++ TCP Server from Scratch with cppbox

This article explains why the author created a custom C++ TCP server framework, outlines the required prerequisites and coding standards, and walks through a minimal echo server implementation together with the key cppbox library interfaces and usage examples.

360 Tech Engineering
360 Tech Engineering
360 Tech Engineering
Building a Simple C++ TCP Server from Scratch with cppbox

Starting in 2023 the author began developing a TCP server in C++ and now shares the complete process of building the framework, named cppbox, from zero to a functional echo server.

Why build a custom framework? Existing open‑source solutions are abundant, but the author needed a unified, lightweight library for internal team use, wanted deep understanding of core networking details, found the development effort manageable, and saw value in accumulating technical knowledge.

Prerequisite knowledge includes basic programming concepts and network programming fundamentals, with references to earlier tutorial articles and the Muduo library source code.

Coding standards follow the Google C++ Style Guide, linked in the article.

Minimal echo server program (the simplest possible TCP server) is provided below:

#include <iostream>
#include <sys/socket.h>
#include <netinet/in.h>
#include <cstring>
#include <arpa/inet.h>
#include <unistd.h>

#define RW_BUF_LEN 1024

void echo(int connfd) {
    ssize_t n;
    char buf[RW_BUF_LEN];
    while (true) {
        n = read(connfd, buf, RW_BUF_LEN);
        if (n == 0) {
            std::cout << "disconnected" << std::endl;
            break;
        }
        if (n < 0) {
            if (errno != EINTR && errno != EWOULDBLOCK) {
                perror("read error");
                return;
            }
            std::cout << "nonblock" << std::endl;
            sleep(3);
        } else {
            write(connfd, buf, n);
        }
    }
}

int main() {
    int listenfd = socket(AF_INET, SOCK_STREAM, 0);
    if (listenfd == -1) {
        perror("listenfd fail");
        return 1;
    }
    struct sockaddr_in serverAddr;
    memset(&serverAddr, 0, sizeof(struct sockaddr_in));
    serverAddr.sin_family = AF_INET;
    if (inet_pton(AF_INET, "127.0.0.1", &(serverAddr.sin_addr)) != 1) {
        std::cout << "inet_pton fail" << std::endl;
        return 1;
    }
    serverAddr.sin_port = htons(8838);
    if (bind(listenfd, (struct sockaddr *)&serverAddr, sizeof(serverAddr)) == -1) {
        perror("bind fail");
        return 1;
    }
    if (listen(listenfd, 1024) == -1) {
        perror("listen failed");
        return 1;
    }
    bool running = true;
    while (running) {
        struct sockaddr_in clientAddr;
        memset(&clientAddr, 0, sizeof(struct sockaddr_in));
        socklen_t clientLen;
        int connfd = accept4(listenfd, (struct sockaddr *)&clientAddr, &clientLen, SOCK_NONBLOCK | SOCK_CLOEXEC);
        if (connfd == -1) {
            running = false;
            perror("accept failed");
        } else {
            echo(connfd);
        }
        close(connfd);
    }
    return 0;
}

This program handles a single client connection and demonstrates the core steps of socket creation, binding, listening, accepting, and echoing data.

cppbox encapsulation provides convenient wrappers for common operations, such as creating non‑blocking IPv4 sockets, setting socket options, binding, listening, and accepting connections. The relevant header and function signatures are shown below:

static const int kDefaultBacklog = 128;

int NewTcpIpV4NonBlockSocket(); // creates a non‑blocking IPv4 socket

misc::ErrorUptr SetSockOpt(int sockfd, int level, int optname, const void *optval, socklen_t optlen);
misc::ErrorUptr SetReuseAddr(int sockfd);
misc::ErrorUptr BindForTcpIpV4(int sockfd, const char *ip, uint16_t port);
misc::ErrorUptr Listen(int sockfd, int backlog = kDefaultBacklog);
misc::ErrorUptr BindAndListenForTcpIpV4(int sockfd, const char *ip, uint16_t port, bool reuseaddr = true, int backlog = kDefaultBacklog);

struct InetAddress {
    std::string ip;
    uint16_t port;
};

int Accept(int listenfd, struct InetAddress &address, int flags = SOCK_CLOEXEC | SOCK_NONBLOCK);

Usage example demonstrates how to create a socket, bind and listen using cppbox, accept a connection, and retrieve the remote IP and port:

TEST_F(NetTest, Misc) {
    int sockfd = ::socket(AF_INET, SOCK_STREAM | SOCK_CLOEXEC, 0);
    if (sockfd == -1) {
        std::cout << cppbox::misc::NewErrorUptrByErrno()->String() << std::endl;
        return;
    }
    auto error_uptr = cppbox::net::BindAndListenForTcpIpV4(sockfd, "127.0.0.1", 8860);
    if (error_uptr != nullptr) {
        std::cout << error_uptr->String() << std::endl;
        return;
    }
    cppbox::net::InetAddress raddr;
    int connfd = cppbox::net::Accept(sockfd, raddr);
    if (connfd == -1) {
        std::cout << "accept error: " << cppbox::misc::NewErrorUptrByErrno()->String() << std::endl;
        return;
    }
    std::cout << "remote ip is " << raddr.ip << std::endl;
    std::cout << "remote port is " << raddr.port << std::endl;
    ::sleep(10);
}

The article concludes that the presented echo server is a minimal foundation; additional features such as connection timeouts and buffer management can be built on top of this base.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

BackendCTCPServerNetwork programmingSocketcppbox
360 Tech Engineering
Written by

360 Tech Engineering

Official tech channel of 360, building the most professional technology aggregation platform for the brand.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.