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”然后关闭连接。