文章

C++ 网络库实战指南 —— 不要手搓轮子,选对库事半功倍

系统介绍 C++ 主流网络库(Boost.Asio、libevent、libuv、gRPC、cpp-httplib、Poco 等),覆盖选型建议、基本用法、注意事项和踩坑经验

C++ 网络库实战指南 —— 不要手搓轮子,选对库事半功倍

上一篇 网络编程面试题 讲了底层原理,但实际开发中你不应该从 socket() + epoll() 开始手搓。选一个成熟的网络库,让你专注于业务逻辑而不是和字节序、粘包、异步回调搏斗。

这篇文章帮你搞清楚:有哪些库可选、各自擅长什么、怎么用、有什么坑。


全景对比:一张表看清所有选择

先看全貌,后面逐个详解:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
库名             定位                  语言    异步模型       跨平台    难度    适合场景

Boost.Asio      通用异步 IO 框架       C++     Proactor       ✅       中高    需要精细控制的网络服务
                                                                             高性能服务器

cpp-httplib     轻量 HTTP 库           C++     同步/线程池     ✅       低     简单的 HTTP 服务/客户端
                (Header-only)                                                快速原型、工具

Poco            企业级应用框架          C++     多种            ✅       中     企业应用、HTTP/SMTP/FTP

libcurl         HTTP 客户端            C       同步+异步       ✅       低中   HTTP 客户端请求

gRPC            RPC 框架               多语言  异步            ✅       中     微服务间通信

libevent        事件驱动框架           C       Reactor         ✅       中     轻量级事件驱动服务器

libuv           异步 IO 框架           C       事件循环        ✅       中     跨平台异步 IO(Node.js 底层)

ZeroMQ          消息传递库             C       多种模式        ✅       中     分布式系统、消息队列

Muduo           Linux 网络库           C++     Reactor         Linux    中     Linux 高性能 TCP 服务器

Crow/Drogon     Web 框架               C++     异步            ✅       中     RESTful API 服务

Boost.Beast     HTTP/WebSocket         C++     基于 Asio       ✅       高     需要 WebSocket 或底层 HTTP 控制

一、Boost.Asio —— C++ 网络编程的”标准答案”

1.1 是什么

Boost.Asio 是 C++ 中最成熟、最主流的异步 IO 库。它不仅处理网络,还能处理定时器、信号、串口等异步操作。C++20 的 Networking TS(网络标准提案)就是以 Asio 为蓝本设计的。

1
2
3
4
5
地位:
├── Boost 库的一部分,但也可以独立使用(standalone Asio)
├── 几乎所有 C++ 网络框架的底层都用它
├── C++ 标准网络库的候选方案
└── 支持协程(C++20 co_await)

1.2 核心概念

1
2
3
4
5
6
7
8
9
10
11
12
Asio 的三大核心:

1. io_context(事件循环)
   所有异步操作的调度中心
   调用 io_context.run() 开始事件循环

2. Socket(网络操作对象)
   tcp::socket、udp::socket

3. 异步操作 + 回调
   async_read、async_write、async_accept
   操作完成后调用你提供的回调函数

1.3 同步 TCP 服务器(入门)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include <boost/asio.hpp>
using boost::asio::ip::tcp;

int main() {
    boost::asio::io_context io;

    // 监听 8080 端口
    tcp::acceptor acceptor(io, tcp::endpoint(tcp::v4(), 8080));

    while (true) {
        tcp::socket socket(io);
        acceptor.accept(socket);  // 阻塞等待连接

        // 读数据
        boost::asio::streambuf buf;
        boost::asio::read_until(socket, buf, "\n");
        std::string message = boost::asio::buffer_cast<const char*>(buf.data());

        // 写数据
        std::string response = "Echo: " + message;
        boost::asio::write(socket, boost::asio::buffer(response));
    }
}

1.4 异步 TCP 服务器(实战模式)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
#include <boost/asio.hpp>
#include <memory>
using boost::asio::ip::tcp;

class Session : public std::enable_shared_from_this<Session> {
    tcp::socket socket_;
    char data_[1024];

public:
    Session(tcp::socket socket) : socket_(std::move(socket)) {}

