面试题答案
一键面试1. C++ 中利用libevent实现异步I/O操作的基本原理
- 事件驱动模型:libevent基于事件驱动模型,应用程序向libevent库注册感兴趣的事件(如套接字可读、可写事件),并指定事件发生时要执行的回调函数。libevent库在后台持续监控这些事件,当某个事件发生时,库会调用相应的回调函数进行处理,这样应用程序无需阻塞等待I/O操作完成,实现了异步操作。
- 事件循环:libevent库维护一个事件循环,它不断地检查已注册事件是否发生。在事件循环运行期间,应用程序可以继续执行其他任务,直到有事件触发,然后处理该事件,处理完后又回到事件循环继续等待下一个事件。
- 底层I/O多路复用:libevent内部使用了操作系统提供的I/O多路复用机制(如select、poll、epoll等,不同操作系统会选择最优的实现)。这些机制允许应用程序在一个线程中同时监控多个文件描述符(如套接字)的状态变化,大大提高了I/O操作的效率。
2. 创建简单事件监控机制,监听套接字可读事件的步骤
- 引入头文件:
#include <event2/event.h>
#include <iostream>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>
- 定义回调函数:
void read_callback(evutil_socket_t fd, short event, void* arg) {
char buffer[1024];
int len = recv(fd, buffer, sizeof(buffer), 0);
if (len > 0) {
buffer[len] = '\0';
std::cout << "Received: " << buffer << std::endl;
} else if (len == 0) {
std::cout << "Connection closed." << std::endl;
} else {
std::cerr << "Receive error." << std::endl;
}
}
- 初始化libevent库并创建事件:
int main() {
// 创建一个libevent的上下文
struct event_base* base = event_base_new();
if (!base) {
std::cerr << "Could not initialize libevent." << std::endl;
return 1;
}
// 创建一个套接字
int sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0) {
std::cerr << "Socket creation failed." << std::endl;
event_base_free(base);
return 1;
}
struct sockaddr_in servaddr;
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(8080);
servaddr.sin_addr.s_addr = inet_addr("127.0.0.1");
if (connect(sockfd, (struct sockaddr*)&servaddr, sizeof(servaddr)) != 0) {
std::cerr << "Connection failed." << std::endl;
close(sockfd);
event_base_free(base);
return 1;
}
// 创建一个事件来监听套接字的可读事件
struct event* ev = event_new(base, sockfd, EV_READ | EV_PERSIST, read_callback, NULL);
if (!ev) {
std::cerr << "Could not create event." << std::endl;
close(sockfd);
event_base_free(base);
return 1;
}
// 添加事件到事件循环
if (event_add(ev, NULL) != 0) {
std::cerr << "Could not add event." << std::endl;
event_free(ev);
close(sockfd);
event_base_free(base);
return 1;
}
// 启动事件循环
event_base_dispatch(base);
// 清理资源
event_free(ev);
close(sockfd);
event_base_free(base);
return 0;
}
上述代码中,首先初始化libevent库,创建一个套接字并连接到指定服务器。然后创建一个事件用于监听套接字的可读事件,将该事件添加到事件循环中,最后启动事件循环。当有数据可读时,会调用read_callback
函数进行处理。处理完后继续等待下一个可读事件。最后在程序结束时,清理相关资源。