kqueue例子
网络服务器通常都使用epoll进行异步IO处理,而开发者通常使用mac,为了方便开发,我把自己的handy库移植到了mac平台上。移植过程中,网上居然没有搜到kqueue的使用例子,让我惊讶不已。为了让大家不用像我一样再次花费大力气搞定kqueue,我整理了一个简单清晰可运行的kqueue例子,供大家参考。
kqueue一共有几个函数:
//类似epoll_create
int kqueue(void);
//兼具epoll_ctl及epoll_wait功能
int kevent(int kq, const struct kevent *changelist, int nchanges, struct kevent *eventlist, int nevents, const struct timespec *timeout);
//设定kevent参数的宏
EV_SET(&kev, ident, filter, flags, fflags, data, udata);
struct kevent {
uintptr_t ident; /* identifier for this event */
int16_t filter; /* filter for event */
uint16_t flags; /* general flags */
uint32_t fflags; /* filter-specific flags */
intptr_t data; /* filter-specific data */
void *udata; /* opaque user data identifier */
};
函数调用示例:
//创建kqueue
int epollfd = kqueue();
//添加或者修改fd
struct kevent ev[];
int n = ;
if (events & kReadEvent) {
EV_SET(&ev[n++], fd, EVFILT_READ, EV_ADD|EV_ENABLE, , , (void*)(intptr_t)fd);
} else if (modify){
EV_SET(&ev[n++], fd, EVFILT_READ, EV_DELETE, , , (void*)(intptr_t)fd);
}
if (events & kWriteEvent) {
EV_SET(&ev[n++], fd, EVFILT_WRITE, EV_ADD|EV_ENABLE, , , (void*)(intptr_t)fd);
} else if (modify){
EV_SET(&ev[n++], fd, EVFILT_WRITE, EV_DELETE, , , (void*)(intptr_t)fd);
}
printf("%s fd %d events read %d write %d\n",
modify ? "mod" : "add", fd, events & kReadEvent, events & kWriteEvent);
int r = kevent(efd, ev, n, NULL, , NULL);
//获取ready的fd
struct timespec timeout;
timeout.tv_sec = waitms / ;
timeout.tv_nsec = (waitms % ) * * ;
const int kMaxEvents = ;
struct kevent activeEvs[kMaxEvents];
int n = kevent(efd, NULL, , activeEvs, kMaxEvents, &timeout);
//处理IO事件
for (int i = ; i < n; i ++) {
int fd = (int)(intptr_t)activeEvs[i].udata;
int events = activeEvs[i].filter;
if (events == EVFILT_READ) {
handleRead(efd, fd);
} else if (events == EVFILT_WRITE) {
handleWrite(efd, fd);
}
}
注意kevent与epoll最大的不同在于READ/WRITE事件是分开注册并且分开返回的,而Epoll则是一个fd一次返回读和写事件,用标志位来判断。
可以运行的代码如下:kqueue-example(handy对kqueue提供了封装版本)
#include <sys/socket.h>
#include <sys/event.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h> #define exit_if(r, ...) if(r) {printf(__VA_ARGS__); printf("error no: %d error msg %s\n", errno, strerror(errno)); exit(1);} const int kReadEvent = ;
const int kWriteEvent = ; void setNonBlock(int fd) {
int flags = fcntl(fd, F_GETFL, );
exit_if(flags<, "fcntl failed");
int r = fcntl(fd, F_SETFL, flags | O_NONBLOCK);
exit_if(r<, "fcntl failed");
} void updateEvents(int efd, int fd, int events, bool modify) {
struct kevent ev[];
int n = ;
if (events & kReadEvent) {
EV_SET(&ev[n++], fd, EVFILT_READ, EV_ADD|EV_ENABLE, , , (void*)(intptr_t)fd);
} else if (modify){
EV_SET(&ev[n++], fd, EVFILT_READ, EV_DELETE, , , (void*)(intptr_t)fd);
}
if (events & kWriteEvent) {
EV_SET(&ev[n++], fd, EVFILT_WRITE, EV_ADD|EV_ENABLE, , , (void*)(intptr_t)fd);
} else if (modify){
EV_SET(&ev[n++], fd, EVFILT_WRITE, EV_DELETE, , , (void*)(intptr_t)fd);
}
printf("%s fd %d events read %d write %d\n",
modify ? "mod" : "add", fd, events & kReadEvent, events & kWriteEvent);
int r = kevent(efd, ev, n, NULL, , NULL);
exit_if(r, "kevent failed ");
} void handleAccept(int efd, int fd) {
struct sockaddr_in raddr;
socklen_t rsz = sizeof(raddr);
int cfd = accept(fd,(struct sockaddr *)&raddr,&rsz);
exit_if(cfd<, "accept failed");
sockaddr_in peer, local;
socklen_t alen = sizeof(peer);
int r = getpeername(cfd, (sockaddr*)&peer, &alen);
exit_if(r<, "getpeername failed");
printf("accept a connection from %s\n", inet_ntoa(raddr.sin_addr));
setNonBlock(cfd);
updateEvents(efd, cfd, kReadEvent|kWriteEvent, false);
} void handleRead(int efd, int fd) {
char buf[];
int n = ;
while ((n=::read(fd, buf, sizeof buf)) > ) {
printf("read %d bytes\n", n);
int r = ::write(fd, buf, n); //写出读取的数据
//实际应用中,写出数据可能会返回EAGAIN,此时应当监听可写事件,当可写时再把数据写出
exit_if(r<=, "write error");
}
if (n< && (errno == EAGAIN || errno == EWOULDBLOCK))
return;
exit_if(n<, "read error"); //实际应用中,n<0应当检查各类错误,如EINTR
printf("fd %d closed\n", fd);
close(fd);
} void handleWrite(int efd, int fd) {
//实际应用应当实现可写时写出数据,无数据可写才关闭可写事件
updateEvents(efd, fd, kReadEvent, true);
} void loop_once(int efd, int lfd, int waitms) {
struct timespec timeout;
timeout.tv_sec = waitms / ;
timeout.tv_nsec = (waitms % ) * * ;
const int kMaxEvents = ;
struct kevent activeEvs[kMaxEvents];
int n = kevent(efd, NULL, , activeEvs, kMaxEvents, &timeout);
printf("epoll_wait return %d\n", n);
for (int i = ; i < n; i ++) {
int fd = (int)(intptr_t)activeEvs[i].udata;
int events = activeEvs[i].filter;
if (events == EVFILT_READ) {
if (fd == lfd) {
handleAccept(efd, fd);
} else {
handleRead(efd, fd);
}
} else if (events == EVFILT_WRITE) {
handleWrite(efd, fd);
} else {
exit_if(, "unknown event");
}
}
} int main() {
short port = ;
int epollfd = kqueue();
exit_if(epollfd < , "epoll_create failed");
int listenfd = socket(AF_INET, SOCK_STREAM, );
exit_if(listenfd < , "socket failed");
struct sockaddr_in addr;
memset(&addr, , sizeof addr);
addr.sin_family = AF_INET;
addr.sin_port = htons(port);
addr.sin_addr.s_addr = INADDR_ANY;
int r = ::bind(listenfd,(struct sockaddr *)&addr, sizeof(struct sockaddr));
exit_if(r, "bind to 0.0.0.0:%d failed %d %s", port, errno, strerror(errno));
r = listen(listenfd, );
exit_if(r, "listen failed %d %s", errno, strerror(errno));
printf("fd %d listening at %d\n", listenfd, port);
setNonBlock(listenfd);
updateEvents(epollfd, listenfd, kReadEvent, false);
for (;;) { //实际应用应当注册信号处理函数,退出时清理资源
loop_once(epollfd, listenfd, );
}
return ;
}
kqueue例子的更多相关文章
- 可扩展的事件复用技术:epoll和kqueue
通常来说我喜欢Linux更甚于BSD系统,但是我真的想在Linux上拥有BSD的kqueue功能. 什么是事件复用技术 假设你有一个简单的web服务器,并且那里已经打开了两个socket连接.当服务器 ...
- 再谈select, iocp, epoll,kqueue及各种I/O复用机制
原文:http://blog.csdn.net/shallwake/article/details/5265287 首先,介绍几种常见的I/O模型及其区别,如下: blocking I/O nonbl ...
- Kqueue与epoll机制
首先介绍阻塞与非阻塞:阻塞是个什么概念呢?比如某个时候你在等快递,但是你不知道快递什么时候过来,而且你没有别的事可以干(或者说接下来的事要等快递来了才能做):那么你可以去睡觉了,因为你知道快递把货送来 ...
- [转]Kqueue与epoll机制
首先介绍阻塞与非阻塞:阻塞是个什么概念呢?比如某个时候你在等快递,但是你不知道快递什么时候过来,而且你没有别的事可以干(或者说接下来的事要等快递来了才能做):那么你可以去睡觉了,因为你知道快递把货送来 ...
- select, iocp, epoll,kqueue及各种I/O复用机制
http://blog.csdn.net/heyan1853/article/details/6457362 首先,介绍几种常见的I/O模型及其区别,如下: blocking I/O nonblock ...
- 【网络】再谈select, iocp, epoll,kqueue及各种I/O复用机制 && Reactor与Proactor的概念
首先,介绍几种常见的I/O模型及其区别,如下: blocking I/O nonblocking I/O I/O multiplexing (select and poll) signal drive ...
- epoll 或者 kqueue 的原理是什么?
来自知乎:http://www.zhihu.com/question/20122137 epoll 或者 kqueue 的原理是什么? 为什么epoll和kqueue可以用基于事件的方式,单线程的实现 ...
- 转: 再谈select, iocp, epoll,kqueue及各种I/O复用机制
首先,介绍几种常见的I/O模型及其区别,如下: blocking I/O nonblocking I/O I/O multiplexing (select and poll) signal drive ...
- 高级IO模型之kqueue和epoll
目录 简介 block IO和nonblocking IO IO多路复用和select poll epoll kqueue epoll和kqueue的优势 简介 任何一个程序都离不开IO,有些是很明显 ...
随机推荐
- NodeJs之pm2
pm2 pm2是一个进程管理工具,可以用它来管理你的node进程,并查看node进程的状态,当然也支持性能监控,进程守护,负载均衡等功能. 开发过程中建议时不时的参看官方详细命令行使用:命令行 pm2 ...
- 【疯狂造轮子-iOS】JSON转Model系列之一
[疯狂造轮子-iOS]JSON转Model系列之一 本文转载请注明出处 —— polobymulberry-博客园 1. 前言 之前一直看别人的源码,虽然对自己提升比较大,但毕竟不是自己写的,很容易遗 ...
- TODO:Laravel增加验证码
TODO:Laravel增加验证码1. 先聊聊验证码是什么,有什么作用?验证码(CAPTCHA)是"Completely Automated Public Turing test to te ...
- 一个表缺失索引发的CPU资源瓶颈案例
背景 近几日,公司的应用团队反应业务系统突然变慢了,之前是一直比较正常.后与业务部门沟通了解详情,得知最近生意比较好,同时也在做大的促销活动,使得业务数据处理的量出现较大的增长,最终系统在处理时出现瓶 ...
- css3线条围绕跑马+jquery打字机效果
原文地址:css3线条围绕跑马+jquery打字机效果 有图有真相,今天偶然看到了一种效果,仔细看了下,发现它是用css的clip+css3的动画实现的,简直叼.于是自己拿来了前一阵子写的打字机效果, ...
- 漫谈TCP
不得不承认,tcp是一个非常复杂的协议.它包含了RFC793及之后的一些协议.能把tcp的所有方面面面具到地说清楚,本身就是个很复杂的事情.如果再讲得枯燥,那么就会更让人昏昏欲睡了.本文希望能尽量用稍 ...
- C# 自定义控件VS用户控件
1 自定义控件与用户控件区别 WinForm中, 用户控件(User Control):继承自 UserControl,主要用于开发 Container 控件,Container控件可以添加其他Con ...
- 【教程】SQLite数据库修复
SQLite 大家都知道,就不多说了. 有时候数据量大了,或者存储过程中出现异常,数据库就可能会出问题. 这是以前公司产品出现过的问题,导致软件都打不开了,我花了不少时间才解决的,趁现在有空贡献出来. ...
- MySQL 数据库双向同步复制
MySQL 复制问题的最后一篇,关于双向同步复制架构设计的一些设计要点与制约. 问题和制约 数据库的双主双写并双向同步场景,主要考虑数据完整性.一致性和避免冲突.对于同一个库,同一张表,同一个记录中的 ...
- Unity3D新手引导开发手记
最近开始接手新手引导的开发,记录下这块相关的心得 首先客户端是Unity,在接手前,前面的同学已经初步完成了新手引导框架的搭建,这套框架比较简单,有优点也有缺点,稍后一一点评 我们的新手引导是由一个个 ...