    void start() { doRead(); }

private:
    void doRead() {
        auto self = shared_from_this();  // 防止回调时对象已销毁
        socket_.async_read_some(
            boost::asio::buffer(data_, sizeof(data_)),
            [this, self](boost::system::error_code ec, size_t length) {
                if (!ec) {
                    doWrite(length);
                }
            });
    }

    void doWrite(size_t length) {
        auto self = shared_from_this();
        boost::asio::async_write(
            socket_, boost::asio::buffer(data_, length),
            [this, self](boost::system::error_code ec, size_t) {
                if (!ec) {
                    doRead();  // 写完继续读
                }
            });
    }
};

class Server {
    tcp::acceptor acceptor_;

public:
    Server(boost::asio::io_context& io, short port)
        : acceptor_(io, tcp::endpoint(tcp::v4(), port)) {
        doAccept();
    }

private:
    void doAccept() {
        acceptor_.async_accept(
            [this](boost::system::error_code ec, tcp::socket socket) {
                if (!ec) {
                    std::make_shared<Session>(std::move(socket))->start();
                }
                doAccept();  // 继续等下一个连接
            });
    }
};

int main() {
    boost::asio::io_context io;
    Server server(io, 8080);
    io.run();  // 开始事件循环(阻塞在这里)
}

1.5 C++20 协程版(最现代的写法)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
#include <boost/asio.hpp>
#include <boost/asio/co_spawn.hpp>
#include <boost/asio/detached.hpp>
using boost::asio::ip::tcp;
using boost::asio::awaitable;
using boost::asio::use_awaitable;

awaitable<void> handleClient(tcp::socket socket) {
    char data[1024];
    while (true) {
        // co_await!看起来像同步代码,实际是异步执行
        auto n = co_await socket.async_read_some(
            boost::asio::buffer(data), use_awaitable);

        co_await boost::asio::async_write(
            socket, boost::asio::buffer(data, n), use_awaitable);
    }
}

awaitable<void> listener(tcp::acceptor acceptor) {
    while (true) {
        auto socket = co_await acceptor.async_accept(use_awaitable);
        co_spawn(acceptor.get_executor(), handleClient(std::move(socket)),
                 boost::asio::detached);
    }
}

int main() {
    boost::asio::io_context io;
    co_spawn(io, listener(tcp::acceptor(io, {tcp::v4(), 8080})),
             boost::asio::detached);
    io.run();
}

// 对比回调版:代码量减半,可读性翻倍
// 没有回调嵌套、没有 shared_from_this
// 异步代码看起来和同步一样直观

1.6 注意事项和常见坑

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
坑 1:对象生命周期
  异步回调执行时,对象可能已销毁
  解决:enable_shared_from_this + shared_ptr
        在回调中捕获 self = shared_from_this()

坑 2:线程安全
  io_context.run() 可以多线程调用(线程池模式)
  但同一个 socket 的操作不能并发
  解决:用 strand 串行化同一连接的操作
        boost::asio::strand<boost::asio::io_context::executor_type>

坑 3:忘记保持 io_context 有活干
  如果没有待处理的异步操作,io_context.run() 会立即返回
  解决:用 work_guard 保持 io_context 存活
        auto guard = boost::asio::make_work_guard(io);

坑 4:缓冲区生命周期
  async_write(buffer) 中的 buffer 必须在回调触发前保持有效
  解决:buffer 放在 Session 的成员变量中,或 shared_ptr 管理

坑 5:错误处理
  每个异步回调的第一个参数都是 error_code
  必须检查!忽略错误码 = 迟早崩溃

二、cpp-httplib —— 最简单的 HTTP 库

2.1 是什么

一个 Header-only 的 C++ HTTP/HTTPS 库。一个头文件搞定,不需要编译任何东西。适合快速搭建 HTTP 服务或发送 HTTP 请求。

1
2
3
4
5
6
7
8
9
10
11
优点:
├── 一个头文件(httplib.h),#include 即用
├── 同时支持 HTTP 服务器和客户端
├── 支持 HTTPS(需链接 OpenSSL)
├── 支持多线程处理请求
└── API 极其简洁

缺点:
├── 不是异步的(每个请求一个线程)
├── 不适合高并发场景(万级连接以上)
└── 不支持 WebSocket

