C++ Network Programming with Asio

Blocking sockets vs the asio proactor, TCP echo server, asynchronous read/write, timers, and production settings.

AdvancedUpdated 2026-10-2833K views

Blocking Sockets: the Foundation

#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>

// 服务端:socket → bind → listen → accept
int srv = socket(AF_INET, SOCK_STREAM, 0);
sockaddr_in addr{};
addr.sin_family = AF_INET;
addr.sin_port   = htons(8080);
addr.sin_addr.s_addr = htonl(INADDR_ANY);

int one = 1;
setsockopt(srv, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one));

bind(srv, (sockaddr*)&addr, sizeof(addr));
listen(srv, 64);              // backlog:等待队列

int conn = accept(srv, nullptr, nullptr);   // 阻塞直至连接到达
char buf[4096];
ssize_t n = read(conn, buf, sizeof(buf));  // 阻塞
write(conn, buf, n);
close(conn);

One blocking socket is simple; one thread per connection does not scale past a few thousand. The reactive alternative is epoll/kqueue, and asio gives you that power with a friendlier API.

Asio: the Proactor Model

#include <asio.hpp>
using asio::ip::tcp;

// 异步 TCP 回显:io_context 驱动事件循环
std::make_shared<Session>(io).start();
io.run();

class Session : public std::enable_shared_from_this<Session> {
public:
    Session(asio::io_context& io) : socket_(io) {}
    tcp::socket& socket() { return socket_; }

    void start() { do_read(); }

    void do_read() {
        auto self = shared_from_this();        // 延长生命周期
        socket_.async_read_some(asio::buffer(data_),
            [self](auto ec, std::size_t n) {
                if (!ec) self->do_write(n);     // 回写
            });
    }

    void do_write(std::size_t len) {
        auto self = shared_from_this();
        asio::async_write(socket_, asio::buffer(data_, len),
            [self](auto ec, std::size_t) {
                if (!ec) self->do_read();       // 继续读
            });
    }

private:
    tcp::socket socket_;
    std::array<char, 4096> data_{};
};
The completion handler captures self (shared_from_this) so the Session outlives the async operation; otherwise the callback could touch a deleted object. This is the single most important asio idiom.

Timers and io_context Threads

// 定时器:心跳 / 超时清理
asio::steady_timer timer(io, std::chrono::seconds(30));
timer.async_wait([](auto ec) {
    if (!ec) sweep_expired();     // ec == operation_aborted 表示被取消
});

// 多线程:一个 io_context,N 个线程 run()
std::vector<std::thread> pool;
for (int i = 0; i < 4; ++i)
    pool.emplace_back([&io] { io.run(); });
// 回调可能在任一线程执行
// 需要 strand 保证处理顺序
asio::strand<asio::io_context::executor_type> strand(io.get_executor());
post(strand, [] { /* 串行化执行 */ });

Production Settings

  • Set SO_REUSEADDR (avoid TIME_WAIT bind failures), TCP_NODELAY for small interactive writes.
  • Size buffers to realistic payloads; copy payload out of the IO buffer before the next read.
  • Log ec values: operation_aborted is normal on shutdown, eof means peer closed.
  • Consider coroutines (asio co_await, C++20) to flatten callback chains.

Friends & Partners