网络server通常都使用epoll进行异步IO处理,而开发人员通常使用mac,为了方便开发。我把自己的handy库移植到了mac平台上。

移植过程中,网上竟然没有搜到kqueue的使用样例。让我吃惊不已。为了让大家不用像我一样再次花费大力气搞定kqueue,我整理了一个简单清晰可执行的kqueue样例,供大家參考。

kqueue一共同拥有几个函数:

  1. int kqueue(void); //相似epoll_create
  2. int kevent(int kq, const struct kevent *changelist, int nchanges, struct kevent *eventlist, int nevents, const struct timespec *timeout); //兼具epoll_ctl及epoll_wait功能
  3. EV_SET(&kev, ident, filter, flags, fflags, data, udata); //设定kevent參数的宏
  4. struct kevent {
  5. uintptr_t ident; /* identifier for this event */
  6. int16_t filter; /* filter for event */
  7. uint16_t flags; /* general flags */
  8. uint32_t fflags; /* filter-specific flags */
  9. intptr_t data; /* filter-specific data */
  10. void *udata; /* opaque user data identifier */
  11. };

函数调用演示样例:

  1. //创建kqueue
  2. int epollfd = kqueue();
  3. //加入或者改动fd
  4. struct kevent ev[2];
  5. int n = 0;
  6. if (events & kReadEvent) {
  7. EV_SET(&ev[n++], fd, EVFILT_READ, EV_ADD|EV_ENABLE, 0, 0, (void*)(intptr_t)fd);
  8. } else if (modify){
  9. EV_SET(&ev[n++], fd, EVFILT_READ, EV_DELETE, 0, 0, (void*)(intptr_t)fd);
  10. }
  11. if (events & kWriteEvent) {
  12. EV_SET(&ev[n++], fd, EVFILT_WRITE, EV_ADD|EV_ENABLE, 0, 0, (void*)(intptr_t)fd);
  13. } else if (modify){
  14. EV_SET(&ev[n++], fd, EVFILT_WRITE, EV_DELETE, 0, 0, (void*)(intptr_t)fd);
  15. }
  16. printf("%s fd %d events read %d write %d\n",
  17. modify ?
  18. "mod" : "add", fd, events & kReadEvent, events & kWriteEvent);
  19. int r = kevent(efd, ev, n, NULL, 0, NULL);
  20. //获取ready的fd
  21. struct timespec timeout;
  22. timeout.tv_sec = waitms / 1000;
  23. timeout.tv_nsec = (waitms % 1000) * 1000 * 1000;
  24. const int kMaxEvents = 20;
  25. struct kevent activeEvs[kMaxEvents];
  26. int n = kevent(efd, NULL, 0, activeEvs, kMaxEvents, &timeout);
  27. //处理IO事件
  28. for (int i = 0; i < n; i ++) {
  29. int fd = (int)(intptr_t)activeEvs[i].udata;
  30. int events = activeEvs[i].filter;
  31. if (events == EVFILT_READ) {
  32. handleRead(efd, fd);
  33. } else if (events == EVFILT_WRITE) {
  34. handleWrite(efd, fd);
  35. }
  36. }

注意kevent与epoll最大的不同在于READ/WRITE事件是分开注冊而且分开返回的,而Epoll则是一个fd一次返回读和写事件,用标志位来推断。