2.2 HTTP 服务器(10 行搞定)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#include "httplib.h"

int main() {
    httplib::Server svr;

    // GET 请求
    svr.Get("/hello", [](const httplib::Request& req, httplib::Response& res) {
        res.set_content("Hello, World!", "text/plain");
    });

    // 带参数的 GET
    svr.Get(R"(/users/(\d+))", [](const httplib::Request& req, httplib::Response& res) {
        auto id = req.matches[1];  // 正则捕获
        res.set_content("User ID: " + std::string(id), "text/plain");
    });

    // POST 请求(JSON)
    svr.Post("/api/data", [](const httplib::Request& req, httplib::Response& res) {
        auto body = req.body;  // 请求体
        res.set_content("{\"status\": \"ok\"}", "application/json");
    });

    // 静态文件服务
    svr.set_mount_point("/static", "./public");

    svr.listen("0.0.0.0", 8080);
}

2.3 HTTP 客户端

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
#include "httplib.h"

// GET 请求
httplib::Client cli("http://httpbin.org");
auto res = cli.Get("/get");
if (res && res->status == 200) {
    std::cout << res->body << std::endl;
}

// POST JSON
httplib::Client cli("http://localhost:8080");
auto res = cli.Post("/api/data",
    "{\"name\": \"Alice\", \"age\": 30}",
    "application/json");

// 带 Header
httplib::Headers headers = {
    {"Authorization", "Bearer token123"},
    {"Accept", "application/json"}
};
auto res = cli.Get("/api/profile", headers);

// HTTPS
httplib::SSLClient cli("https://api.example.com");
cli.set_ca_cert_path("/etc/ssl/certs/ca-certificates.crt");
auto res = cli.Get("/secure");

// 超时设置
cli.set_connection_timeout(5);  // 连接超时 5 秒
cli.set_read_timeout(10);       // 读超时 10 秒

2.4 注意事项

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
1. 线程模型
   默认每个请求创建一个线程(或用线程池)
   svr.new_task_queue = [] { return new httplib::ThreadPool(8); };
   高并发场景不适合,但几百并发没问题

2. 大文件处理
   用流式 API,不要把整个文件读到内存
   svr.Get("/download", [](auto& req, auto& res) {
       res.set_content_provider(fileSize, "application/octet-stream",
           [](size_t offset, size_t length, auto& sink) {
               sink.write(data + offset, length);
               return true;
           });
   });

3. 编译注意
   Header-only 意味着编译时间较长(httplib.h 有 1 万多行)
   建议只在一个 .cpp 中 #include "httplib.h"
   其他文件前向声明或通过接口使用

三、libcurl —— HTTP 客户端之王

3.1 是什么

最流行的 HTTP 客户端库,几乎所有语言的 HTTP 库底层都是 libcurl。C 语言编写,但有很好的 C++ 封装。

1
2
3
4
5
6
特点:
├── 支持 HTTP/1.1、HTTP/2、HTTP/3
├── 支持几乎所有协议:FTP、SMTP、POP3、IMAP...
├── 极其成熟稳定(20+ 年历史)
├── 所有平台都有
└── 只做客户端,不做服务器

3.2 基本用法(C API)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
#include <curl/curl.h>

// 回调函数:接收响应数据
size_t writeCallback(void* contents, size_t size, size_t nmemb, std::string* out) {
    out->append((char*)contents, size * nmemb);
    return size * nmemb;
}

int main() {
    curl_global_init(CURL_GLOBAL_DEFAULT);
    CURL* curl = curl_easy_init();

    if (curl) {
        std::string response;

        curl_easy_setopt(curl, CURLOPT_URL, "https://api.example.com/data");
        curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writeCallback);
        curl_easy_setopt(curl, CURLOPT_WRITEDATA, &response);
        curl_easy_setopt(curl, CURLOPT_TIMEOUT, 10L);  // 超时 10 秒

        CURLcode res = curl_easy_perform(curl);

        if (res == CURLE_OK) {
            long http_code;
            curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http_code);
            std::cout << "HTTP " << http_code << ": " << response << std::endl;
        } else {
            std::cerr << "Error: " << curl_easy_strerror(res) << std::endl;
        }

        curl_easy_cleanup(curl);
    }
    curl_global_cleanup();
}

