libevent源码分析:hello-world例子

hello-world是libevent自带的一个例子,这个例子的作用是启动后监听一个端口,对于所有通过这个端口连接上服务器的程序发送一段字符:hello-world,然后关闭连接。

  1 /*
  2 * gcc -g -o hello-world hello-world.c -levent_core
  3 */
  4 #include <sys/socket.h>
  5 #include <netinet/in.h>
  6 #include <arpa/inet.h>
  7 #include <string.h>
  8 #include <errno.h>
  9 #include <stdio.h>
 10 #include <signal.h>
 11
 12 #include <event2/bufferevent.h>
 13 #include <event2/buffer.h>
 14 #include <event2/listener.h>
 15 #include <event2/util.h>
 16 #include <event2/event.h>
 17
 18 static const char MESSAGE[] = "Hello, World!\n";
 19
 20 static const int PORT = 9995;
 21
 22 static void listener_cb(struct evconnlistener *, evutil_socket_t, struct sockaddr *, int socklen, void *);
 23 static void conn_writecb(struct bufferevent *, void *);
 24 static void conn_eventcb(struct bufferevent *, short, void *);
 25 static void signal_cb(evutil_socket_t, short, void *);
 26
 27 int main(void)
 28 {
 29     struct event_base *base;
 30     struct evconnlistener *listener;
 31     struct event *signal_event;
 32
 33     struct sockaddr_in sin;
 34
 35     base = event_base_new();
 36     if (!base)
 37     {
 38         fprintf(stderr, "Could not initialize libevent\n");
 39         return 1;
 40     }
 41
 42     memset(&sin, 0, sizeof(sin));
 43     sin.sin_family = AF_INET;
 44     sin.sin_port = htons(PORT);
 45
 46     listener = evconnlistener_new_bind(base, listener_cb, (void *)base,
 47             LEV_OPT_REUSEABLE | LEV_OPT_CLOSE_ON_FREE, -1, (struct sockaddr *)&sin, sizeof(sin));
 48
 49     if (!listener)
 50     {
 51         fprintf(stderr, "Could not create a listener!\n");
 52         return 1;
 53     }
 54     printf("Listening on %d\n", PORT);
 55
 56     signal_event = evsignal_new(base, SIGINT, signal_cb, (void *)base);
 57
 58     if (!signal_event || event_add(signal_event, NULL) < 0)
 59     {
 60         fprintf(stderr, "Could not create/add a signal event!\n");
 61         return 1;
 62     }
 63
 64     event_base_dispatch(base);
 65
 66     evconnlistener_free(listener);
 67     event_free(signal_event);
 68     event_base_free(base);
 69
 70     printf("Done!\n");
 71     return 0;
 72 }
 73
 74 static void listener_cb(struct evconnlistener *listener, evutil_socket_t fd, struct sockaddr *sa, int socklen, void *user_data)
 75 {
 76     struct event_base *base = user_data;
 77     struct bufferevent *bev;
 78     struct sockaddr_in *sa_in = (struct sockaddr_in*)sa;
 79
 80     bev = bufferevent_socket_new(base, fd, BEV_OPT_CLOSE_ON_FREE);
 81     if (!bev)
 82     {
 83         fprintf(stderr, "Error construction bufferevent!");
 84         event_base_loopbreak(base);
 85         return;
 86     }
 87     printf("Recv a new connection, ip[%s], port[%d]\n", inet_ntoa(sa_in->sin_addr), ntohs(sa_in->sin_port));
 88     bufferevent_setcb(bev, NULL, conn_writecb, conn_eventcb, NULL);
 89     bufferevent_enable(bev, EV_WRITE);
 90     bufferevent_disable(bev, EV_READ);
 91
 92     bufferevent_write(bev, MESSAGE, strlen(MESSAGE));
 93 }
 94
 95
 96 static void conn_writecb(struct bufferevent *bev, void *user_data)
 97 {
 98     struct evbuffer *output = bufferevent_get_output(bev);
 99     if (evbuffer_get_length(output) == 0)
100     {
101         printf("flushed answer\n");
102         bufferevent_free(bev);
103     }
104 }
105
106 static void conn_eventcb(struct bufferevent *bev, short events, void *user_data)
107 {
108     if (events & BEV_EVENT_EOF)
109     {
110         printf("Connection closed.\n");
111     }
112     else if (events & BEV_EVENT_ERROR)
113     {
114         printf("Got an error on the connection: %s\n", strerror(errno));
115     }
116
117     bufferevent_free(bev);
118 }
119
120 static void signal_cb(evutil_socket_t sig, short events, void *user_data)
121 {
122     struct event_base *base = user_data;
123     struct timeval delay = { 2, 0 };
124     printf("Caught an interrupt signal; exiting cleanly in two seconds\n");
125     event_base_loopexit(base, &delay);
126 }

下面就通过分析这个例子来看一下libevent对于IO事件是如何处理的:

1、调用event_base_new获得event_base对象。

2、调用evconnlistener_new_bind,返回一个struct evconnlistener对象指针,evconnlistener_new_bind函数内部实现如下:

1)调用evutil_socket_函数获取一个socket。

2)调用evconnlistener_new函数获取一个struct evconnlistener对象指针,并返回。

在evconnlistener_new函数内部,首先调用malloc函数分配一个struct evconnlistener_event对象,然后利用传入的形参fd调用listen函数,然后初始化base的各个参数,调用event_assign初始化成员listener。

