面试题答案
一键面试1. libevent库事件驱动模型工作原理
- 事件注册: 应用程序将感兴趣的事件(如文件描述符上的可读、可写事件)注册到libevent库的事件循环中。每个事件都与一个回调函数相关联,当事件发生时,该回调函数会被调用。例如,在处理TCP连接时,会将监听套接字的可读事件注册,当有新连接到来时,监听套接字变为可读,触发相应回调。
- 事件多路复用: libevent库内部使用操作系统提供的事件多路复用机制,如select、poll、epoll(在Linux系统上)等。这些机制允许程序同时监视多个文件描述符,当其中任何一个文件描述符上有事件发生时,通知应用程序。通过这种方式,libevent可以高效地管理大量并发连接,而无需为每个连接创建一个单独的线程或进程。
- 事件循环: libevent库维护一个事件循环,不断检查注册的事件是否发生。当有事件发生时,从事件队列中取出该事件,并调用与之关联的回调函数进行处理。处理完事件后,继续循环检查其他事件,直到应用程序退出事件循环。
2. 使用libevent库创建简单TCP服务器步骤
- 引入头文件:
#include <event2/event.h>
#include <event2/listener.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>
- 定义回调函数: 定义处理新连接的回调函数,该函数在有新连接到来时被调用。
void accept_conn_cb(struct evconnlistener *listener, evutil_socket_t fd, struct sockaddr *sa, int socklen, void *user_data) {
struct event_base *base = (struct event_base *)user_data;
printf("New connection accepted\n");
// 这里可以进行与新连接相关的操作,如创建新的事件处理该连接的读写
}
- 创建事件基和监听器:
int main(int argc, char **argv) {
struct event_base *base;
struct evconnlistener *listener;
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(9999);
sin.sin_addr.s_addr = INADDR_ANY;
listener = evconnlistener_new_bind(base, accept_conn_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");
event_base_free(base);
return 1;
}
event_base_dispatch(base);
evconnlistener_free(listener);
event_base_free(base);
return 0;
}
在上述代码中:
- 首先创建一个事件基
event_base
,它是libevent库事件处理的核心。 - 然后设置监听地址和端口,并使用
evconnlistener_new_bind
函数创建一个监听器,将其与事件基和处理新连接的回调函数accept_conn_cb
绑定。 - 最后通过
event_base_dispatch
启动事件循环,开始监听新连接。当有新连接到来时,会调用accept_conn_cb
函数进行处理。程序结束时,释放监听器和事件基。