Poco 与 libevent 网络编程

Poco 与 libevent 网络编程

C++ 网络编程是后端开发的核心能力之一。本文将介绍两个重要的 C++ 网络框架------Poco C++ 框架和 libevent 网络库,涵盖 Poco 框架的安装、字符串格式化、SocketReactor 网络编程模型,以及 libevent 的事件驱动网络 I/O 实战。

一、Poco C++ 框架

1.1 为什么选择 Poco

Poco(Portable Components,便携式组件)是一个被低估但非常实用的 C++ 框架。与其他主流框架相比:

框架 优点 缺点
Boost 大而全,标准库后备 臃肿,学习成本高,没有数据库/Redis 等支持
Qt 强大的 GUI 和工具类 始终离不开客户端开发,不适合后端
STL C++ 标准 太底层,开发应用需要编写的代码太多
Poco 实用、轻量、功能全面 国内使用较少,资料不多

Poco 的最大优势在于一个库覆盖大部分后端开发需求:数据库、Redis、串口、网络、HTTP 服务器/客户端,应有尽有。

1.2 安装 Poco

Linux(CMake 编译,推荐):

bash 复制代码
mkdir cmake-build && cd cmake-build
cmake .. -DCMAKE_BUILD_TYPE=Release
gmake -j8
sudo gmake install

注意关注 CMake 中的 Could NOT find XXX 提示,需要安装对应的依赖库。

Windows(vcpkg 安装,推荐新手):

bash 复制代码
vcpkg install POCO

Android(交叉编译):

bash 复制代码
cmake .. \
  -DCMAKE_TOOLCHAIN_FILE=$ANDROID_NDK/build/cmake/android.toolchain.cmake \
  -DANDROID_ABI=armeabi-v7a \
  -DANDROID_PLATFORM=android-23 \
  -DBUILD_SHARED_LIBS=NO \
  -DCMAKE_BUILD_TYPE=Release

1.3 Poco 字符串格式化

Poco 的格式化对类型要求非常严格,必须严格匹配类型控制符:

cpp 复制代码
#include <Poco/Format.h>

// 类型控制符
// %d -> int, %u -> unsigned int
// %ld -> long, %Ld -> long long
// %f -> double, %s -> std::string(注意:不支持 const char*)

Int8 num1 = 45;
format("%d", num1);           // 错误!
format("%d", (int)num1);      // 正确

Int16 num2 = 45;
format("%d", num2);           // 错误!
format("%d", (int)num2);      // 正确

关键规则%c 只能用于 char,%d 只能用于 int/int32,%s 只能用于 std::string。

1.4 Poco 网络编程

Poco 的网络编程支持多种模型,其中 SocketReactor 是高性能方案:

cpp 复制代码
#include <Poco/Net/StreamSocket.h>
#include <Poco/Net/SocketReactor.h>
#include <Poco/Net/SocketAddress.h>

using namespace Poco::Net;

// 创建套接字并连接
SocketAddress addr("192.168.1.1", 8999);
StreamSocket socket(addr);

// 使用 Reactor 模式
SocketReactor reactor;

// 添加事件处理器
reactor.addEventHandler(socket,
    NObserver<EchoHandler, ReadableNotification>(
        *this, &EchoHandler::onSocketReadable));

reactor.addEventHandler(socket,
    NObserver<EchoHandler, WritableNotification>(
        *this, &EchoHandler::onSocketWritable));

// 运行事件循环
reactor.run();

三个关键回调的理解:

回调 触发条件 注意事项
onSocketReadable 缓冲区有数据时 如果数据未读完会持续回调;receiveData 返回 <=0 表示套接字异常
onSocketWritable 套接字处于连接状态时 会持续回调,应配合发送队列使用,无数据时延时释放 CPU
onSocketShutdown reactor.stop() 后调用 不是套接字关闭的回调,要看注释而非字面意思

1.5 Poco 智能指针

cpp 复制代码
// AutoPtr:需要类继承 RefCountedObject 实现引用计数
class MyClass : public Poco::RefCountedObject {
    // ...
};
Poco::AutoPtr<MyClass> ptr(new MyClass);

// AutoPtr 可实现 enable_shared_from_this 功能
// AutoPtr(C* ptr, bool shared) 构造函数是关键

// SharedPtr:类似 std::shared_ptr,但缺少 enable_shared_from_this
Poco::SharedPtr<MyClass> ptr(new MyClass);

