缺点:一个fd等待时,其他fd无法被监听
2.多线程阻塞等待缺点:一个fd占用一个线程,线程过多,效率很低
3.非阻塞、忙轮询缺点:无效轮询占用大量CPU时间
4.Select缺点:能够打开的fd数量受内核限制,如需修改,需要编译内核。效率较低,每次需要遍历fd数组,复杂度和最大fd数量相同
5.Epoll优点:用红黑树管理fd,每次返回可以读写或者发生其他事件的fd,大部分情况下效率高于select。
缺点:Linux系统的API。当每个连接都非常活跃时,效率会低于select
Epoll API(Doc地址:epoll document) 1.头文件:#include2.创建Epoll
//size是Epoll最大监听的fd的数量,对应内部红黑树的最大节点数 //返回一个epoll的描述符 epoll fd int epoll_create(int size);3.设置Epoll
epoll_data_t data;
}
其中,events为监听的事件,可以是:
EPOLLIN 可读
EPOLLOUT 可写
EPOLLRDHUP socket另一端关闭连接
POLLPRI 预料之外的错误,很少用,参见https://man7.org/linux/man-pages/man2/poll.2.html
EPOLLERR 发生错误,默认开启,无法关闭,所以也没必要传入
EPOLLHUP 描述符被挂起
EPOLLET 边沿触发事件。默认情况下是水平触发
等
typedef union epoll_data {
void *ptr;
int fd;
uint32_t u32;
uint64_t u64;
} epoll_data_t;
data是一个结构体,可以是用户自行传入的任意数据的指针(这是实现Reactor模式的关键)
*/
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
4.监听Epoll事件
int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout);5.Epoll编程常用步骤
#define MAX_FD 1024
//创建 epoll
int epfd = epoll_crete(MAX_FD);
//将监听的socket添加进 epoll 中
epoll_ctl(epfd, EPOLL_CTL_ADD, listen_fd, &event);
while (1) {
//阻塞等待 epoll 中 的fd 触发
int active_cnt = epoll_wait(epfd, events, 1000, -1);
# 对返回的fd进行
for (i = 0 ; i < active_cnt; i++) {
//如果监听的fd触发,那么accept请求,并将fd加入epoll
if (evnets[i].data.fd == listen_fd) {
}
//如果其他fd触发,则进行读写
else if (events[i].events & EPOLLIN) {
}
else if (events[i].events & EPOLLOUT) {
}
}
}
Epoll回显服务器服务器端
#include客户端#include #include #include #define SERVER_PORT 9998 #define MAX_PENDING_ConNECTION 1024 #define EPOLL_MAX_FD (1024*1024) #define MAX_BUFFER_SIZE (1024*1024) using namespace std; bool running = true; int main(int argc, char* argv[]){ int32_t listen_fd = 0, epoll_fd = 0; sockaddr_in server_addr{}; // 创建监听套接字 listen_fd = socket(AF_INET, SOCK_STREAM, 0); // 绑定端口 server_addr.sin_family = AF_INET; server_addr.sin_addr.s_addr = htonl(INADDR_ANY); server_addr.sin_port = htons(SERVER_PORT); bind(listen_fd, (struct sockaddr*)&server_addr, sizeof(server_addr)); // 监听 listen(listen_fd, MAX_PENDING_CONNECTION); // 创建epoll epoll_fd = epoll_create(EPOLL_MAX_FD); if (epoll_fd < 0){ perror("Epoll create failed"); return -1; } struct epoll_event event; event.events = EPOLLIN; event.data.fd = listen_fd; // 将监听套接字加入epoll if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, listen_fd, &event) < 0){ perror("Epoll CTL add listen fd"); return -1; } struct epoll_event *events = new struct epoll_event[EPOLL_MAX_FD]; int client_fd, active_fd; sockaddr_in client_addr{}; char buffer[MAX_BUFFER_SIZE]; while(running){ // 等待epoll事件触发 active_fd = epoll_wait(epoll_fd, events ,EPOLL_MAX_FD, -1); // 遍历epoll for (int i=0; i %s", buffer); write(client_fd, buffer, n); } } // 套接字可写事件,没有监听,也不处理 else if (events[i].events & EPOLLOUT){ printf("Send Data -> Epoll out end! n"); } } } close(epoll_fd); close(listen_fd); return 0; }
#include#include #include #include #include #include #define MAX_BUFFER_SIZE (1024*1024) #define SERVER_PORT 9998 int main(int argc, char* argv[]) { int sockfd; char buffer[MAX_BUFFER_SIZE]; struct sockaddr_in server_addr{}; if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0){ perror("socket"); return -1; } server_addr.sin_family = AF_INET; server_addr.sin_port = htons(SERVER_PORT); if (inet_pton(AF_INET, "0.0.0.0", &server_addr.sin_addr) <= 0){ perror("inet_pton"); return -1; } if (connect(sockfd, (struct sockaddr*)&server_addr, sizeof(server_addr)) < 0){ perror("connect"); return -1; } int opts = 0; opts = fcntl(sockfd, F_GETFL); opts = opts | O_NONBLOCK; fcntl(sockfd, F_SETFL); char input[1024]; while(fgets(input, 1024, stdin) != nullptr){ printf("Sending -> %s", input); int n = send(sockfd, input, strlen(input), 0); if (n < 0){ perror("send"); continue; } usleep(500000); n = read(sockfd, buffer, MAX_BUFFER_SIZE); if (n <= 0){ perror("read"); return -1; } else{ buffer[n] = ' '; printf("Recv <- %s", buffer); continue; } } close(sockfd); return 0; }
运行服务器和客户端后,从终端向客户端输入即可看到现象。