能够执行的代码例如以下:kqueue-examplehandy对kqueue提供了封装版本号)

  1. #include <sys/socket.h>
  2. #include <sys/event.h>
  3. #include <netinet/in.h>
  4. #include <arpa/inet.h>
  5. #include <fcntl.h>
  6. #include <unistd.h>
  7. #include <stdio.h>
  8. #include <errno.h>
  9. #include <string.h>
  10. #include <stdlib.h>
  11. #define exit_if(r, ...) if(r) {printf(__VA_ARGS__); printf("error no: %d error msg %s\n", errno, strerror(errno)); exit(1);}
  12. const int kReadEvent = 1;
  13. const int kWriteEvent = 2;
  14. void setNonBlock(int fd) {
  15. int flags = fcntl(fd, F_GETFL, 0);
  16. exit_if(flags<0, "fcntl failed");
  17. int r = fcntl(fd, F_SETFL, flags | O_NONBLOCK);
  18. exit_if(r<0, "fcntl failed");
  19. }
  20. void updateEvents(int efd, int fd, int events, bool modify) {
  21. struct kevent ev[2];
  22. int n = 0;
  23. if (events & kReadEvent) {
  24. EV_SET(&ev[n++], fd, EVFILT_READ, EV_ADD|EV_ENABLE, 0, 0, (void*)(intptr_t)fd);
  25. } else if (modify){
  26. EV_SET(&ev[n++], fd, EVFILT_READ, EV_DELETE, 0, 0, (void*)(intptr_t)fd);
  27. }
  28. if (events & kWriteEvent) {
  29. EV_SET(&ev[n++], fd, EVFILT_WRITE, EV_ADD|EV_ENABLE, 0, 0, (void*)(intptr_t)fd);
  30. } else if (modify){
  31. EV_SET(&ev[n++], fd, EVFILT_WRITE, EV_DELETE, 0, 0, (void*)(intptr_t)fd);
  32. }
  33. printf("%s fd %d events read %d write %d\n",
  34. modify ? "mod" : "add", fd, events & kReadEvent, events & kWriteEvent);
  35. int r = kevent(efd, ev, n, NULL, 0, NULL);
  36. exit_if(r, "kevent failed ");
  37. }
  38. void handleAccept(int efd, int fd) {
  39. struct sockaddr_in raddr;
  40. socklen_t rsz = sizeof(raddr);
  41. int cfd = accept(fd,(struct sockaddr *)&raddr,&rsz);
  42. exit_if(cfd<0, "accept failed");
  43. sockaddr_in peer, local;
  44. socklen_t alen = sizeof(peer);
  45. int r = getpeername(cfd, (sockaddr*)&peer, &alen);
  46. exit_if(r<0, "getpeername failed");
  47. printf("accept a connection from %s\n", inet_ntoa(raddr.sin_addr));
  48. setNonBlock(cfd);
  49. updateEvents(efd, cfd, kReadEvent|kWriteEvent, false);
  50. }
  51. void handleRead(int efd, int fd) {
  52. char buf[4096];
  53. int n = 0;
  54. while ((n=::read(fd, buf, sizeof buf)) > 0) {
  55. printf("read %d bytes\n", n);
  56. int r = ::write(fd, buf, n); //写出读取的数据
  57. //实际应用中。写出数据可能会返回EAGAIN,此时应当监听可写事件。当可写时再把数据写出
  58. exit_if(r<=0, "write error");
  59. }
  60. if (n<0 && (errno == EAGAIN || errno == EWOULDBLOCK))
  61. return;
  62. exit_if(n<0, "read error"); //实际应用中,n<0应当检查各类错误,如EINTR
  63. printf("fd %d closed\n", fd);
  64. close(fd);
  65. }
  66. void handleWrite(int efd, int fd) {
  67. //实际应用应当实现可写时写出数据,无数据可写才关闭可写事件
  68. updateEvents(efd, fd, kReadEvent, true);
  69. }
  70. void loop_once(int efd, int lfd, int waitms) {
  71. struct timespec timeout;
  72. timeout.tv_sec = waitms / 1000;
  73. timeout.tv_nsec = (waitms % 1000) * 1000 * 1000;
  74. const int kMaxEvents = 20;
  75. struct kevent activeEvs[kMaxEvents];
  76. int n = kevent(efd, NULL, 0, activeEvs, kMaxEvents, &timeout);
  77. printf("epoll_wait return %d\n", n);
  78. for (int i = 0; i < n; i ++) {
  79. int fd = (int)(intptr_t)activeEvs[i].udata;
  80. int events = activeEvs[i].filter;
  81. if (events == EVFILT_READ) {
  82. if (fd == lfd) {
  83. handleAccept(efd, fd);
  84. } else {
  85. handleRead(efd, fd);
  86. }
  87. } else if (events == EVFILT_WRITE) {
  88. handleWrite(efd, fd);
  89. } else {
  90. exit_if(1, "unknown event");
  91. }
  92. }
  93. }
  94. int main() {
  95. short port = 99;
  96. int epollfd = kqueue();
  97. exit_if(epollfd < 0, "epoll_create failed");
  98. int listenfd = socket(AF_INET, SOCK_STREAM, 0);
  99. exit_if(listenfd < 0, "socket failed");
  100. struct sockaddr_in addr;
  101. memset(&addr, 0, sizeof addr);
  102. addr.sin_family = AF_INET;
  103. addr.sin_port = htons(port);
  104. addr.sin_addr.s_addr = INADDR_ANY;
  105. int r = ::bind(listenfd,(struct sockaddr *)&addr, sizeof(struct sockaddr));
  106. exit_if(r, "bind to 0.0.0.0:%d failed %d %s", port, errno, strerror(errno));
  107. r = listen(listenfd, 20);
  108. exit_if(r, "listen failed %d %s", errno, strerror(errno));
  109. printf("fd %d listening at %d\n", listenfd, port);
  110. setNonBlock(listenfd);
  111. updateEvents(epollfd, listenfd, kReadEvent, false);
  112. for (;;) { //实际应用应当注冊信号处理函数。退出时清理资源
  113. loop_once(epollfd, listenfd, 10000);
  114. }
  115. return 0;
  116. }