3.3 POST 请求

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
CURL* curl = curl_easy_init();
std::string response;

// JSON POST
const char* json = "{\"name\": \"Alice\"}";
struct curl_slist* headers = NULL;
headers = curl_slist_append(headers, "Content-Type: application/json");

curl_easy_setopt(curl, CURLOPT_URL, "https://api.example.com/users");
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, json);
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writeCallback);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &response);

curl_easy_perform(curl);

curl_slist_free_all(headers);
curl_easy_cleanup(curl);

3.4 推荐的 C++ 封装库

原生 C API 比较啰嗦,推荐用封装库:

1
2
3
4
5
6
7
8
9
// curlpp(C++ 封装)
#include <curlpp/cURLpp.hpp>
#include <curlpp/Easy.hpp>
#include <curlpp/Options.hpp>

curlpp::Easy request;
request.setOpt(curlpp::options::Url("https://api.example.com/data"));
request.setOpt(curlpp::options::WriteStream(&std::cout));
request.perform();

3.5 注意事项

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
1. curl_global_init 不是线程安全的
   必须在程序启动时(主线程)调用一次
   curl_easy_* 是线程安全的(每个线程一个 CURL handle)

2. 内存管理
   curl_easy_init 必须配对 curl_easy_cleanup
   curl_slist_append 创建的链表必须 curl_slist_free_all
   推荐用 RAII 包装

3. HTTPS 证书验证
   默认验证服务器证书(这是好事!)
   如果连接失败检查 CA 证书路径:
   curl_easy_setopt(curl, CURLOPT_CAINFO, "/path/to/ca-bundle.crt");
   不要图省事关闭验证:CURLOPT_SSL_VERIFYPEER = 0  // ❌ 不安全

4. 超时设置
   一定要设超时!默认无超时,可能永远挂起
   CURLOPT_TIMEOUT         → 总超时(秒)
   CURLOPT_CONNECTTIMEOUT  → 连接超时(秒)

四、gRPC —— 微服务通信标准

4.1 是什么

Google 开源的 RPC 框架,用 Protocol Buffers 定义接口,基于 HTTP/2 传输。微服务架构中最流行的通信方式。

1
2
3
4
5
6
特点:
├── 跨语言(C++、Java、Go、Python、Rust...)
├── 高性能(HTTP/2 多路复用 + Protobuf 二进制序列化)
├── 支持四种通信模式(一元、服务端流、客户端流、双向流)
├── 内置认证、负载均衡、健康检查
└── 有完善的代码生成工具

4.2 定义服务(.proto 文件)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
syntax = "proto3";

package example;

service Greeter {
    // 普通 RPC(一元调用)
    rpc SayHello (HelloRequest) returns (HelloReply);

    // 服务端流
    rpc ListUsers (ListRequest) returns (stream User);

    // 客户端流
    rpc UploadLogs (stream LogEntry) returns (Summary);

    // 双向流
    rpc Chat (stream Message) returns (stream Message);
}

message HelloRequest {
    string name = 1;
}

message HelloReply {
    string message = 1;
}

4.3 服务端实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include <grpcpp/grpcpp.h>
#include "greeter.grpc.pb.h"

class GreeterService final : public Greeter::Service {
    grpc::Status SayHello(grpc::ServerContext* context,
                          const HelloRequest* request,
                          HelloReply* reply) override {
        reply->set_message("Hello, " + request->name() + "!");
        return grpc::Status::OK;
    }
};

int main() {
    std::string address = "0.0.0.0:50051";
    GreeterService service;

    grpc::ServerBuilder builder;
    builder.AddListeningPort(address, grpc::InsecureServerCredentials());
    builder.RegisterService(&service);

    auto server = builder.BuildAndStart();
    std::cout << "Server listening on " << address << std::endl;
    server->Wait();
}

4.4 客户端调用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include <grpcpp/grpcpp.h>
#include "greeter.grpc.pb.h"

