IOLoop

libtnet采用的是prefork + event loop的架构方式,prefork就是server在启动的时候预先fork多个子进程同时工作,而event loop则是基于epoll的事件处理机制。

在最新的linux系统中,提供了timerfd,eventfd,signalfd,加上原先的socket,大部分功能都可以抽象成io事件来处理了。而在libtnet中,这一切的基础就是IOLoop。

类似于tornado,libtnet的IOLoop也提供了相似的接口。其中最核心的就是以下三个:

typedef std::function<void (IOLoop*, int)> IOHandler_t;

int addHandler(int fd, int events, const IOHandler_t& handler);
int updateHandler(int fd, int events);
int removeHandler(int fd);

对于任意的IO,我们可以注册感兴趣的事件(TNET_READ和TNET_WRITE),并绑定一个对应的callback回调。

callback的回调采用的是std::function的方式,这也就意味着,你可以在外部通过std::bind绑定任意不同的实现,再加上shared_ptr技术模拟闭包。

假设现在我们需要创建了一个socket对象,并将其添加到IOLoop中,我们可以这么做:

std::shared_ptr<Connection> conn = std::make_shared<Connection>(socketfd);

ioloop->addHandler(socketfd, TNET_READ, std::bind(&Connection::onHandler, conn, _1, _2));

这样,当该socket有读事件发生的时候,对应的onHandler就会被调用。在这里,我是用了shared_ptr技术,主要是为了方便进行对象生命周期的管理。

在上面的例子中,因为std::bind的时候引用了conn,只要不将socketfd进行removeHandler,conn对象就会一直存在。所以libtnet在IOLoop内部,自行维护了conn对象的生命周期。外面不需要在将其保存到另一个地方(如果真保存了该shared_ptr的conn,反而会引起内存泄露)。在libtnet的基础模块中,我都使用的是weak_ptr来保存相关对象,每次使用都通过lock来判定是否该对象存活。

在IOLoop内部,我使用一个vector来存放注册的handler,vector的索引就是io的fd。这样,我们通过io的fd就可以非常快速的查找到对应的handler了。为什么可以这样设计,是因为在linux系统中,进程中新建文件的file descriptor都是系统当前最小的可用整数。譬如,我创建了一个socket,fd为10,然后我关闭了该socket,再次新建一个socket,这时候新的socket的fd仍然为最小可用的整数,也就是10。

EPoll

提到linux下面的高性能网络编程,epoll是一个铁定绕不开的话题,关于epoll的使用,网上有太多的讲解,这里就不展开了。

libtnet在Poller中集成了epoll,参考了libev的实现。epoll有两种工作模式,水平触发和边沿触发,各有利弊。libtnet使用的是水平触发方式,主要原因在于水平触发方式在有消息但是没处理的时候会一直通知你处理,实现起来不容易出错,也比较简单。

fork and epoll_create

这里顺便记录一下我在实现prefork模型的时候遇到的一个坑。这个问题就是epoll fd应该在fork之前还是之后创建?

大家都知道,linux fork的时候采用COW(copy on write)方式复制父进程的内容,然后我想当然的以为各个子进程会拥有独立的epoll内核空间,于是在fork之前创建了epoll fd。但是后面我却惊奇的发现一个子进程对epoll的操作竟然会影响另一个子进程。也就是说,各个子进程共享了父进程的epoll内核空间。

所以,epoll fd的创建应该在fork之后,各个子进程独立创建。

Example

Timer

IOLoop提供了一个简单的runAfter函数,用以实现定时器功能,使用非常简单:

void func(IOLoop* loop)
{
cout << "hello world" << endl;
loop->stop();
} IOLoop loop;
loop.runAfter(10 * 1000, std::bind(&func, &loop));
loop.start();

loop启动十秒之后,会打印hello world,然后整个loop退出。更多定制化的timer使用,可以使用libtnet提供的Timer class。

Callback

libtnet是一个单线程单ioloop的模型,但是不排除仍然会有其他线程希望与IOLoop进行通信,所以IOLoop提供了addCallback功能,这是libtnet唯一一个线程安全的函数。因为加入callback是一个很快速的操作,IOLoop使用了spinlock。在IOLoop每次循环的末尾,会将全部的callback取出,依次执行。

void callback(IOLoop* loop)
{
cout << "tell to exit" << endl;
loop->stop();
} IOLoop loop;
loop.addCallback(std::bind(&func, &loop));
loop.start();

最后,再次列出libtnet的地址https://github.com/siddontang/libtnet,欢迎围观。