kqueue演示样例的更多相关文章

  1. Python Web框架Tornado的异步处理代码演示样例

    1. What is Tornado Tornado是一个轻量级但高性能的Python web框架,与还有一个流行的Python web框架Django相比.tornado不提供操作数据库的ORM接口 ...

  2. JDBC连接MySQL数据库及演示样例

    JDBC是Sun公司制定的一个能够用Java语言连接数据库的技术. 一.JDBC基础知识         JDBC(Java Data Base Connectivity,java数据库连接)是一种用 ...

  3. java 覆盖hashCode()深入探讨 代码演示样例

    java 翻盖hashCode()深入探讨 代码演示样例 package org.rui.collection2.hashcode; /** * 覆盖hashcode * 设计HashCode时最重要 ...

  4. 模式识别 - 处理多演示样例学习(MIL)特征(matlab)

    处理多演示样例学习(MIL)特征(matlab) 本文地址: http://blog.csdn.net/caroline_wendy/article/details/27206325 多演示样例学习( ...

  5. java并行调度框架封装及演示样例

    參考资料:  阿里巴巴开源项目 CobarClient  源代码实现. 分享作者:闫建忠 分享时间:2014年5月7日 ---------------------------------------- ...

  6. Java连接redis的使用演示样例

    Java连接redis的使用演示样例 Redis是开源的key-value存储工具,redis通经常使用来存储结构化的数据,由于redis的key能够包括String.hash.listset和sor ...

  7. Introspector(内省)简单演示样例 与 简单应用

    简单演示样例: package com.asdfLeftHand.test; import java.beans.BeanDescriptor; import java.beans.BeanInfo; ...

  8. libcurl使用演示样例

    简要说明:C++使用libcurl訪问"www.baidu.com".获取返回码和打印出http文件 /* * @ libcurl使用演示样例 * @ 2014.04.29 * @ ...

  9. 构造Scala开发环境并创建ApiDemos演示样例项目

    从2011年開始写Android ApiDemos 以来.Android的版本号也更新了非常多,眼下的版本号已经是4.04. ApiDemos中的样例也添加了不少,有必要更新Android ApiDe ...

随机推荐

  1. JavaScript、SSH知识点整理

    七.Javascript部分 1:什么是Javascript JavaScript是一种基于对象(Object)和事件驱动(Event Driven)并具有安全性能的脚本语言. 2:Java和Java ...

  2. ShareREC for iOS v1.0.4 已经公布

    ShareREC for iOS v1.0.4 已经公布 版本号:v1.0.4 2015-3-13 1.新增视频列表的筛选排序功能 2.修复在開始录制后,没有调用结束录制直接进入社区崩溃问题 3.优化 ...

  3. Python 下的 return 关键字

    def make_sum(a, b): return ('+', a, b) >> make_sum(1, 2) ('+', 1, 2) 显示地返回一个元组(tuple),当然 retur ...

  4. [JavaEE] Maven简介

    转载自:百度 http://baike.baidu.com/view/336103.htm?fr=aladdin 一.简介 Maven是基于项目对象模型(POM),可以通过一小段描述信息来管理项目的构 ...

  5. 如何在ubuntu中安装mysql与mysql workbench

    安装过程如下 sudo apt-get install mysql-server 安装过程中随后设置mysql的密码 之后sudo apt-get install mysql-client 安装好之后 ...

  6. CLR - 基础

    前言 好记性不如烂“笔头”系列... 目录 托管模块 JIT(just-in-time) 元数据 CLR 解析类型引用 托管模块 面向 CLR 的编译器在编译源文件时最终会编译成一个 PE(可移植执行 ...

  7. Creative Cloud 无法连接问题

    防火墙允许 PDApp.exe Windows:Program Files\Common Files\Adobe\OOBE\PDApp\core Mac OS:应用程序 > 实用工具 > ...

  8. MySQL中的存储函数和存储过程的简单示例

    存储函数 定义 CREATE FUNCTION `fn_sum`(`a` int,`b` int) RETURNS int(11) BEGIN RETURN a + b; END 调用 Navicat ...

  9. 设置cookie,删除cookie,读取cookie

    1.首先来说下cookie的作用 我们在浏览器中,经常涉及到数据的交换,比如你登录邮箱,登录一个页面.我们经常会在此时设置30天内记住我,或者自动登录选项.那么它们是怎么记录信息的呢,答案就是今天的主 ...

  10. AlertDialog的使用

    1.Alertdialog的几种形式: 2.第一种:简单对话框 AlertDialog.Builder localBuilder = new AlertDialog.Builder(this); lo ...