面试题答案
一键面试关键代码片段及说明
- 初始化libevent库
#include <event2/event.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>
// 全局变量,保存event_base
struct event_base *base;
// 初始化libevent库
void init_libevent() {
base = event_base_new();
if (!base) {
fprintf(stderr, "Could not initialize libevent!\n");
exit(1);
}
}
此部分代码初始化了libevent库,创建了一个event_base
对象,后续的事件都将在这个event_base
上进行管理。
- 创建监听套接字并设置事件
// 监听回调函数
void listen_cb(evutil_socket_t listen_fd, short events, void *arg) {
struct sockaddr_in client_addr;
socklen_t client_len = sizeof(client_addr);
int client_fd = accept(listen_fd, (struct sockaddr *)&client_addr, &client_len);
if (client_fd < 0) {
perror("accept");
return;
}
// 设置为非阻塞
evutil_make_socket_nonblocking(client_fd);
// 创建读事件
struct event *read_event = event_new(base, client_fd, EV_READ | EV_PERSIST, read_cb, (void *)(intptr_t)client_fd);
if (!read_event) {
fprintf(stderr, "Failed to create read event!\n");
close(client_fd);
return;
}
// 添加读事件到event_base
event_add(read_event, NULL);
}
// 设置监听
void setup_listen() {
int listen_fd = socket(AF_INET, SOCK_STREAM, 0);
if (listen_fd < 0) {
perror("socket");
exit(1);
}
// 设置为非阻塞
evutil_make_socket_nonblocking(listen_fd);
struct sockaddr_in server_addr;
memset(&server_addr, 0, sizeof(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);
exit(1);
}
if (listen(listen_fd, 10) < 0) {
perror("listen");
close(listen_fd);
exit(1);
}
// 创建监听事件
struct event *listen_event = event_new(base, listen_fd, EV_READ | EV_PERSIST, listen_cb, NULL);
if (!listen_event) {
fprintf(stderr, "Failed to create listen event!\n");
close(listen_fd);
exit(1);
}
// 添加监听事件到event_base
event_add(listen_event, NULL);
}
此部分代码创建了一个监听套接字,绑定到指定端口并开始监听。当有新连接到来时,listen_cb
函数被调用,在该函数中接受新连接,设置为非阻塞,并为新连接创建读事件。
- 处理读事件(接收消息)
// 读回调函数
void read_cb(evutil_socket_t client_fd, short events, void *arg) {
char buffer[1024];
int n = recv(client_fd, buffer, sizeof(buffer) - 1, 0);
if (n <= 0) {
if (n == 0) {
printf("Client disconnected\n");
} else {
perror("recv");
}
// 移除事件并关闭套接字
struct event *ev = event_self_cbarg();
event_free(ev);
close(client_fd);
return;
}
buffer[n] = '\0';
printf("Received: %s\n", buffer);
// 简单回应消息
const char *response = "Message received!";
send(client_fd, response, strlen(response), 0);
}
当有数据可读时,read_cb
函数被调用,从套接字接收数据,打印接收到的消息,并简单地回显一条确认消息。如果接收数据出错或连接关闭,相应地处理事件并关闭套接字。
- 进入事件循环
int main() {
init_libevent();
setup_listen();
// 进入事件循环
event_base_dispatch(base);
// 清理
event_base_free(base);
return 0;
}
最后,在main
函数中初始化libevent,设置监听,然后进入事件循环event_base_dispatch
,该函数会阻塞并处理注册的事件,直到所有事件处理完毕或显式退出。程序结束时,释放event_base
资源。
通过以上步骤,使用libevent库实现了处理多个并发TCP连接以及简单的消息收发功能。在实际应用中,可能需要更复杂的消息处理逻辑和错误处理机制。