根本应用办法
libevent
是一个应用事件驱动模型的网络网络库,网络开发,能够通过应用这个库,非常简单、清晰的代码做出一个反对I/O复用的程序。工作中须要应用到此库,所以记录一下学习进度。
根本应用能够参考源码的sample/
目录下的应用示例,依据示例名称,我首先看一下 hello-workd.c
这个程序:代码不少,然而单个函数拆分来看,还是分清晰的。
首先是main
函数中:
//...... base = event_base_new(); //...... listener = evconnlistener_new_bind(base, listener_cb, (void *)base, LEV_OPT_REUSEABLE|LEV_OPT_CLOSE_ON_FREE, -1, (struct sockaddr*)&sin, sizeof(sin)); //...... /// 注册了一个信号事件,该事件处理函数 signal_cb 解决的Ctrl+C信号 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);
首先调用 event_base_new
创立了一个 event_base
构造体,也不晓得是干嘛的,然而并没有给他提供任何网络相干的参数,临时先不论它。
之后调用了 evconnlistener_new_bind
函数,给他传入了网络的sockaddr
构造信息,联合文件名看来就是在这里开始创立套接字和监听了。进入头文件listener.h
中也能看到对于该函数的介绍:
/** Allocate a new evconnlistener object to listen for incoming TCP connections on a given address. @param base The event base to associate the listener with. event 会和 event_base 关联 @param cb A callback to be invoked when a new connection arrives. If the callback is NULL, the listener will be treated as disabled until the callback is set. 链接来的socket的处理函数 @param ptr A user-supplied pointer to give to the callback. 参数指针 @param flags Any number of LEV_OPT_* flags @param backlog Passed to the listen() call to determine the length of the acceptable connection backlog. Set to -1 for a reasonable default. @param sa The address to listen for connections on. @param socklen The length of the address. */
能够看出 listener_cb
是解决链接客户端的socket
,LEV_OPT_REUSEABLE|LEV_OPT_CLOSE_ON_FREE
能够依据listen.h
中找到解释,设置了socket地址的可重用和敞开时开释, sa 是地址信息。
而后在 listener_cb
中,应该是解决链接的socket
的数据的函数了,
bev = bufferevent_socket_new(base, fd, BEV_OPT_CLOSE_ON_FREE); if (!bev) { fprintf(stderr, "Error constructing bufferevent!"); event_base_loopbreak(base); return; } ///设置bufferevent的读写事件处理函数 bufferevent_setcb(bev, NULL, conn_writecb, conn_eventcb, NULL); /// 使能写事件 bufferevent_enable(bev, EV_WRITE); /// 失能读事件 bufferevent_disable(bev, EV_READ); ///向bufferevent写入数据 bufferevent_write(bev, MESSAGE, strlen(MESSAGE));
如下面代码,依据头文件中的形容这个bufferevent
构造的bev
变量是和套接字fd
关联的,而后应用bufferevent_setcb
有绑定了两个回调函数,写函数和异样处理函数。并enable
了写,diable
读,并向bev中写入了"Hello, World!\n"
。
进入两个回调函数中看一下,第一个
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); }}
这里有些看不明确的是,在之前的 listener_cb
当中曾经调用了一次bufferevent_write
写了数据,这里的写回调是有什么用途呢,
函数外面通过获取event_buffer
的output
的buffer
并判断其长度,如果为0,就开释资源,依据后面建设连贯时的flag参数能够晓得这里开释了资源就相当于敞开链接了。那就是给客户端写了一个hello world就退出链接了。。。(起初想到,这里的conn_writecb
是为了保障outputt
的数据曾经写完了,再敞开链接的用处)。
static void conn_eventcb(struct bufferevent *bev, short events, void *user_data) { if (events & BEV_EVENT_EOF) { /// 写到完结即socket收到FIN后返回0 printf("Connection closed.\n"); } else if (events & BEV_EVENT_ERROR) { /// socket出错 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); }
conn_eventcb应该就是异样解决和完结解决的函数,没什么看的了。
看了代码之后,依据之气那的剖析看一下理论执行是不是这样子了:
咱们先执行一下编译的可执行程序,执行后在新窗口执行telnet 127.0.01 9995
后能够看到输入如下:
Trying 127.0.0.1...Connected to 127.0.0.1.Escape character is '^]'.Hello, World!Connection closed by foreign host.
执行 hello-word
的窗口显示如下:
root@DESKTOP-RMCD5EP:/mnt/d/code/libevent/build/bin# ./hello-worldflushed answer
能够看到hello-world
执行的流程是:hello-world
程序监听9995
端口,telnet
链接上之后,hello-world
程序间接显示了 “flushed answer”
并给客户端的链接套接字写入了 “Hello, World!"
(不晓得先后顺序),之后把客户端链接套接字敞开了。导致telnet
程序退出了。
除了 socket
解决,前面还有一个信号事件处理,基本上也是一样的流程,然而信号处理中先是应用 evsignal_new
函数新建一个事件,并同时将事件处理的信号类型以及函数指针传入,失去一个 event
构造,之后须要调用 evnet_add
,这个函数将没有找到具体的阐明,不过看起来像是把新建的 event
退出到惟一的 event_base
中。须要深入分析。之后等到信号传入时, hello-world
程序就会主动调用自定义的信号处理函数了。
总结一下
应用libevent
,首先初始化一个event_base
构造体,而后创立event
构造,创立tcp
服务器应用evconnlistener_new_bind
函数,他返回一个evnet
构造,之后将解决客户端链接的解决函数指针传入evconnlistener_new_bind
,这个函数帮咱们实现了从创立socket
到connect
的解决,咱们只须要将解决客户但链接的函数指针传入就能够了。之后调用event_base_dispatch
函数 libevnet
就主动开始监听了。
再解决客户端链接的时候,libevent
提供了bufferevent
,咱们能够将一个客户端链接socket
绑定bufferevent
,这个bufferevent
替咱们做了读写socket
获取传输内容的操作。当接管时调用bufferevent_read
函数,当发送时间接调用 bufferevent_write
函数往befferevnet
中写就能够了。这样一个简略的服务器就实现了。