其实这些函数归根结底还是会调用最基本的libevent函数,只是这些函数对基本的函数做了一些封装提供更高级、更方便的使用方式。

struct evconnlistener和struct evconnlistener_event的定义如下:

 1 struct evconnlistener {
 2     const struct evconnlistener_ops *ops;
 3     void *lock;
 4     evconnlistener_cb cb;
 5     evconnlistener_errorcb errorcb;
 6     void *user_data;
 7     unsigned flags;
 8     short refcnt;
 9     int accept4_flags;
10     unsigned enabled : 1;
11 };
12
13 struct evconnlistener_event {
14     struct evconnlistener base;
15     struct event listener;
16 };

关于这里,我有些不明白的是listener是如何被调用event_add函数的?

3、调用evsignal_new函数获取一个信号事件对象,其实这个函数也是对event_new函数的封装。

1 #define evsignal_new(b, x, cb, arg)                2     event_new((b), (x), EV_SIGNAL|EV_PERSIST, (cb), (arg))

4、调用event_base_dispatch函数进入事件循环。

5、调用evconnlistener_free释放struct evconnlistener对象。

然后看一下回调函数,在创建evconnlistener对象时传入了一个回调函数:listener_cb,该函数会在监听对象有新连接到来时被调用,在它的内部,首先创建一个struct bufferevent对象,然后设置该对象的回调函数,然后就是调用bufferevent_write函数将要发送的数据写入到该对象对应的buffer中,就不用管了。

由此可见使用libevent库来开发网络服务器是多么的方便,高效。

至此一个基于libevent的简单服务器就完成了,只能对连接上的客户端发送“hello world”然后关闭连接。

时间: 2024-10-05 06:21:06

libevent源码分析:hello-world例子的相关文章

libevent源码分析:http-server例子

http-server例子是libevent提供的一个简单web服务器,实现了对静态网页的处理功能. 1 /* 2 * gcc -g -o http-server http-server.c -levent 3 */ 4 #include <stdio.h> 5 #include <stdlib.h> 6 #include <string.h> 7 8 #include <sys/types.h> 9 #include <sys/stat.h>

Libevent源码分析-timer和signal处理

timer处理 Signal处理 timerfd和signalfd timerfd signalfd timer处理 在Libevent源码分析-event处理流程中,使用了定时器,来看一下源码: evtimer_set(&ev, time_cb, NULL);//设置定时器事件 其中evtimer_set是个宏定义 #define evtimer_set(ev, cb, arg) event_set((ev), -1, 0, (cb), (arg)) //event_set原型 void ev

【转】libevent源码分析

libevent源码分析 转自:http://www.cnblogs.com/hustcat/archive/2010/08/31/1814022.html 这两天没事,看了一下Memcached和libevent的源码,做个小总结. 1.入门 1.1.概述Libevent是一个用于开发可扩展性网络服务器的基于事件驱动(event-driven)模型的网络库.Libevent有几个显著的亮点: (1)事件驱动(event-driven),高性能:(2)轻量级,专注于网络,不如 ACE 那么臃肿庞

Libevent源码分析 (1) hello-world

Libevent源码分析 (1) hello-world ⑨月份接触了久闻大名的libevent,当时想读读源码,可是由于事情比较多一直没有时间,现在手头的东西基本告一段落了,我准备读读libevent的源码,凡是我觉得有必要的内容均一一记录,与君共勉. 首先要说说什么是libevent: libevent是一个事件通知库,libevent API提供一种机制使得我们可以在一个文件描述符(file descriptor)发生特定事件时或者timeout发生时执行指定的回调函数.libevent意

libevent源码分析-event

event结构 event相关接口 Libevent对event的管理 event结构 event是Reactor模式中的最重要的组件.它包含了了一个句柄fd,并设置监听这个句柄上的哪些事件(读/写等),设置了对应的函数指针,在事件到来时,回调函数指针来处理事件. 先看一下event的结构.它位于include/event2/event_struct.h中 struct event { TAILQ_ENTRY(event) ev_active_next; TAILQ_ENTRY(event) e

[libevent源码分析] event_init

libevent采用的是经典的reactor网络框架,集成了信号.定时.网络事件于一体 首先对event_init进行源码剖析 event_init 主要创建event_base对象, struct event_base { const struct eventop *evsel; //lievent支持select epoll kequeue..等网络api,包括init.add.del.dispatch的接口,每种网络框架都支持 void *evbase; //支持相应网络api的 结构对象

[libevent源码分析] event_set

libevent使用event来封装网络事件回调,参数.fd...等一些信息,函数很简单 void event_set(struct event *ev, int fd, short events, void (*callback)(int, short, void *), void *arg) { /* Take the current base - caller needs to set the real base later */ ev->ev_base = current_base; /

[libevent源码分析] event_add

event_add 把event往当前event中的ev_base追加,如果需要定时,那么tv不能为空 int event_add(struct event *ev, const struct timeval *tv) { struct event_base *base = ev->ev_base; //event_add 会把event加入到他的ev_base成员里 const struct eventop *evsel = base->evsel; //对应linux的epoll相关函数

[libevent源码分析] event_base_dispatch

分析下事件循环 event_base_dispatch int event_base_dispatch(struct event_base *event_base) { return (event_base_loop(event_base, 0)); } int event_base_loop(struct event_base *base, int flags) { const struct eventop *evsel = base->evsel; void *evbase = base->