int main() {
    auto channel = grpc::CreateChannel("localhost:50051",
                                       grpc::InsecureChannelCredentials());
    auto stub = Greeter::NewStub(channel);

    HelloRequest request;
    request.set_name("World");

    HelloReply reply;
    grpc::ClientContext context;
    context.set_deadline(std::chrono::system_clock::now() +
                         std::chrono::seconds(5));  // 超时

    grpc::Status status = stub->SayHello(&context, request, &reply);

    if (status.ok()) {
        std::cout << reply.message() << std::endl;  // "Hello, World!"
    } else {
        std::cerr << "RPC failed: " << status.error_message() << std::endl;
    }
}

4.5 注意事项

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
1. 编译复杂
   需要安装 protobuf + grpc
   CMake 配置较复杂
   建议用 vcpkg:vcpkg install grpc

2. 消息大小限制
   默认最大消息 4MB
   修改:builder.SetMaxReceiveMessageSize(64 * 1024 * 1024);

3. 连接管理
   Channel 是线程安全的,可以复用
   不要为每次调用创建新 Channel(开销大)
   auto channel = grpc::CreateChannel(...);  // 创建一次
   // 多个线程可以共享这个 channel

4. 错误处理
   用 Status 而不是异常
   检查 status.error_code() 和 status.error_message()
   常见错误码:UNAVAILABLE(服务不可达)、DEADLINE_EXCEEDED(超时)

5. 调试
   设置环境变量 GRPC_VERBOSITY=DEBUG 和 GRPC_TRACE=all
   或者用 grpcurl 命令行工具测试

五、libevent / libuv —— C 语言事件驱动库

5.1 libevent

1
2
定位:轻量级事件驱动库,封装了 epoll/kqueue/IOCP
代表用户:Memcached、Chromium(部分)、Tor
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
#include <event2/event.h>
#include <event2/listener.h>
#include <event2/bufferevent.h>

// 读回调
void readCallback(struct bufferevent* bev, void* ctx) {
    char buf[1024];
    int n = bufferevent_read(bev, buf, sizeof(buf));
    bufferevent_write(bev, buf, n);  // Echo
}

// 新连接回调
void acceptCallback(struct evconnlistener* listener,
                    evutil_socket_t fd, struct sockaddr* addr,
                    int socklen, void* ctx) {
    auto base = evconnlistener_get_base(listener);
    auto bev = bufferevent_socket_new(base, fd, BEV_OPT_CLOSE_ON_FREE);
    bufferevent_setcb(bev, readCallback, NULL, NULL, NULL);
    bufferevent_enable(bev, EV_READ | EV_WRITE);
}

int main() {
    auto base = event_base_new();
    struct sockaddr_in sin = {};
    sin.sin_family = AF_INET;
    sin.sin_port = htons(8080);

    auto listener = evconnlistener_new_bind(base, acceptCallback, NULL,
        LEV_OPT_REUSEABLE | LEV_OPT_CLOSE_ON_FREE, -1,
        (struct sockaddr*)&sin, sizeof(sin));

    event_base_dispatch(base);  // 事件循环

    evconnlistener_free(listener);
    event_base_free(base);
}

5.2 libuv

1
2
3
定位:跨平台异步 IO 库(Node.js 的底层)
特点:统一的事件循环,支持文件 IO、子进程、DNS 等
代表用户:Node.js、Julia、Luvit
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#include <uv.h>

uv_loop_t* loop;

void onNewConnection(uv_stream_t* server, int status) {
    auto client = (uv_tcp_t*)malloc(sizeof(uv_tcp_t));
    uv_tcp_init(loop, client);
    uv_accept(server, (uv_stream_t*)client);
    uv_read_start((uv_stream_t*)client, allocBuffer, onRead);
}

int main() {
    loop = uv_default_loop();

    uv_tcp_t server;
    uv_tcp_init(loop, &server);

    struct sockaddr_in addr;
    uv_ip4_addr("0.0.0.0", 8080, &addr);
    uv_tcp_bind(&server, (const struct sockaddr*)&addr, 0);

    uv_listen((uv_stream_t*)&server, 128, onNewConnection);

    uv_run(loop, UV_RUN_DEFAULT);  // 事件循环
}

5.3 libevent vs libuv 对比

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
              libevent                 libuv

