试试Boost.Asio

慢慢一点一点看看Boost,这段时间就Asio库吧。 据说这货和libevent的效率差不多,但是Boost的平台兼容性,你懂得。还有它帮忙干掉了很多线程安全和线程分发的事情。

Boost.Asio 依赖项:

  1. Boost.System (所以它必须链接boost_system)

  2. [可选] 如果使用read_until() or async_read_until() 函数,则依赖Boost.Regex(boost_regex)

  3. [可选] SSL功能依赖OpenSSL

先来个简单的,系统信号量 Signal控制:

使用ASIO操作信号量有一个注意事项,不允许再使用其他库或工具管理信号量(如signal() 或 sigaction()函数)

#include <cstdio>
#include <iostream>
#include <cstdlib>
#include <stdint.h>
#include <cstring>
#include <string>
#include <vector>

#include <boost/asio.hpp>
#include <boost/bind.hpp>

void signal_handler(
    boost::asio::signal_set& stSigs,
    const boost::system::error_code& error,
    int signal_number)
{
    if (error)
    {
        std::cout<< error.message()<< std::endl;
    }

    std::cout<< "Catch a signal: "<< signal_number<< std::endl;

    stSigs.async_wait(boost::bind(signal_handler, boost::ref(stSigs), _1, _2));
}

void signal_handler2(
    boost::asio::signal_set& stSigs,
    const boost::system::error_code& error,
    int signal_number)
{
    if (error)
    {
        std::cout<< error.message()<< std::endl;
    }

    std::cout<< "Catch a signal - handle 2: "<< signal_number<< std::endl;

    stSigs.async_wait(boost::bind(signal_handler2, boost::ref(stSigs), _1, _2));
}

int main(int argc, char* argv[]) {

    boost::asio::io_service stMainService;
    boost::asio::signal_set stSigs(stMainService);

    stSigs.add(SIGINT);
    stSigs.add(SIGTERM);

    stSigs.async_wait(boost::bind(signal_handler, boost::ref(stSigs), _1, _2));
    stSigs.async_wait(boost::bind(signal_handler2, boost::ref(stSigs), _1, _2));

    stMainService.run();

    return 0;
}

So easy吧,可以一次注册多个handler。 需要注意的是每次触发signal的handler后,handler就被取消了,需要重新注册一次。否则下一次就不会跳到这个handler了

第二个尝试,网络IO:

按照文档描述,除非使用宏来禁止功能,网络IO在不同的环境下采用了不同的实现方式

  1. Windows: IOCP

  2. Linux: epoll

  3. Mac OS X, FreeBSD, NetBSD, OpenBSD: kqueue

  4. Solaris: /dev/poll

Boost.Asio的接口是仿IOCP的异步IO的形式的(参见:http://www.cnblogs.com/hello-leo/archive/2011/04/12/leo.html),所以,其他环境下都是模拟的。

这个搞起来相当费劲,经常需要跳转到Boost的源码中,查看一些回调函数的定义式write和write_some函数在completion_condition返回0时才发送,否则将数据加入到发送窗口,并且没有发生数据拷贝,也就是说,如果是异步操作,开发者必须保证发送时数据有效。(这类函数默认的completion_condition是仿函数transfer_all,返回default_max_transfer_size: 65536) 同样,read和read_some也是这样。 Send和receive函数才是立即执行的(不推荐使用)。

另外,streambuf流用于管理发送或接收缓冲,但是在发送或接收完后,要执行consume函数移出或commit移入缓冲区,否则数据不会被销毁。

UDP和TCP的类似,我就不再多写一个demo了。 以上sample的client和server的读数据采用了两种不同的方式

有一点比较爽,在多线程条件下 io_service的run函数是线程安全的,也就是说,多个线程调用同一个run的时候,就自动被加入工作线程池,在消息到来的时候io_service会找到一个可用的线程进行处理。

注:以上代码Visual Studio中需要包含Boost的include目录和lib目录;GCC或Clang中需要加编译选项-I[BOOSTPREFIX目录]/include –L[BOOST_PREFIX目录]/lib -lboost_random -lboost_thread -lboost_system -lstdc++ -lrt -pthread -D_POSIX_MT, 如果BOOST不在系统动态库查找目录中且同时存在Boost的动态和静态库则加上 -static 选项

另外,这个demo代码包含一些功能检测的代码。

小试牛的第三刀,定时器Timer:

还是喜欢上代码,so…

话说Boost.Asio每次异步wait回调之后还要重新wait一下挺麻烦的

额外功能:

设备文件支持

boost::asio::serial_port 可以打开一个Unix设备文件,并作为输入输出流,然后可以用自由函数boost::asio::read(),boost::asio::async_read(),boost::asio::write(),boost::asio::async_write(),boost::asio::read_until() 和 boost::asio::async_read_until()操作这些文件

在Windows上,需要系统支持I/O completion port时才能使用,可以通过BOOST_ASIO_HAS_SERIAL_PORTS 这个宏来检测是否可用这个功能(如果定义了则可用)。

Unix系统功能

第一项是Unix Domain Sockets

这种socket可以通过

来使用,其他的部分和普通的Socket一样

第二项是指向流的文件描述符

posix::stream_descriptor in(my_io_service, ::dup(STDIN_FILENO)); posix::stream_descriptor out(my_io_service, ::dup(STDOUT_FILENO));

创建出的descriptor可以用asio的自由函数的读写函数操作

第三项是fork支持通过notify_fork函数来重建内部描述符

SSL支持

这部分依赖OpenSSL,简单的说,就是在socket外面包了一层,然后操作带ssl的socket。

大概就是

然后用ssl_socket代替tcp::socket

简要性能测试

  • 测试机器: CPU Intel(R) Xeon(R) CPU X3440 @ 2.53GHz × 8 , 内存 8096MB

  • 测试环境: Linux 2.6.32.43, GCC 4.8.0, 编译优化配置 -O2 -g -ggdb

  • 程序配置: 16 工作进程,1主进程,主线程参与逻辑

  • 测试结果: 5000-8000连接,每秒收到约320K个报文,7MB流量,每秒发送约320K个报文,12MB流量, CPU 负载: 180%(5000连接) – 195% (8000连接)

结论: 不知道为什么,压力再也上不去了, 我是把输出重定向到文件的,所以开始以为是磁盘IO爆了,写出日志次数太多,但是gperftools显示磁盘IO占用并不高,性能分布还是比较平均的。但是基本上就在16万个报文了(每个包有一次发送长度的包[4字节]和一次数据的send[不定长])

测试代码地址: https://gist.github.com/owt5008137/5660983 profile性能分析报告: http://api.owent.net/resource/doc/link/test.asio.pdf

主要就是这个样子,一些更底层的东西比如srand可以以后再看( ^ _ ^ )

Last updated

Was this helpful?