面试题答案
一键面试-
初始化libev事件循环:
- 在libev中,事件循环由
struct ev_loop
结构体管理。可以使用ev_loop_new()
函数来创建一个新的事件循环。
#include <ev.h> struct ev_loop *loop = ev_loop_new(0); if (!loop) { // 处理创建失败的情况,例如内存不足等 perror("ev_loop_new"); return -1; }
- 解释:
#include <ev.h>
:引入libev库的头文件,包含了使用libev所需的各种结构体、函数声明等。struct ev_loop *loop = ev_loop_new(0);
:调用ev_loop_new
函数创建一个新的事件循环。参数0
表示使用默认的事件循环配置。如果函数执行成功,会返回一个指向struct ev_loop
的指针loop
,后续对事件的操作都将基于这个事件循环。if (!loop)
:检查事件循环是否创建成功。如果loop
为NULL
,表示创建失败,通过perror("ev_loop_new")
输出错误信息,这里错误信息会提示ev_loop_new
函数调用失败的原因,例如“Out of memory”等,最后return -1
表示程序以错误状态退出。
- 在libev中,事件循环由
-
添加一个TCP连接监听事件:
- 需要使用
ev_io
结构体来表示I/O事件(这里是TCP监听事件),并使用ev_io_init
和ev_io_start
函数来初始化和启动这个事件。
#include <sys/socket.h> #include <arpa/inet.h> #include <unistd.h> void accept_cb(struct ev_loop *loop, struct ev_io *w, int revents); int main() { int listen_fd = socket(AF_INET, SOCK_STREAM, 0); if (listen_fd < 0) { perror("socket"); return -1; } struct sockaddr_in servaddr; servaddr.sin_family = AF_INET; servaddr.sin_port = htons(8888); servaddr.sin_addr.s_addr = INADDR_ANY; if (bind(listen_fd, (struct sockaddr *)&servaddr, sizeof(servaddr)) < 0) { perror("bind"); close(listen_fd); return -1; } if (listen(listen_fd, 128) < 0) { perror("listen"); close(listen_fd); return -1; } struct ev_loop *loop = ev_loop_new(0); if (!loop) { perror("ev_loop_new"); return -1; } struct ev_io listen_watcher; ev_io_init(&listen_watcher, accept_cb, listen_fd, EV_READ); ev_io_start(loop, &listen_watcher); ev_loop(loop, 0); ev_io_stop(loop, &listen_watcher); ev_loop_destroy(loop); close(listen_fd); return 0; } void accept_cb(struct ev_loop *loop, struct ev_io *w, int revents) { if (revents & EV_READ) { int client_fd = accept(w->fd, NULL, NULL); if (client_fd < 0) { perror("accept"); return; } // 处理新连接,例如可以在这里创建新的I/O watcher来处理客户端数据读写 close(client_fd); } }
- 解释:
#include <sys/socket.h>
,#include <arpa/inet.h>
,#include <unistd.h>
:这些头文件是进行TCP socket编程所需要的,分别提供了socket相关函数、地址转换函数以及文件描述符操作函数等。int listen_fd = socket(AF_INET, SOCK_STREAM, 0);
:创建一个TCP socket,AF_INET
表示使用IPv4地址族,SOCK_STREAM
表示使用TCP协议,0
表示使用默认协议。如果创建失败,listen_fd
会小于0,通过perror("socket")
输出错误信息并return -1
退出程序。- 对
servaddr
结构体的初始化:设置地址族为IPv4(sin_family = AF_INET
),端口号为8888(sin_port = htons(8888)
,htons
函数将主机字节序转换为网络字节序),绑定地址为任意地址(sin_addr.s_addr = INADDR_ANY
)。 bind(listen_fd, (struct sockaddr *)&servaddr, sizeof(servaddr))
:将socket绑定到指定的地址和端口。如果绑定失败,通过perror("bind")
输出错误信息,关闭socket(close(listen_fd)
)并return -1
退出程序。listen(listen_fd, 128)
:将socket设置为监听状态,128
是等待连接队列的最大长度。如果监听失败,同样输出错误信息,关闭socket并退出程序。struct ev_loop *loop = ev_loop_new(0);
:如上述初始化事件循环部分所解释,创建一个新的事件循环。struct ev_io listen_watcher;
:定义一个ev_io
结构体变量listen_watcher
,用于表示TCP监听的I/O事件。ev_io_init(&listen_watcher, accept_cb, listen_fd, EV_READ);
:初始化listen_watcher
。第一个参数是指向ev_io
结构体的指针;第二个参数accept_cb
是事件发生时的回调函数;第三个参数listen_fd
是要监听的文件描述符(这里是TCP监听socket的文件描述符);EV_READ
表示监听读事件,即有新的连接请求时会触发该事件。ev_io_start(loop, &listen_watcher);
:启动listen_watcher
,将这个I/O事件添加到事件循环loop
中开始监听。ev_loop(loop, 0);
:启动事件循环,开始处理事件。0
表示使用默认的事件循环模式。ev_io_stop(loop, &listen_watcher);
:停止listen_watcher
,从事件循环中移除该I/O事件。ev_loop_destroy(loop);
:销毁事件循环,释放相关资源。close(listen_fd);
:关闭TCP监听socket。accept_cb
函数:当listen_watcher
监听的读事件(新连接请求)发生时会调用这个回调函数。if (revents & EV_READ)
检查是否是读事件发生。如果是,调用accept
函数接受新连接,获取客户端socket的文件描述符client_fd
。如果accept
失败,输出错误信息并返回。在实际应用中,可以在获取到client_fd
后创建新的I/O watcher来处理客户端的数据读写等操作,这里简单关闭了client_fd
。
- 需要使用