现在的位置: 首页 > 综合 > 正文

处理大并发之三 对libevent的初步认识

2013年12月13日 ⁄ 综合 ⁄ 共 4200字 ⁄ 字号 评论关闭

处理大并发之三 对libevent的初步认识

首先翻译下http://www.wangafu.net/~nickm/libevent-book/01_intro.html里的一段文章

对异步IO的一个简要介绍

大多数应用程序都是阻塞的IO调用,如果IO调用是同步(阻塞)的,它不能立即返回直到它的操作完成,或者经过了很长时间直到网络栈丢弃。当你在一个TCP连接上调用”connect()”时,例如,你的操作系统从TCP连接的另一端主机排队等待一个SYN包时。它不能立即返回你的应用程序,直到从连接的另一端主机收到SYN ACK包,或者经历了足够的时候,驱动层丢弃。

libevent简要介绍

Libevent是一个轻量级的开源高性能网络库。

Libevent特点: (来自http://www.cnblogs.com/hustcat/archive/2010/08/31/1814022.html
(1)事件驱动(event-driven),高性能;
(2)轻量级,专注于网络,不如 ACE 那么臃肿庞大; 
(3)源代码相当精炼、易
(4)跨平台,支持 WindowsLinux*BSD和 Mac Os 
(5)支持多种 I/O多路复用技术,epollpolldev/pollselect kqueue 等; 
(6)支持 I/O,定时器和信号等事件; 
(7)注册事件优先级; 
 Libevent 已经被广泛的应用,作为底层的网络库;比如 memcached、 Vomi t、 Nylon、 Netchat等等。

至目前为止资料也看了一部分,源码也看了一部分,我觉得研究源码,查资料,分析源码都很重要,但是最关键的是做demo分析demo

现在想使用libevent做一个服务端的程序,结合之前使用epoll做的客户端程序做测试。

刚开始的时候在网上找了一个demo,结果编译不过,折腾半天还是放弃了。

郁闷了半天,找到一个例子不错,分享出来,我自己分析了下,修改了一些细节然后加了些注释,代码来源于:http://www.felix021.com/blog/read.php?2068 (尊重原创)

对了,这里不在陈述libevent的安装,之前在研究memcache里有相应的介绍,链接:

其实安装基本都是./configure –prefix=路径,然后make && make install就可以了

服务端demo代码:libevent_server.c

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>

#include <event2/event.h>
#include <event2/bufferevent.h>

#define LISTEN_PORT 8000
#define LISTEN_BACKLOG 32

void do_accept_cb(evutil_socket_t listener, short event, void *arg);
void read_cb(struct bufferevent *bev, void *arg);
void error_cb(struct bufferevent *bev, short event, void *arg);
void write_cb(struct bufferevent *bev, void *arg);

int main(int argc, char *argv[])
{
    int ret;
    evutil_socket_t listener;
    listener = socket(AF_INET, SOCK_STREAM, 0);
    if(listener < 0)
    {
        printf("socket error!\n");
        return 1;
    }

    evutil_make_listen_socket_reuseable(listener);//端口重用,查看源码evutil.c中可以知道就是对setsockopt做了层封装,之所以这样做是因为为了和Win32 networking API兼容

    struct sockaddr_in sin;
    sin.sin_family = AF_INET;
    sin.sin_addr.s_addr=htonl(INADDR_ANY);
    sin.sin_addr.s_addr=inet_addr("127.0.0.1");
    sin.sin_port = htons(LISTEN_PORT);

    if (bind(listener, (struct sockaddr *)&sin, sizeof(sin)) < 0) {
        perror("bind");
        return 1;
    }

    if (listen(listener, LISTEN_BACKLOG) < 0) {
        perror("listen");
        return 1;
    }

  printf ("Listening...\n");
  
  evutil_make_socket_nonblocking(listener);//设置为非阻塞模式,查看源码evutil.c中可以知道就是对fcntl做了层封装
  
      struct event_base *base = event_base_new();//创建一个event_base对象也既是创建了一个新的libevent实例
      if (!base) {
          fprintf(stderr, "Could not initialize libevent!\n");
          return 1;
      }
  
      //创建并绑定一个event
      struct event *listen_event;
      listen_event = event_new(base, listener, EV_READ|EV_PERSIST, do_accept_cb, (void*)base);
      event_add(listen_event, NULL);//注册时间,参数NULL表示无超时设置
      event_base_dispatch(base);//)程序进入无限循环,等待就绪事件并执行事件处理
  
      printf("The End.");
      return 0;
  }
  
  void do_accept_cb(evutil_socket_t listener, short event, void *arg)
  {
      struct event_base *base = (struct event_base *)arg;
      evutil_socket_t fd;
      struct sockaddr_in sin;
      socklen_t slen;
      fd = accept(listener, (struct sockaddr *)&sin, &slen);
      if (fd < 0) {
          perror("accept");
          return;
      }
  
      printf("accept: fd = %u\n", fd);
  
      //使用bufferevent_socket_new创建一个struct bufferevent *bev,关联该sockfd,托管给event_base
      struct bufferevent *bev = bufferevent_socket_new(base, fd, BEV_OPT_CLOSE_ON_FREE);//BEV_OPT_CLOSE_ON_FREE表示释放buff
  erevent时关闭底层传输端口。这将关闭底层套接字,释放底层bufferevent等。
  
      bufferevent_setcb(bev, read_cb, NULL, error_cb, arg);
      bufferevent_enable(bev, EV_READ|EV_WRITE|EV_PERSIST);//启用read/write事件
  }
  
  void read_cb(struct bufferevent *bev, void *arg)
  {
  #define MAX_LINE    256
      char line[MAX_LINE+1];
      int n;
      evutil_socket_t fd = bufferevent_getfd(bev);
  
      while (n = bufferevent_read(bev, line, MAX_LINE), n > 0) {
          line[n] = '\0';
          printf("fd=%u, read line: %s\n", fd, line);
  
          bufferevent_write(bev, line, n);
      }
  }
  
  void write_cb(struct bufferevent *bev, void *arg) {}
  
  void error_cb(struct bufferevent *bev, short event, void *arg)
  {
      evutil_socket_t fd = bufferevent_getfd(bev);
      printf("fd = %u, ", fd);
      if (event & BEV_EVENT_TIMEOUT) {
          printf("Timed out\n"); //if bufferevent_set_timeouts() called
      }
      else if (event & BEV_EVENT_EOF) {
          printf("connection closed\n");
      }
      else if (event & BEV_EVENT_ERROR) {
          printf("some other error\n");
      }
      bufferevent_free(bev);
  }

编译:

gcc -I/usr/include -o 123 libevent_server.c -L/usr/local/lib -levent

注意这里应该是包含文件的路径和库文件的路径加上去,具体路径取决于安装目录

测试1:将客户端的并发数设置为2,查看运行结果

服务端:

客户端:

测试2:将客户端的并发数设置为10000,(如果没有修改内核,需要修改句柄的最大数,ulimit n 65535)查看端口占用结果:

lsof i:8000 >log

grep “TCP localhost.localdomain” log |wc 查看端口占用20001,结果正确,测试成功。

如是转载,请指明原出处:http://blog.csdn.net/feitianxuxue,谢谢合作!

抱歉!评论已关闭.