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++ 第三方库与框架实战》

相关推荐
江安下小雨21 分钟前
muduo网络库(十三):Buffer 缓冲区类
c++·经验分享
liulilittle2 小时前
XTCP简述
网络·c++·tcp/ip·ip·tcp·通信
一只旭宝2 小时前
预约系统版本2(基于第一版改良)
服务器·数据库·c++
被怪兽吃掉了2 小时前
5.2.1一维组数定义方式
开发语言·c++·算法
旖旎夜光2 小时前
LeetCode 69:x 的平方根(二分查找) —— 题解
数据结构·c++·算法·leetcode·二分查找
j7~2 小时前
【C++】《C++二叉搜索树(BST)从入门到精通:概念、实现与Key/Value模型全解析》
开发语言·c++·学习·二叉搜索树
hehelm2 小时前
仿muduo库实现高并发服务器—Channel类
linux·服务器·开发语言·网络·c++
余额瞒着我当琳2 小时前
C++ list第二讲数据结构修炼:迭代器源码 + 栈队列适配器 + LeetCode 三道高频题
数据结构·c++·list
hehelm2 小时前
仿muduo库实现高并发服务器—EventLoop
linux·服务器·网络·c++
_Narcissus_2 小时前
常见数论算法笔记
数据结构·c++·算法·高精度·数论·快速幂·质数筛