V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
爱意满满的作品展示区。
luohaha
V2EX  ›  分享创造

c 写的一个简单的事件驱动库 libel

  •  
  •   luohaha · 2016-06-11 22:41:31 +08:00 · 2709 次点击
    这是一个创建于 3087 天前的主题,其中的信息可能已经有所发展或是发生改变。

    libel 是什么

    一个类似 libevent , libev 的事件驱动库,当然目前的功能还非常简单,不过基本框架已建立,功能会慢慢加上。
    目前支持 freeBSD 和 linux 。在 freeBSD 上使用kqueue,在 linux 上使用epoll

    项目地址

    https://github.com/luohaha/libel

    api

    el_loop *el_loop_new()
    

    创建新的 event loop

    event *el_event_new(int fd, int flags, void (*cb) (int fd, int size, void *arg), void arg)
    

    创建新的事件.
    fd : the file descriptor that you want to listen.
    flags : READ_EVENT or WRITE_EVENT or both
    cb : callback function that will be called by libel
    arg : it will be pass to callback function

    void el_event_add(el_loop *loop, event *e)
    

    将事件注册到 event loop 上

    int el_loop_run(el_loop *loop)
    

    启动 loop

    void el_loop_free(el_loop *loop)
    

    回收 loop

    void error(const char *msg)
    

    错误处理

    例子

    一个服务器端接收多个客户端请求的例子。

    server

    #include<libel/el.h>
    
    /**
    create a listener
     **/
    int create_listener() {
      int listenfd;
      struct sockaddr_in *servaddr = (struct sockaddr_in*) malloc(sizeof(struct sockaddr_in));
      if ((listenfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
        error("socket error!");
      servaddr->sin_family = AF_INET;
      servaddr->sin_port = htons(3333);
      if (inet_pton(AF_INET, "0.0.0.0", &servaddr->sin_addr) < 0)
        error("inet_pton error!");
      if (bind(listenfd, (struct sockaddr *)servaddr, sizeof(struct sockaddr_in)) < 0)
        error("bind error!");
      if (listen(listenfd, LISTENQ) < 0)
        error("listen error!");
      return listenfd;
    }
    
    /**
    	处理客户端发送过来的数据,打印数据。
    */
    void onread(int fd, int size, void *arg) {
      el_loop *loop = (el_loop*)arg;
      char buf[MAXLINE];
      int n;
      while ((n = read(fd, buf, MAXLINE)) > 0) {
        buf[n] = '\0';
        printf("%s", buf);
      }
      if (n == 0) {
      //客户端已经完成数据的发送。关闭连接。
        close(fd);
      } else if (n < 0) {
        if (errno == EWOULDBLOCK || errno == EAGAIN) {
        //如果客户端未发送数据完成,需要继续监听
          event *e = el_event_new(fd, READ_EVENT, onread, loop);
          el_event_add(loop, e);
          return;
        } else
          error("read from connected socket error!");
      }
    }
    
    /*
    	处理客户端发起连接的回调函数
    */
    void onaccept(int fd, int size, void *arg) {
      el_loop *loop = (el_loop*)arg;
      int i;
      //size 为缓冲区中的字节数
      for (i = 0; i < size; i++) {
        int connfd;
        if (( connfd = accept(fd, NULL, NULL)) < 0) {
          if (errno == EWOULDBLOCK || errno == ECONNABORTED
    	  || errno == EINTR || errno == EPROTO) {
    	continue;
          } else
    	error("accept error!");
        }
        /**
        	将新建立的连接新建为事件,并加入 loop 。回调函数为 onread 。
        */
        event *e = el_event_new(connfd, READ_EVENT, onread, loop);
        el_event_add(loop, e);
        /**
        	在 libel 的处理机制中,每次事件触发并处理完成后,都会从 loop 中删除,
        	所以需要重新创建,并加入 loop
        */
        event *old = el_event_new(fd, READ_EVENT, onaccept, loop);
        el_event_add(loop, old);
      }
    }
    
    int main() {
      //创建监听
      int listenfd = create_listener();
      //新建 loop
      el_loop *loop = el_loop_new();
      //新建要监听的事件,回调函数为 onaccept
      event *e = el_event_new(listenfd, READ_EVENT, onaccept, loop);
      //将要监听的事件注册到 loop 上
      el_event_add(loop, e);
      //启动 loop
      return el_loop_run(loop);
    }
    

    client

    #include<stdio.h>
    #include<stdlib.h>
    #include<sys/types.h>
    #include<sys/socket.h>
    #include<sys/time.h>
    #include<arpa/inet.h>
    #include<errno.h>
    #include<string.h>
    #define MAX 1024
    
    int main() {
      int sockfd;
      struct sockaddr_in servaddr;
      if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
        fprintf(stderr, "socket error!\n");
        exit(-1);
      }
      servaddr.sin_family = AF_INET;
      servaddr.sin_port = htons(3333);
      if (inet_pton(AF_INET, "0.0.0.0", &servaddr.sin_addr) < 0) {
        fprintf(stderr, "inet_pton error!\n");
        exit(-1);
      }
      if (connect(sockfd, (struct sockaddr*) &servaddr, sizeof(servaddr)) < 0) {
        fprintf(stderr, "connect error!\n");
        exit(-1);
      }
      char buf[MAX];
      /**
      	客户端从终端接收数据,并向服务器端发送。
      */
      while (fgets(buf, MAX, stdin) != NULL) {
        if (write(sockfd, buf, strlen(buf)) < 0) {
          fprintf(stderr, "write error!\n");
          exit(-1);
        }
      }
      close(sockfd);
      return 0;
    }
    
    
    3 条回复    2016-06-12 12:27:57 +08:00
    Balthild
        1
    Balthild  
       2016-06-12 10:33:35 +08:00 via Android
    nice
    wadahana
        2
    wadahana  
       2016-06-12 11:34:39 +08:00
    对比 libev libevent libuv 有神马优势?
    luohaha
        3
    luohaha  
    OP
       2016-06-12 12:27:57 +08:00
    @wadahana 其实底层的性能差不多,用的都是 epoll , kqueue 等。但是功能还没有它们那些多。。
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   1074 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 22ms · UTC 19:13 · PVG 03:13 · LAX 11:13 · JFK 14:13
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.