面试题答案
一键面试libev库核心数据结构
- ev_loop:事件循环结构体,管理所有注册的事件和调度I/O、定时器等事件。一个应用程序通常只有一个主事件循环实例。
- ev_io:用于处理I/O事件,如TCP套接字、文件描述符等。它包含了文件描述符(fd)、事件类型(如读、写事件)以及事件触发时的回调函数。
- ev_timer:用于处理定时事件。包含定时时间(延迟时间和重复时间)以及定时器到期时的回调函数。
使用libev创建简单事件循环并监听TCP套接字连接事件示例代码(以C语言为例)
#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <ev.h>
// 监听套接字的文件描述符
static int listen_fd;
// 当有新连接时的回调函数
static void accept_cb(struct ev_loop *loop, ev_io *w, int revents) {
struct sockaddr_in client_addr;
socklen_t client_addr_len = sizeof(client_addr);
int client_fd = accept(listen_fd, (struct sockaddr *)&client_addr, &client_addr_len);
if (client_fd < 0) {
perror("accept");
return;
}
printf("Accepted new connection: %d\n", client_fd);
close(client_fd);
}
int main() {
// 创建事件循环
struct ev_loop *loop = ev_default_loop(0);
// 创建监听套接字
listen_fd = socket(AF_INET, SOCK_STREAM, 0);
if (listen_fd < 0) {
perror("socket");
return 1;
}
struct sockaddr_in server_addr;
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(12345);
server_addr.sin_addr.s_addr = INADDR_ANY;
if (bind(listen_fd, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0) {
perror("bind");
close(listen_fd);
return 1;
}
if (listen(listen_fd, 5) < 0) {
perror("listen");
close(listen_fd);
return 1;
}
// 创建并初始化一个I/O watcher用于监听新连接
struct ev_io accept_watcher;
ev_io_init(&accept_watcher, accept_cb, listen_fd, EV_READ);
ev_io_start(loop, &accept_watcher);
// 进入事件循环
ev_run(loop, 0);
// 清理资源
ev_io_stop(loop, &accept_watcher);
close(listen_fd);
ev_loop_destroy(loop);
return 0;
}
上述代码实现了以下步骤:
- 创建一个事件循环
ev_loop
。 - 创建一个TCP监听套接字,并绑定到指定端口。
- 初始化一个
ev_io
结构体accept_watcher
用于监听监听套接字的读事件(即新连接事件),并指定回调函数accept_cb
。 - 将
accept_watcher
加入到事件循环中开始监听。 - 启动事件循环
ev_run
。 - 当有新连接时,
accept_cb
回调函数被调用,打印新连接的文件描述符并关闭该连接。 - 最后清理资源,停止
ev_io
监听,关闭监听套接字并销毁事件循环。