Libevent官方代码样例学习(二)
连接监听器: 接收TCP连接请求
evconnlistener机制用于监听并接受TCP连接请求. 这些方法在event2/listener.h中声明, 在Libevent 2.0.2-alpha之后的版本中可用.
创建和释放evconnlistener
struct evconnlistener *evconnlistener_new( struct event_base *base, evconnlistener_cb cb, void *ptr, unsigned flags, int backlog, evutil_socket_t fd); struct evconnlistener *evconnlistener_new_bind(struct event_base *base, evconnlistener_cb cb, void *ptr, unsigned flags, int backlog, const struct sockaddr *sa, int socklen); void evconnlistener_free(struct evconnlistener *lev);
这两个 evconnlistener_new*() 方法都会allocate并返回一个新的connection listener对象. connection listener使用event_base 来通知来自于制定的socket上的新的TCP连接请求. 并调用预设的回调方法.
这两个方法相同的参数:
base 是listener用来监听的event_base.
cb 是回调方法, 如果cb is NULL, listener会被视为禁用, 直到回调被设置.
ptr 指针会作为参数被传递给回调方法.
flags 参数控制listener的行为.
backlog 参数控制允许的等待的连接数的上限. 如果backlog是负数, Libevent会自动设置一个合适的值, 如果为0, 则Libevent会认为你已经在socket上调用了listen()方法.
这两个方法中不同的参数, 在于如何初始化listener socket. evconnlistener_new() 方法假定你已经在要监听的端口上绑定了socket, 你只需要传递socket的fd就够. 如果你希望Libevent自己allocate并绑定socket, 就调用evconnlistener_new_bind(), 并传入你想监听的的sockaddr和它的length.
Tip
|
[使用 evconnlistener_new时要确保监听的socket工作于non-blocking 状态, (用evutil_make_socket_nonblocking 或手工指定正确的socket选项). When the listening socket is left in blocking mode, undefined behavior might occur.] |
要释放一个 listener, 使用evconnlistener_free().
创建一个signal events
Libevent can also watch for POSIX-style signals. 要创建一个signal的handler, 使用接口:
#define evsignal_new(base, signum, cb, arg) event_new(base, signum, EV_SIGNAL|EV_PERSIST, cb, arg)
可以看到其实是调用了event_new, 只不过flag是EV_SIGNAL|EV_PERSIST, 并且不是传fd而是传signum
struct event *hup_event; struct event_base *base = event_base_new(); /* call sighup_function on a HUP signal */ hup_event = evsignal_new(base, SIGHUP, sighup_function, NULL);
注意: signal回调在signal发生后, 在event loop中执行, 所以一些在常规POSIX信号handler中不能调用的方法是可以在这种情况下调用的.
注意
|
不要在signal event中设置timeout |
另外一些相关的宏设定:
#define evsignal_add(ev, tv) \ event_add((ev),(tv)) #define evsignal_del(ev) \ event_del(ev) #define evsignal_pending(ev, what, tv_out) \ event_pending((ev), (what), (tv_out))
evsignal_* 宏定义也是在Libevent 2.0.1-alpha之后的版本中可用.
例子
/* This example program provides a trivial server program that listens for TCP connections on port 9995. When they arrive, it writes a short message to each client connection, and closes each connection once it is flushed. Where possible, it exits cleanly in response to a SIGINT (ctrl-c). */ #include <string.h> #include <errno.h> #include <stdio.h> #include <signal.h> #include <event2/bufferevent.h> #include <event2/buffer.h> #include <event2/listener.h> static const char MESSAGE[] = "Hello, World!\n"; static const int PORT = 9995; static void listener_cb(struct evconnlistener *, evutil_socket_t, struct sockaddr *, int socklen, void *); static void conn_writecb(struct bufferevent *, void *); static void conn_eventcb(struct bufferevent *, short, void *); static void signal_cb(evutil_socket_t, short, void *); int main(int argc, char **argv) { struct event_base *base; struct evconnlistener *listener; struct event *signal_event; struct sockaddr_in sin; base = event_base_new(); if (!base) { fprintf(stderr, "Could not initialize libevent!\n"); return 1; } memset(&sin, 0, sizeof(sin)); sin.sin_family = AF_INET; sin.sin_port = htons(PORT); listener = evconnlistener_new_bind( base, listener_cb, (void *) base, LEV_OPT_REUSEABLE | LEV_OPT_CLOSE_ON_FREE, -1, (struct sockaddr *) &sin, sizeof(sin)); if (!listener) { fprintf(stderr, "Could not create a listener!\n"); return 1; } signal_event = evsignal_new(base, SIGINT, signal_cb, (void *) base); if (!signal_event || event_add(signal_event, NULL) < 0) { fprintf(stderr, "Could not create/add a signal event!\n"); return 1; } event_base_dispatch(base); evconnlistener_free(listener); event_free(signal_event); event_base_free(base); printf("done\n"); return 0; } static void listener_cb(struct evconnlistener *listener, evutil_socket_t fd, struct sockaddr *sa, int socklen, void *user_data) { struct event_base *base = user_data; struct bufferevent *bev; bev = bufferevent_socket_new(base, fd, BEV_OPT_CLOSE_ON_FREE); if (!bev) { fprintf(stderr, "Error constructing bufferevent!"); event_base_loopbreak(base); return; } bufferevent_setcb(bev, NULL, conn_writecb, conn_eventcb, NULL); bufferevent_enable(bev, EV_WRITE); bufferevent_disable(bev, EV_READ); bufferevent_write(bev, MESSAGE, strlen(MESSAGE)); } static void conn_writecb(struct bufferevent *bev, void *user_data) { struct evbuffer *output = bufferevent_get_output(bev); if (evbuffer_get_length(output) == 0) { printf("flushed answer\n"); bufferevent_free(bev); } } static void conn_eventcb(struct bufferevent *bev, short events, void *user_data) { if (events & BEV_EVENT_EOF) { printf("Connection closed.\n"); } else if (events & BEV_EVENT_ERROR) { printf("Got an error on the connection: %s\n", strerror(errno));/*XXX win32*/ } /* None of the other events can happen here, since we haven't enabled * timeouts */ bufferevent_free(bev); } static void signal_cb(evutil_socket_t sig, short events, void *user_data) { struct event_base *base = user_data; struct timeval delay = {2, 0}; printf("Caught an interrupt signal; exiting cleanly in two seconds.\n"); event_base_loopexit(base, &delay); }