语言          C                        C
异步模型      Reactor                  事件循环(类 Proactor)
网络 IO       ✅                       ✅
文件 IO       ❌ (需要自己处理)          ✅ (线程池异步文件 IO)
子进程        ❌                        ✅
DNS           ✅ (内置异步 DNS)          ✅
定时器        ✅                        ✅
Windows       ⚠️ (支持但不完美)          ✅ (IOCP 原生支持)
线程池        ❌                        ✅ (内置)
代表用户      Memcached               Node.js

选择建议:
  纯网络服务器 → libevent 更轻量
  需要文件 IO + 子进程 + 跨平台 → libuv
  C++ 项目 → 优先考虑 Boost.Asio

六、Poco —— 企业级 C++ 应用框架

6.1 是什么

一个完整的 C++ 应用框架,不仅有网络,还包括日志、JSON、XML、数据库、加密等。适合需要”全家桶”的企业项目。

1
2
3
4
5
6
7
8
9
模块:
├── Poco::Net         → HTTP/HTTPS 客户端和服务器
├── Poco::Net::SSL    → TLS/SSL 支持
├── Poco::JSON        → JSON 解析和生成
├── Poco::XML         → XML 解析
├── Poco::Data        → 数据库抽象层(MySQL/SQLite/ODBC)
├── Poco::Crypto      → 加密和哈希
├── Poco::Util        → 配置文件、命令行解析
└── Poco::Logger      → 日志系统

6.2 HTTP 服务器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
#include <Poco/Net/HTTPServer.h>
#include <Poco/Net/HTTPRequestHandler.h>
#include <Poco/Net/HTTPResponse.h>
#include <Poco/Net/ServerSocket.h>

class HelloHandler : public Poco::Net::HTTPRequestHandler {
public:
    void handleRequest(Poco::Net::HTTPServerRequest& request,
                       Poco::Net::HTTPServerResponse& response) override {
        response.setContentType("text/plain");
        response.setStatus(Poco::Net::HTTPResponse::HTTP_OK);
        std::ostream& out = response.send();
        out << "Hello from Poco!";
    }
};

class HandlerFactory : public Poco::Net::HTTPRequestHandlerFactory {
public:
    Poco::Net::HTTPRequestHandler* createRequestHandler(
        const Poco::Net::HTTPServerRequest& request) override {
        return new HelloHandler;
    }
};

int main() {
    Poco::Net::ServerSocket socket(8080);
    Poco::Net::HTTPServer server(new HandlerFactory, socket,
                                  new Poco::Net::HTTPServerParams);
    server.start();
    // 等待退出信号...
    server.stop();
}

6.3 HTTP 客户端

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include <Poco/Net/HTTPClientSession.h>
#include <Poco/Net/HTTPRequest.h>
#include <Poco/Net/HTTPResponse.h>
#include <Poco/StreamCopier.h>

Poco::Net::HTTPClientSession session("api.example.com", 80);

Poco::Net::HTTPRequest request(Poco::Net::HTTPRequest::HTTP_GET, "/data");
request.set("Accept", "application/json");
session.sendRequest(request);

Poco::Net::HTTPResponse response;
auto& body = session.receiveResponse(response);
std::string result;
Poco::StreamCopier::copyToString(body, result);

std::cout << response.getStatus() << ": " << result << std::endl;

七、WebSocket 相关库

7.1 选择方案

1
2
3
4
5
6
7
需要 WebSocket 的场景:实时聊天、推送通知、在线游戏、实时数据

方案选择:
├── Boost.Beast → 最底层,最灵活,和 Asio 配合
├── websocketpp → 老牌 WebSocket 库
├── uWebSockets → 极致性能(号称最快的 WebSocket 库)
└── Drogon/Crow → Web 框架内置 WebSocket 支持

7.2 Boost.Beast WebSocket 示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#include <boost/beast.hpp>
#include <boost/asio.hpp>
namespace beast = boost::beast;
namespace websocket = beast::websocket;
using tcp = boost::asio::ip::tcp;

// 同步 WebSocket 客户端
int main() {
    boost::asio::io_context io;
    tcp::resolver resolver(io);
    websocket::stream<tcp::socket> ws(io);

    auto results = resolver.resolve("echo.websocket.org", "80");
    boost::asio::connect(ws.next_layer(), results);

    ws.handshake("echo.websocket.org", "/");

    ws.write(boost::asio::buffer("Hello WebSocket!"));

    beast::flat_buffer buffer;
    ws.read(buffer);
    std::cout << beast::buffers_to_string(buffer.data()) << std::endl;

    ws.close(websocket::close_code::normal);
}