高性能C++网络库libtnet实现:IOLoop的更多相关文章

  1. 高性能C++网络库libtnet实现:Connection

    Connection libtnet只支持IPv4 TCP Connection,之所以这么做都是为了使得实现尽可能的简单.我们主要在Connection类中封装了对tcp连接的操作. Connect ...

  2. 高性能C++网络库libtnet实现:http

    HTTP libtnet提供了简单的http支持,使用也很简单. 一个简单的http server: void onHandler(const HttpConnectionPtr_t& con ...

  3. 高性能C++网络库libtnet实践:comet单机百万连接挂载测试

    最近在用go语言做一个挂载大量长连接的推送服务器,虽然已经完成,但是内存占用情况让我不怎么满意,于是考虑使用libtnet来重新实现一个.后续我会使用comet来表明推送服务器. 对于comet来说, ...

  4. 发布一个参考tornado的高性能c++网络库:libtnet

    libtnet是一个用c++编写的高性能网络库,它在设计上面主要参考tornado,为服务端网络编程提供简洁而高效的接口,非常易于使用. Echo Server void onConnEvent(co ...

  5. 专注于HTTP的高性能高易用性网络库:Fslib.network库

    博客列表页:http://blog.fishlee.net/tag/fslib-network/ 原创FSLib.Network库(目前专注于HTTP的高性能高易用性网络库) FSLib.Networ ...

  6. [开源] gnet: 一个轻量级且高性能的 Golang 网络库

    Github 主页 https://github.com/panjf2000/gnet 欢迎大家围观~~,目前还在持续更新,感兴趣的话可以 star 一下暗中观察哦. 简介 gnet 是一个基于 Ev ...

  7. Cowboy 开源 WebSocket 网络库

    Cowboy.WebSockets 是一个托管在 GitHub 上的基于 .NET/C# 实现的开源 WebSocket 网络库,其完整的实现了 RFC 6455 (The WebSocket Pro ...

  8. 开源免费的C/C++网络库(c/c++ sockets library)

    (1)ACE 庞大.复杂,适合大型项目.开源.免费,不依赖第三方库,支持跨平台. http://www.cs.wustl.edu/~schmidt/ACE.html (2)Asio Asio基于Boo ...

  9. poco网络库分析,教你如何学习使用开源库

    Poco::Net库中有 FTPClient HTML HTTP HTTPClient HTTPServer ICMP Logging Mail Messages NetCore NTP OAuth ...

随机推荐

  1. Go 语言常量

    常量是一个简单值的标识符,在程序运行时,不会被修改的量. 常量中的数据类型只可以是布尔型.数字型(整数型.浮点型和复数)和字符串型. 常量的定义格式: const identifier [type] ...

  2. MongoDB 关系

    MongoDB 的关系表示多个文档之间在逻辑上的相互联系. 文档间可以通过嵌入和引用来建立联系. MongoDB 中的关系可以是: 1:1 (1对1) 1: N (1对多) N: 1 (多对1) N: ...

  3. Android 多窗口

    随着手机屏幕越来越大,单手操作手机越来越难,所以一些大厂早就开始研究多窗口,如iphone.samsung的单手模式,作为一个发展趋势google肯定也不会不考虑用户的体验,所以在android N中 ...

  4. Android 开发环境的搭建(新环境)

    最近想往Android 转型,所以又重新捡起Android学习.看了一下各位大神的文章,说的比较乱,因为版本不一样所以搭建过程也不一样,我在这里说一下最简单快捷的方式.(PS:那时候搭建环境好复杂啊, ...

  5. RxJava(二) map操作符用法详解

    欢迎转载,转载请标明出处: http://blog.csdn.net/johnny901114/article/details/51531348 本文出自:[余志强的博客] 1 map操作符的作用 R ...

  6. SpriteKit中为何不要在update方法中测试碰撞

    大熊猫猪·侯佩原创或翻译作品.欢迎转载,转载请注明出处. 如果觉得写的不好请多提意见,如果觉得不错请多多支持点赞.谢谢! hopy ;) 我们若要在游戏中做碰撞检测有2种办法,一是利用物理引擎,二是自 ...

  7. Hibernate通过SQL查询常量时只能返回第一个字符的解决方法

    在Hibernate中如果通过 [java] view plaincopy session.createSQLQuery("select '合计' as name from dual&quo ...

  8. 5.2、Android Studio截图

    Android Monitor允许你截取连接的设备或者虚拟机的屏幕,保存为PNG格式. 设备截图 1. 打开一个项目 2. 在设备或虚拟机中运行应用 3. 显示Android Monitor 4. 切 ...

  9. 解决Scrollview 嵌套recyclerview不能显示,高度不正常的问题

    我们先看一个效果,问题说的就是中间的Grid效果在Scrollview 嵌套recyclerview显示问题,在Android Api 24是好的,不过在5,1,1版本(api 22)缺出现了问题 最 ...

  10. UNIX网络编程——基于UDP协议的网络程序

    一.下图是典型的UDP客户端/服务器通讯过程 下面依照通信流程,我们来实现一个UDP回射客户/服务器: #include <sys/types.h> #include <sys/so ...