epoll_create函数
int epoll_create(int size); //创建一棵监听红黑树
size:创建的红黑树的监听节点数量。(仅供内核参考。)
返回值:指向新创建的红黑树的根节点的 fd。
失败: -1 errno
epoll_ctl函数
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event); //操作监听红黑树
epfd:epoll_create 函数的返回值。 epfd
op:对该监听红黑数所做的操作。
EPOLL_CTL_ADD 添加 fd 到 监听红黑树
EPOLL_CTL_MOD 修改 fd 在 监听红黑树上的监听事件。
EPOLL_CTL_DEL 将一个 fd 从监听红黑树上摘下(取消监听)
fd:
待监听的 fd
event: 本质 struct epoll_event 结构体 地址
成员 events: EPOLLIN / EPOLLOUT / EPOLLERR
成员 data: 联合体(共用体):
int fd; 对应监听事件的 fd
void *ptr;
uint32_t u32;
uint64_t u64;
返回值:成功 0; 失败: -1 errno
epoll_wait函数
int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout); //阻塞监听。
epfd:epoll_create 函数的返回值。 epfd
events:传出参数,【数组】, 满足监听条件的 那些 fd 结构体。
maxevents:数组 元素的总个数。 1024
struct epoll_event evnets[1024]
timeout:
-1: 阻塞
0: 不阻塞
>0: 超时时间 (毫秒)
返回值:
> 0: 满足监听的 总个数。 可以用作循环上限。
0: 没有 fd 满足监听事件
-1:失败。 errno
epoll_server.c
/*************************************************************************
> File Name: epoll_server.c
> Author: zsy
> Created Time: 2023年07月25日 星期二 15时09分43秒
************************************************************************/
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<pthread.h>
#include<arpa/inet.h>
#include<sys/socket.h>
#include<sys/epoll.h>
#include<ctype.h>
#define SERV_PORT 9999
#define MAXEVENTS 1024
int main(int argc, char* argv[])
{
int lfd, cfd, epfd;
int ret, nready;
int i, j;
int read_size;
char buf[1024];
// 服务器的地址结构
struct sockaddr_in serv_addr;
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
serv_addr.sin_port = htons(SERV_PORT);
lfd = socket(AF_INET, SOCK_STREAM, 0);
bind(lfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr));
listen(lfd, 5);
// 客户端的地址结构
struct sockaddr_in clnt_addr;
socklen_t clnt_addr_size;
char clnt_ip[1024];
unsigned short int clnt_port;
// cfd = accept(lfd, (struct sockaddr *)&clnt_addr, &clnt_addr_size);
// printf("client ip is: %s, port is: %d \n",
// inet_ntop(AF_INET, &clnt_addr.sin_addr.s_addr, clnt_ip, sizeof(clnt_ip)),
// ntohs(clnt_addr.sin_port));
struct epoll_event tep, ep[MAXEVENTS];
// 创建一个红黑树
epfd = epoll_create(100);
// 给红黑树添加一个lfd结点
tep.events = EPOLLIN;
tep.data.fd = lfd;
epoll_ctl(epfd, EPOLL_CTL_ADD, lfd, &tep);
while(1){
// lfd 遇到了读事件,即需要产生一个与客户端通讯的套接字
// timeout = -1, 阻塞等待lfd读事件.
nready = epoll_wait(epfd, ep, MAXEVENTS, -1);
if(nready == -1){
perror("epoll_wait error \n");
continue;
}
for(i = 0; i < nready; i++){
// 当lfd满足条件,说明有客户端请求通信。
if(ep[i].data.fd == lfd){
clnt_addr_size = sizeof(clnt_addr);
cfd = accept(lfd, (struct sockaddr *)&clnt_addr, &clnt_addr_size);
printf("client ip is: %s, port is: %d \n",
inet_ntop(AF_INET, &clnt_addr.sin_addr.s_addr, clnt_ip, sizeof(clnt_ip)),
ntohs(clnt_addr.sin_port));
// 将新建立的cfd加入红黑树
tep.events = EPOLLIN;
tep.data.fd = cfd;
epoll_ctl(epfd, EPOLL_CTL_ADD, cfd, &tep);
}
// 除了lfd,就是cfd, 说明有客户端在传输数据。
// 在ep数组里的都是有通信请求的
else{
read_size = read(ep[i].data.fd, buf, sizeof(buf));
if(read_size == 0){
// 该客户端已经断开连接
// 先摘结点,再断开。
printf("client closed \n");
epoll_ctl(epfd, EPOLL_CTL_DEL, ep[i].data.fd, NULL);
close(ep[i].data.fd);
}
else if(read_size < 0){
printf("read error \n");
// 先摘结点,再断开。
epoll_ctl(epfd, EPOLL_CTL_DEL, ep[i].data.fd, NULL);
close(ep[i].data.fd);
}
else{
write(STDOUT_FILENO, buf, read_size);
for(j = 0; j < read_size; j++){
buf[j] = toupper(buf[j]);
}
write(ep[i].data.fd, buf, read_size);
}
}
}
}
close(lfd);
printf("OVER!\n");
return 0;
}