高性能C++网络库libtnet实现:IOLoop
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的更多相关文章
- 高性能C++网络库libtnet实现:Connection
Connection libtnet只支持IPv4 TCP Connection,之所以这么做都是为了使得实现尽可能的简单.我们主要在Connection类中封装了对tcp连接的操作. Connect ...
- 高性能C++网络库libtnet实现:http
HTTP libtnet提供了简单的http支持,使用也很简单. 一个简单的http server: void onHandler(const HttpConnectionPtr_t& con ...
- 高性能C++网络库libtnet实践:comet单机百万连接挂载测试
最近在用go语言做一个挂载大量长连接的推送服务器,虽然已经完成,但是内存占用情况让我不怎么满意,于是考虑使用libtnet来重新实现一个.后续我会使用comet来表明推送服务器. 对于comet来说, ...
- 发布一个参考tornado的高性能c++网络库:libtnet
libtnet是一个用c++编写的高性能网络库,它在设计上面主要参考tornado,为服务端网络编程提供简洁而高效的接口,非常易于使用. Echo Server void onConnEvent(co ...
- 专注于HTTP的高性能高易用性网络库:Fslib.network库
博客列表页:http://blog.fishlee.net/tag/fslib-network/ 原创FSLib.Network库(目前专注于HTTP的高性能高易用性网络库) FSLib.Networ ...
- [开源] gnet: 一个轻量级且高性能的 Golang 网络库
Github 主页 https://github.com/panjf2000/gnet 欢迎大家围观~~,目前还在持续更新,感兴趣的话可以 star 一下暗中观察哦. 简介 gnet 是一个基于 Ev ...
- Cowboy 开源 WebSocket 网络库
Cowboy.WebSockets 是一个托管在 GitHub 上的基于 .NET/C# 实现的开源 WebSocket 网络库,其完整的实现了 RFC 6455 (The WebSocket Pro ...
- 开源免费的C/C++网络库(c/c++ sockets library)
(1)ACE 庞大.复杂,适合大型项目.开源.免费,不依赖第三方库,支持跨平台. http://www.cs.wustl.edu/~schmidt/ACE.html (2)Asio Asio基于Boo ...
- poco网络库分析,教你如何学习使用开源库
Poco::Net库中有 FTPClient HTML HTTP HTTPClient HTTPServer ICMP Logging Mail Messages NetCore NTP OAuth ...
随机推荐
- 2.docker常用命令
一.安装相关 #查看docker是否安装 rpm -q docker #CentOS下安装docker sudo yum install docker #启动 Docker systemctl s ...
- springMVC源码分析--ModelFactory
ModelFactory是用来维护Model的,具体包含两个功能 (1)初始化Model (2)处理器执行后将Model中相应的参数更新到SessionAttributes中 1.初始化Model其实 ...
- Gradle 1.12用户指南翻译——第四十五章. 应用程序插件
本文由CSDN博客貌似掉线翻译,其他章节的翻译请参见: http://blog.csdn.net/column/details/gradle-translation.html 翻译项目请关注Githu ...
- Android重绘ListView高度
Android重绘ListView高度 经常会有这样需求,需要ListView默认将所有的条目显示出来,这就需要外层使用ScrollView,ScrollView里面放置一个重绘高度的ListView ...
- 剑指Offer——中国银行面试知识储备
剑指Offer--中国银行面试知识储备+面试内容 事件介绍 时间:2016.11.23 08:30 地点:北京市海淀区永丰路299号南门(中国银行软件中心) 事件:中国银行面试(中英文面试) 注意事项 ...
- Android 使用DownloadManager进行版本更新的完整方案
在Android App都会有版本更新的功能,以前我们公司是用友盟SDK更新功能,自己服务器没有这样的功能.版本检测.Apk下载都是使用友盟.最近看到友盟的版本更新SDK文档:十月份更新功能将会停止服 ...
- activiti节点跳转
分享牛原创(尊重原创 转载对的时候第一行请注明,转载出处来自分享牛http://blog.csdn.net/qq_30739519) activiti使用的时候,通常需要跟业务紧密的结合在一起,有些业 ...
- 解决Setting property 'source' to 'org.eclipse.jst.jee.server的问题
对于这个问题,我相信我的方法已经能帮90%的人解决了! 当你用Eclipse运行web项目的时候,你就会看到控制台出现: WARNING: [SetPropertiesRule]{Server/Ser ...
- UE4实现闪烁效果
官网文档链接:http://docs.unrealengine.com/latest/CHN/Engine/Rendering/Materials/ExpressionReference/Math/i ...
- Protobuf-net判断字段是否有值
Protobuf-net判断字段是否有值Unity3d使用Protobuf-net序列化数据与服务器通信,但是发现默认情况下,Protobuf-net生成的cs文件中没有接口判断可选参数是否有值.需有 ...