kqueue演示样例

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

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

kqueue一共同拥有几个函数:

int  kqueue(void); //相似epoll_create
int kevent(int kq, const struct kevent *changelist, int nchanges, struct kevent *eventlist, int nevents, const struct timespec *timeout); //兼具epoll_ctl及epoll_wait功能
EV_SET(&kev, ident, filter, flags, fflags, data, udata); //设定kevent參数的宏
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[2];
    int n = 0;
    if (events & kReadEvent) {
        EV_SET(&ev[n++], fd, EVFILT_READ, EV_ADD|EV_ENABLE, 0, 0, (void*)(intptr_t)fd);
    } else if (modify){
        EV_SET(&ev[n++], fd, EVFILT_READ, EV_DELETE, 0, 0, (void*)(intptr_t)fd);
    }
    if (events & kWriteEvent) {
        EV_SET(&ev[n++], fd, EVFILT_WRITE, EV_ADD|EV_ENABLE, 0, 0, (void*)(intptr_t)fd);
    } else if (modify){
        EV_SET(&ev[n++], fd, EVFILT_WRITE, EV_DELETE, 0, 0, (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, 0, NULL);
    //获取ready的fd
    struct timespec timeout;
    timeout.tv_sec = waitms / 1000;
    timeout.tv_nsec = (waitms % 1000) * 1000 * 1000;
    const int kMaxEvents = 20;
    struct kevent activeEvs[kMaxEvents];
    int n = kevent(efd, NULL, 0, activeEvs, kMaxEvents, &timeout);
    //处理IO事件
    for (int i = 0; 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 = 1;
const int kWriteEvent = 2;

void setNonBlock(int fd) {
    int flags = fcntl(fd, F_GETFL, 0);
    exit_if(flags<0, "fcntl failed");
    int r = fcntl(fd, F_SETFL, flags | O_NONBLOCK);
    exit_if(r<0, "fcntl failed");
}

void updateEvents(int efd, int fd, int events, bool modify) {
    struct kevent ev[2];
    int n = 0;
    if (events & kReadEvent) {
        EV_SET(&ev[n++], fd, EVFILT_READ, EV_ADD|EV_ENABLE, 0, 0, (void*)(intptr_t)fd);
    } else if (modify){
        EV_SET(&ev[n++], fd, EVFILT_READ, EV_DELETE, 0, 0, (void*)(intptr_t)fd);
    }
    if (events & kWriteEvent) {
        EV_SET(&ev[n++], fd, EVFILT_WRITE, EV_ADD|EV_ENABLE, 0, 0, (void*)(intptr_t)fd);
    } else if (modify){
        EV_SET(&ev[n++], fd, EVFILT_WRITE, EV_DELETE, 0, 0, (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, 0, 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<0, "accept failed");
    sockaddr_in peer, local;
    socklen_t alen = sizeof(peer);
    int r = getpeername(cfd, (sockaddr*)&peer, &alen);
    exit_if(r<0, "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[4096];
    int n = 0;
    while ((n=::read(fd, buf, sizeof buf)) > 0) {
        printf("read %d bytes\n", n);
        int r = ::write(fd, buf, n); //写出读取的数据
        //实际应用中。写出数据可能会返回EAGAIN,此时应当监听可写事件。当可写时再把数据写出
        exit_if(r<=0, "write error");
    }
    if (n<0 && (errno == EAGAIN || errno == EWOULDBLOCK))
        return;
    exit_if(n<0, "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 / 1000;
    timeout.tv_nsec = (waitms % 1000) * 1000 * 1000;
    const int kMaxEvents = 20;
    struct kevent activeEvs[kMaxEvents];
    int n = kevent(efd, NULL, 0, activeEvs, kMaxEvents, &timeout);
    printf("epoll_wait return %d\n", n);
    for (int i = 0; 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(1, "unknown event");
        }
    }
}

int main() {
    short port = 99;
    int epollfd = kqueue();
    exit_if(epollfd < 0, "epoll_create failed");
    int listenfd = socket(AF_INET, SOCK_STREAM, 0);
    exit_if(listenfd < 0, "socket failed");
    struct sockaddr_in addr;
    memset(&addr, 0, 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, 20);
    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, 10000);
    }
    return 0;
}
时间: 2024-10-10 13:04:23

kqueue演示样例的相关文章

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

1. What is Tornado Tornado是一个轻量级但高性能的Python web框架,与还有一个流行的Python web框架Django相比.tornado不提供操作数据库的ORM接口及严格的MVC开发模式,但能够提供主要的web server功能.故它是轻量级的:它借助non-blocking and event-driven的I/O模型(epoll或kqueue)实现了一套异步网络库,故它是高性能的. Tornado的轻量级+高性能特性使得它特别适用于提供web api的场合

最简单的视音频播放演示样例4:Direct3D播放RGB(通过Texture)

===================================================== 最简单的视音频播放演示样例系列文章列表: 最简单的视音频播放演示样例1:总述 最简单的视音频播放演示样例2:GDI播放YUV, RGB 最简单的视音频播放演示样例3:Direct3D播放YUV,RGB(通过Surface) 最简单的视音频播放演示样例4:Direct3D播放RGB(通过Texture) 最简单的视音频播放演示样例5:OpenGL播放RGB/YUV 最简单的视音频播放演示样例

内存损坏问题的演示样例及分析

原文以演示样例代码系统的讲述了三种内存损坏的情况: 全局内存.栈损坏及堆损坏, 以及它们产生的原因. 粗略整理例如以下. Global Memory Corruption 即全局变量的内存使用出了问题,主要还是越界. 例如以下代码: #include <stdio.h> #define MAX 6 int arrdata[MAX]; int endval; int main() { int i = 0; endval = 12; for (i = MAX; (endval) &&

PHPCMS中GET标签概述、 get 标签语法、get 标签创建工具、get 调用本系统演示样例、get 调用其它系统演示样例

一.get 标签概述 通俗来讲,get 标签是Phpcms定义的能直接调用数据库里面内容的简单化.友好化代码,她可调用本系统和外部数据,仅仅有你对SQL有一定的了解,她就是你的绝世好剑!也就是适合熟悉SQL语句的人使用.有了她,我们打造个性化的站点,能很方便的调用出数据库里面指定的内容.通过条件限制,我们能够调用出不同条件下的不同数据. 二.get标签样式 {get dbsource=" " sql=" "} {/get} 三.get 标签语法 1.get标签属性值

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

JDBC是Sun公司制定的一个能够用Java语言连接数据库的技术. 一.JDBC基础知识         JDBC(Java Data Base Connectivity,java数据库连接)是一种用于执行SQL语句的Java API,能够为多种关系数据库提供统一訪问,它由一组用Java语言编写的类和接口组成.JDBC为数据库开发者提供了一个标准的API,据此能够构建更高级的工具和接口,使数据库开发者能够用纯 Java API 编写数据库应用程序,而且可跨平台执行,而且不受数据库供应商的限制.

java设计模式演示样例

创建模式 1.工厂方法模式(Factory Method)  将程序中创建对象的操作,单独出来处理,创建一个产品的工厂接口,把实际的工作转移到详细的子类.大大提高了系统扩展的柔性,接口的抽象化处理给相互依赖的对象创建提供了最好的抽象模式. public class TestFactoryMethod { public static void main(String[] args) { AnimalFactory af=new DogFactory(); Animal1 a=af.getAnima

展示C代码覆盖率的gcovr工具简单介绍及相关命令使用演示样例

(本人正在參加2015博客之星评选,诚邀你来投票,谢谢:username=zhouzxi">http://vote.blog.csdn.net/blogstar2015/candidate?username=zhouzxi) 近期,由于要展示某项目的单元測试的代码覆盖率.我无意间在网上找到了gcovr工具.使用之后,认为这个工具相当的不错,于是便写下这篇文章,可供相关的开发者參考. 简而言之,gcovr是一个将单元測试中的代码覆盖率以多种方式(包含列表方式.XML文件方式.HTML网页方式

Android线程池(二)——ThreadPoolExecutor及其拒绝策略RejectedExecutionHandler使用演示样例

MainActivity例如以下: package cc.vv; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; import android.os.Bundle; import android.app.Activity; /** * Demo描写叙述: * 线程池(Threa

Android平台调用Web Service:演示样例

近期在学习Android,随着移动设备的流行,当软件走上商业化的道路.为了争夺市场,肯定须要支持Android的,所以開始接触了Android,只是仅仅了解皮毛就好,由于我们要做管理者嘛.懂点Android.管理起来easy些. Android学起来也简单,封装的更好了,一个个的控件,像是又回到了VB的赶脚. 以下将通过一个演示样例解说怎样在Android平台调用Web Service. 我们使用互联网现成的Webservice.供查询手机号码归属地的Web service,它的WSDL为htt