二、libevent 网络库

libevent 是一个轻量级的事件驱动网络库,适合需要高性能网络 I/O 的场景。

2.1 TCP 客户端实现

cpp 复制代码
#include <event2/event.h>
#include <event2/bufferevent.h>

// 创建事件基础结构
struct event_base *base = event_base_new();

// 创建 bufferevent(-1 表示尚未连接)
struct bufferevent *bev = bufferevent_socket_new(base, -1, BEV_OPT_CLOSE_ON_FREE);

// 解析服务器地址
struct sockaddr_in addr;
int len = sizeof(struct sockaddr);
evutil_parse_sockaddr_port("10.18.140.10:44992", (struct sockaddr*)&addr, &len);

// 设置回调函数
bufferevent_setcb(bev, read_cb, write_cb, event_cb, nullptr);
bufferevent_enable(bev, EV_READ | EV_WRITE);

// 发起连接
bufferevent_socket_connect(bev, (struct sockaddr*)&addr, len);

// 事件循环
event_base_dispatch(base);

// 清理资源
bufferevent_free(bev);
event_base_free(base);

2.2 数据读取回调

cpp 复制代码
void read_cb(struct bufferevent *bev, void *ctx) {
    auto input = bufferevent_get_input(bev);

    // 检查数据长度是否满足最小包长度
    if (evbuffer_get_length(input) < sizeof(package_head)) {
        return;
    }

    // 读取包头
    package_head head{};
    evbuffer_copyout(input, &head, sizeof(head));

    // 检查完整包是否到达
    if (evbuffer_get_length(input) < sizeof(head) + head.message_len + 1) {
        return;
    }

    // 读取完整数据包
    int total = sizeof(head) + head.message_len + 1;
    char *buf = new char[total]{};
    evbuffer_copyout(input, buf, total);

    // 移除已处理的数据
    evbuffer_drain(input, total);
    delete[] buf;
}

2.3 事件回调

cpp 复制代码
void event_cb(struct bufferevent *bev, short what, void *ctx) {
    if (what & BEV_EVENT_CONNECTED) {
        printf("连接成功\n");
        // 可以在这里创建定时器发送数据
    }

    if (what & (BEV_EVENT_ERROR | BEV_EVENT_EOF)) {
        printf("连接错误或断开\n");
        event_base_loopexit(bufferevent_get_base(bev), nullptr);
    }
}

总结

Poco 和 libevent 各有定位,适用于不同的网络编程场景:

对比维度 Poco libevent
定位 全功能后端框架 轻量级事件驱动库
学习成本 中等 较低
功能覆盖 数据库、Redis、HTTP、串口等 专注网络 I/O 和定时器
性能模型 SocketReactor(Reactor 模式) 事件驱动(epoll/kqueue)
适用场景 后端服务全栈开发 高性能网络 I/O 场景

在实际项目中,可以根据需求选择:如果需要完整的后端框架(含数据库、HTTP 等),选择 Poco;如果只需要高性能的网络 I/O 处理,libevent 是更轻量的选择。


来源:整理自 zyh/LearnPoco.cc、zyh/learn_libevent.cc 学习笔记,原载于《C++ 第三方库与框架实战》

相关推荐
叼烟扛炮2 小时前
C++第四讲:类和对象(下)
c++·算法·类和对象
Rabitebla2 小时前
vector 的骨架:三根指针、模板陷阱与迭代器失效的第一现场
开发语言·数据结构·c++·算法
晚风吹红霞3 小时前
C++异常处理核心知识点全解析
开发语言·c++
CoderCodingNo3 小时前
【信奥业余科普】C++ 的奇妙之旅 | 17:面的铺展与文本的本质——二维数组与字符串
开发语言·c++
迷途之人不知返3 小时前
优先级队列:priority_queue
数据结构·c++
曦夜日长3 小时前
C++ STL容器string(一):string的变量细节、默认函数的认识以及常用接口的使用
java·开发语言·c++
代码中介商3 小时前
C++ STL 标准模板库完全指南:从容器到迭代器
开发语言·c++·stl
winner88813 小时前
C++ 构造函数、析构函数、虚函数、虚析构
开发语言·c++
想唱rap3 小时前
应用层协议与序列化
linux·运维·服务器·网络·数据结构·c++·算法