八、C++ Web 框架

8.1 Drogon —— 高性能异步 Web 框架

1
2
3
4
5
6
特点:
├── 基于事件循环的异步框架
├── 内置 ORM、WebSocket、HTTP 客户端
├── 性能极高(TechEmpower 排名靠前)
├── 支持 C++17 协程
└── 类似 Go 的 Gin 或 Python 的 FastAPI 的体验
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#include <drogon/drogon.h>

int main() {
    drogon::app()
        .addListener("0.0.0.0", 8080)

        .registerHandler("/hello",
            [](const drogon::HttpRequestPtr& req,
               std::function<void(const drogon::HttpResponsePtr&)>&& callback) {
                auto resp = drogon::HttpResponse::newHttpResponse();
                resp->setBody("Hello from Drogon!");
                callback(resp);
            })

        .registerHandler("/api/users/{id}",
            [](const drogon::HttpRequestPtr& req,
               std::function<void(const drogon::HttpResponsePtr&)>&& callback,
               const std::string& id) {
                Json::Value json;
                json["id"] = id;
                json["name"] = "Alice";
                auto resp = drogon::HttpResponse::newHttpJsonResponse(json);
                callback(resp);
            },
            {drogon::Get})

        .run();
}

8.2 Crow —— 轻量级 Web 框架

1
2
3
4
5
特点:
├── 灵感来自 Python Flask
├── Header-only
├── API 极简
└── 适合小型 API 服务
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include "crow.h"

int main() {
    crow::SimpleApp app;

    CROW_ROUTE(app, "/")([]() {
        return "Hello, World!";
    });

    CROW_ROUTE(app, "/json")([]() {
        crow::json::wvalue result;
        result["status"] = "ok";
        result["count"] = 42;
        return result;
    });

    CROW_ROUTE(app, "/user/<int>")([](int id) {
        return "User: " + std::to_string(id);
    });

    app.port(8080).multithreaded().run();
}

九、ZeroMQ —— 消息传递库

9.1 是什么

不是传统的消息队列(不是 RabbitMQ/Kafka),而是一个高性能消息传递库,提供 Socket 级别的 API 但自带消息模式(发布订阅、请求响应、推拉等)。

1
2
3
4
5
6
消息模式:
├── REQ/REP   → 请求-响应(同步 RPC 风格)
├── PUB/SUB   → 发布-订阅(广播)
├── PUSH/PULL → 推-拉(任务分发/工作队列)
├── PAIR      → 一对一连接
└── ROUTER/DEALER → 异步请求-响应

9.2 请求-响应模式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
// 服务端
#include <zmq.hpp>

zmq::context_t context(1);
zmq::socket_t socket(context, zmq::socket_type::rep);
socket.bind("tcp://*:5555");

while (true) {
    zmq::message_t request;
    socket.recv(request, zmq::recv_flags::none);
    std::string msg(static_cast<char*>(request.data()), request.size());

    zmq::message_t reply(5);
    memcpy(reply.data(), "World", 5);
    socket.send(reply, zmq::send_flags::none);
}

// 客户端
zmq::context_t context(1);
zmq::socket_t socket(context, zmq::socket_type::req);
socket.connect("tcp://localhost:5555");

zmq::message_t request(5);
memcpy(request.data(), "Hello", 5);
socket.send(request, zmq::send_flags::none);

zmq::message_t reply;
socket.recv(reply, zmq::recv_flags::none);

十、选型决策树

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
你需要做什么?

