面试题答案
一键面试优化策略
- 连接池管理:
- 预先分配一定数量的连接对象,当有新连接请求时,从连接池中获取可用连接,使用完毕后再放回连接池。这样避免了频繁创建和销毁连接对象带来的内存开销。
- 在libev环境下,可以结合链表或数组等数据结构来实现连接池。例如,使用双向链表,链表节点包含连接相关的信息(如socket描述符、连接状态等)。
- 内存池管理:
- 对于连接过程中频繁分配和释放的小块内存(如缓冲区),采用内存池技术。内存池在初始化时分配一大块内存,然后按照固定大小的块进行切分,当需要小块内存时,直接从内存池中获取,使用完毕后归还到内存池。
- 在libev应用中,可以根据不同类型的内存需求(如接收缓冲区、发送缓冲区)创建不同的内存池。
- 及时释放资源:
- 当连接关闭时,要确保所有与该连接相关的资源(如socket描述符、分配的内存等)都被正确释放。在libev的事件回调函数中,当检测到连接关闭事件(如
EV_READ
和EV_WRITE
事件都不再活跃且连接状态为关闭),执行资源释放操作。 - 例如,关闭socket描述符,并将相关的内存块归还给对应的内存池或连接池。
- 当连接关闭时,要确保所有与该连接相关的资源(如socket描述符、分配的内存等)都被正确释放。在libev的事件回调函数中,当检测到连接关闭事件(如
- 优化数据结构:
- 选择合适的数据结构来管理连接。比如使用哈希表来快速查找连接,以提高查找效率。在libev应用中,当处理大量并发连接时,哈希表可以根据连接的唯一标识(如IP地址和端口号组合)来快速定位连接对象,避免线性查找带来的性能开销。
- 同时,数据结构的设计要考虑内存对齐等因素,以提高内存访问效率。
代码示例
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <ev.h>
#define MAX_CONNECTIONS 1024
#define BUFFER_SIZE 1024
// 连接结构体
typedef struct Connection {
struct ev_io io_watcher;
int fd;
char buffer[BUFFER_SIZE];
int buffer_len;
int is_free;
} Connection;
// 连接池
Connection connection_pool[MAX_CONNECTIONS];
// 内存池
char *memory_pool;
int memory_pool_size = MAX_CONNECTIONS * BUFFER_SIZE;
// 初始化连接池
void init_connection_pool() {
for (int i = 0; i < MAX_CONNECTIONS; i++) {
connection_pool[i].fd = -1;
connection_pool[i].buffer_len = 0;
connection_pool[i].is_free = 1;
ev_io_init(&connection_pool[i].io_watcher, NULL, -1, 0);
}
}
// 从连接池获取连接
Connection* get_connection() {
for (int i = 0; i < MAX_CONNECTIONS; i++) {
if (connection_pool[i].is_free) {
connection_pool[i].is_free = 0;
return &connection_pool[i];
}
}
return NULL;
}
// 归还连接到连接池
void return_connection(Connection *conn) {
conn->fd = -1;
conn->buffer_len = 0;
conn->is_free = 1;
ev_io_stop(EV_DEFAULT_UC, &conn->io_watcher);
}
// 初始化内存池
void init_memory_pool() {
memory_pool = (char*)malloc(memory_pool_size);
if (memory_pool == NULL) {
perror("malloc");
exit(1);
}
}
// 从内存池获取内存
char* get_memory(int size) {
if (size > BUFFER_SIZE) {
return NULL;
}
static int current_index = 0;
if (current_index + size > memory_pool_size) {
return NULL;
}
char *ptr = memory_pool + current_index;
current_index += size;
return ptr;
}
// 处理连接读事件
void read_cb(struct ev_loop *loop, struct ev_io *w, int revents) {
Connection *conn = (Connection*)w;
int len = recv(conn->fd, conn->buffer, BUFFER_SIZE, 0);
if (len <= 0) {
if (len == 0) {
// 连接关闭
printf("Connection closed\n");
} else {
perror("recv");
}
return_connection(conn);
return;
}
conn->buffer_len = len;
// 处理接收到的数据
// 这里可以添加业务逻辑
}
// 处理新连接
void accept_cb(struct ev_loop *loop, struct ev_io *w, int revents) {
int listen_fd = w->fd;
int client_fd = accept(listen_fd, NULL, NULL);
if (client_fd == -1) {
perror("accept");
return;
}
Connection *conn = get_connection();
if (conn == NULL) {
close(client_fd);
return;
}
conn->fd = client_fd;
ev_io_set(&conn->io_watcher, client_fd, EV_READ);
ev_io_start(EV_DEFAULT_UC, &conn->io_watcher);
}
int main() {
struct ev_loop *loop = EV_DEFAULT;
int listen_fd = socket(AF_INET, SOCK_STREAM, 0);
if (listen_fd == -1) {
perror("socket");
return 1;
}
struct sockaddr_in servaddr;
memset(&servaddr, 0, sizeof(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)) == -1) {
perror("bind");
close(listen_fd);
return 1;
}
if (listen(listen_fd, 128) == -1) {
perror("listen");
close(listen_fd);
return 1;
}
init_connection_pool();
init_memory_pool();
struct ev_io accept_watcher;
ev_io_init(&accept_watcher, accept_cb, listen_fd, EV_READ);
ev_io_start(loop, &accept_watcher);
ev_loop(loop, 0);
free(memory_pool);
close(listen_fd);
return 0;
}
上述代码展示了一个简单的基于libev的高并发网络应用示例,包含连接池和内存池的初始化、获取与归还操作,以及连接的读事件和新连接的处理。在实际应用中,还需要根据具体业务需求进一步完善和优化。