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. ftp传二进制文件时一定要用二进制模式,否则内容会有变化,造成后处理莫名其妙的错误,还以为传输前后内容一致,其实已变化。

    ftp传二进制文件时一定要用二进制模式,否则内容会有变化,造成后处理莫名其妙的错误,还以为传输前后内容一致,其实已变化.

  2. RHEL Linux常用指令

    查询已安装软件包 rpm -qa|grep * 安装软件 rpm -ivh * 查询Linux版本 uname -a lsb_release -a cat /etc/redhat-release ca ...

  3. Java语言程序设计-助教篇

    1. 给第一次上课(软件工程)的老师与助教 现代软件工程讲义 0 课程概述 给学生:看里面的第0个作业要求 2. 助教心得 美国视界(1):第一流的本科课堂该是什么样?(看里面的助教部分) 助教工作看 ...

  4. Webpack 2 设置为从当前文件夹逐级向上查找模块

    比较实用, 当你在cd到子文件夹运行webpack时,你可能想要require文件夹js里面的一些模块, 但你又想将祖先的js文件夹作为fallback.这样设置即可: module.exports ...

  5. Bootstrap3 排版-标题

    HTML 中的所有标题标签,<h1> 到 <h6> 均可使用.另外,还提供了 .h1 到 .h6 类,为的是给内联(inline)属性的文本赋予标题的样式. h1. Boots ...

  6. jboss规则引擎KIE Drools 6.3.0 Final 教程(3)

    在前2部教程中,介绍了如何在本地运行.drools文件以及使用stateless的方法访问远程repository上的规则. KIE Drools还提供了一种叫有状态-stateful的访问方式. 运 ...

  7. TCP发送源码学习(3)--tcp_transmit_skb

    一.tcp_transmit_skb static int tcp_transmit_skb(struct sock *sk, struct sk_buff *skb, int clone_it, g ...

  8. 初识RecyclerView

    初识RecyclerView 效果图 RecyclerView与ListView对比(官方) RecyclerView 小组件比 ListView 更高级且更具灵活性. 此小组件是一个用于显示庞大数据 ...

  9. Android开发学习之路--逆向分析反编译

      一般情况下我们想要了解别人的app怎么实现这个动画,这个效果的时候,总是会想到反编译一下,看下布局,看下代码实现.对,这对于有经验的玩家确实手到擒来了,但是初学者,根本就不知道怎么反编译,怎么看代 ...

  10. Python 3.3.3 使用requests模拟登录网站

    在模拟登录上,requests确实比python标准库中的相关模块更加简洁. 假设你需要去爬一组页面(targetUrls),而这些页面要登录才能进行访问.那么requests能够提供一种相当简单的语 ...