共计 4067 个字符,预计需要花费 11 分钟才能阅读完成。
根本应用办法
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-world
flushed 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
中写就能够了。这样一个简略的服务器就实现了。