简介
在Linux网络编程中,epoll是一种高效的I/O事件通知机制,可以监听多个I/O事件。在实现简单的C/S通信时,利用epoll可以实现服务器和客户端之间的异步通信。
epoll的基本原理
epoll是Linux特有的I/O事件通知机制,是通过在内核中维护一张红黑树和一个双向链表来实现的。它可以同时监听多个文件描述符上的事件,当有文件描述符上的事件满足条件时,就会通过回调函数通知应用程序。
epoll的基本原理如下:
- 创建一个epoll对象。
- 将需要监听的文件描述符添加到epoll对象中。
- 等待事件的发生。
- 若有事件发生,通过回调函数处理该事件。
- 回到第3步,继续等待事件的发生。
服务器端代码示例
下面是一个使用epoll实现简单的C/S通信的服务器端代码示例:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <sys/epoll.h>
#define MAX_EVENTS 10
#define BUF_SIZE 1024
int main() {
int server_sock, client_sock;
struct sockaddr_in server_addr, client_addr;
socklen_t client_addr_size;
int str_len, i;
char buf[BUF_SIZE];
// 创建socket
server_sock = socket(AF_INET, SOCK_STREAM, 0);
memset(&server_addr, 0, sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
server_addr.sin_port = htons(1234);
// 绑定socket
bind(server_sock, (struct sockaddr*)&server_addr, sizeof(server_addr));
// 监听socket
listen(server_sock, 5);
// 创建epoll对象
int epoll_fd = epoll_create(MAX_EVENTS);
struct epoll_event event;
struct epoll_event* events = malloc(sizeof(struct epoll_event) * MAX_EVENTS);
// 将服务器socket添加到epoll对象中
event.events = EPOLLIN;
event.data.fd = server_sock;
epoll_ctl(epoll_fd, EPOLL_CTL_ADD, server_sock, &event);
while (1) {
// 等待事件的发生
int event_cnt = epoll_wait(epoll_fd, events, MAX_EVENTS, -1);
if (event_cnt == -1) {
perror("epoll_wait() error");
break;
}
// 处理事件
for (i = 0; i < event_cnt; ++i) {
if (events[i].data.fd == server_sock) {
// 有新的客户端连接请求
client_addr_size = sizeof(client_addr);
client_sock = accept(server_sock, (struct sockaddr*)&client_addr, &client_addr_size);
event.events = EPOLLIN;
event.data.fd = client_sock;
epoll_ctl(epoll_fd, EPOLL_CTL_ADD, client_sock, &event);
printf("Connected client: %d\n", client_sock);
} else {
// 有客户端消息到达
str_len = read(events[i].data.fd, buf, BUF_SIZE);
if (str_len == 0) {
epoll_ctl(epoll_fd, EPOLL_CTL_DEL, events[i].data.fd, NULL);
close(events[i].data.fd);
} else {
write(events[i].data.fd, buf, str_len);
}
}
}
}
// 关闭socket和epoll对象
close(server_sock);
close(epoll_fd);
return 0;
}
客户端代码示例
下面是一个使用epoll实现简单的C/S通信的客户端代码示例:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <sys/epoll.h>
#define MAX_EVENTS 10
#define BUF_SIZE 1024
int main() {
int client_sock;
struct sockaddr_in server_addr;
socklen_t server_addr_size;
int str_len, event_cnt, i;
char buf[BUF_SIZE];
// 创建socket
client_sock = socket(AF_INET, SOCK_STREAM, 0);
memset(&server_addr, 0, sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = inet_addr("127.0.0.1");
server_addr.sin_port = htons(1234);
// 连接服务器
if (connect(client_sock, (struct sockaddr*)&server_addr, sizeof(server_addr)) == -1) {
perror("connect() error");
exit(1);
}
// 创建epoll对象
int epoll_fd = epoll_create(MAX_EVENTS);
struct epoll_event event;
struct epoll_event* events = malloc(sizeof(struct epoll_event) * MAX_EVENTS);
// 将客户端socket添加到epoll对象中
event.events = EPOLLIN;
event.data.fd = client_sock;
epoll_ctl(epoll_fd, EPOLL_CTL_ADD, client_sock, &event);
while (1) {
// 等待事件的发生
event_cnt = epoll_wait(epoll_fd, events, MAX_EVENTS, -1);
if (event_cnt == -1) {
perror("epoll_wait() error");
break;
}
// 处理事件
for (i = 0; i < event_cnt; ++i) {
if (events[i].data.fd == client_sock) {
// 服务器消息到达
str_len = read(client_sock, buf, BUF_SIZE);
if (str_len == 0) {
epoll_ctl(epoll_fd, EPOLL_CTL_DEL, client_sock, NULL);
close(client_sock);
} else {
printf("Server message: %s\n", buf);
}
}
}
}
// 关闭socket和epoll对象
close(client_sock);
close(epoll_fd);
return 0;
}
总结
通过使用epoll实现简单的C/S通信,我们可以实现服务器和客户端之间的异步通信。epoll是一种高效的I/O事件通知机制,可以同时监听多个I/O事件,当事件满足条件时通过回调函数进行处理。在实际的网络编程中,可以根据具体的需求灵活运用epoll来提高程序的性能和并发处理能力。
参考资料:
- epoll - Linux man page
- 《Linux高性能服务器编程》

评论 (0)