├── 简单的 HTTP API 服务(几十并发)
│   └── cpp-httplib(一个头文件搞定)
│
├── RESTful Web 服务(中等并发)
│   └── Crow(轻量) 或 Drogon(高性能)
│
├── 需要发送 HTTP 请求(客户端)
│   ├── 简单场景 → cpp-httplib
│   └── 复杂场景(代理/Cookie/重定向/HTTP2) → libcurl
│
├── 高性能 TCP 服务器
│   ├── Linux only → Muduo
│   └── 跨平台 → Boost.Asio
│
├── 微服务间通信(RPC)
│   └── gRPC(业界标准)
│
├── 实时通信(WebSocket)
│   ├── 只需 WebSocket → uWebSockets
│   └── WebSocket + HTTP → Drogon 或 Boost.Beast
│
├── 分布式消息传递
│   └── ZeroMQ
│
├── 企业应用(HTTP + JSON + DB + 日志 全家桶)
│   └── Poco
│
└── 需要和 Node.js/Electron 集成
    └── libuv

十一、通用注意事项

11.1 所有网络库都要注意的事

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
1. 超时设置
   永远不要使用默认超时(可能是无限等待)
   连接超时 + 读超时 + 写超时 都要设

2. 错误处理
   网络操作随时可能失败
   每个操作都检查返回值/错误码
   断线重连要有退避策略(exponential backoff)

3. 缓冲区管理
   异步操作中缓冲区必须在回调前保持有效
   注意缓冲区大小限制(防止 OOM)

4. 线程安全
   大多数网络对象(socket、connection)不是线程安全的
   同一个连接的操作要串行化
   共享的连接池/数据结构需要加锁

5. 优雅关闭
   先停止接受新连接
   等待现有连接处理完毕
   设置关闭超时
   释放所有资源

6. 日志和监控
   记录连接数、请求量、错误率、延迟
   方便排查线上问题

7. HTTPS / TLS
   生产环境必须使用 HTTPS
   不要禁用证书验证
   注意证书过期和续期

11.2 性能调优通用建议

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
1. 连接复用
   不要为每次请求创建新连接
   用连接池 / HTTP Keep-Alive / gRPC Channel 复用

2. 减少拷贝
   用 move 语义传递缓冲区
   用 scatter/gather IO(readv/writev)
   大文件用 sendfile(零拷贝)

3. 批量操作
   多个小消息合并发送(Nagle 算法 或 手动 batch)
   如果需要低延迟,禁用 Nagle:TCP_NODELAY

4. 异步优先
   高并发场景用异步 IO(Asio / epoll / IOCP)
   同步模型在几百并发时就会成为瓶颈

5. 压缩
   HTTP 响应开启 gzip/brotli 压缩
   gRPC 默认支持压缩

11.3 CMake 集成示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
cmake_minimum_required(VERSION 3.20)
project(MyServer LANGUAGES CXX)
set(CMAKE_CXX_STANDARD 20)

# 方式 1:vcpkg(推荐)
# vcpkg install boost-asio grpc cpp-httplib
find_package(Boost REQUIRED COMPONENTS system)
find_package(gRPC CONFIG REQUIRED)

# 方式 2:FetchContent(自动下载)
include(FetchContent)
FetchContent_Declare(httplib
    GIT_REPOSITORY https://github.com/yhirose/cpp-httplib
    GIT_TAG v0.15.0
)
FetchContent_MakeAvailable(httplib)

add_executable(server src/main.cpp)
target_link_libraries(server PRIVATE
    Boost::system
    httplib::httplib
)

总结

1
2
3
4
5
6
7
8
9
一句话选型:

"我就想发个 HTTP 请求"         → cpp-httplib(客户端)或 libcurl
"我要写个简单的 API 服务"       → cpp-httplib 或 Crow
"我要写高性能服务器"            → Boost.Asio
"微服务之间要通信"              → gRPC
"我需要 WebSocket"             → Boost.Beast 或 Drogon
"我要个全家桶"                  → Poco 或 Drogon
"我在做分布式系统"              → ZeroMQ

核心原则:不要手搓轮子。 网络编程的坑(粘包、断线重连、超时、并发、内存管理)前人都踩过了,选一个成熟的库让你专注在业务逻辑上。从最简单的库开始(cpp-httplib),随着需求增长再切换到更强大的库(Asio/gRPC)。


本文聚焦最常用的库和最实用的代码示例。每个库都有大量高级用法无法在一篇文章中覆盖,建议选定一个库后深入阅读其官方文档。配合 网络编程面试题 理解底层原理,开发时事半功倍。

本文由作者按照 CC BY 4.0 进行授权