/*
epoll三个重要函数, 底层红黑树
1.int epoll_create(int size); 创建 epoll 树的根节点
size:理论上最大数的节点个数,超出是会自动扩充
2.int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event); 对 epoll 树的操作 添加/删除/修改
op: EPOLL_CTL_ADD/EPOLL_CTL_DEL/EPOLL_CTL_MOD
fd: 文件描述符
*event: 连接到epoll树的文件描述符fd的事件
typedef union epoll_data {
void *ptr;
int fd;
uint32_t u32;
uint64_t u64;
} epoll_data_t;
struct epoll_event {
uint32_t events;
epoll_data_t data;
};
data:所选择的文件描述符
events:
EPOLLIN:读
EPOLLOUT:写
EPOLLET:边沿非阻塞
3.int epoll_wait(int epfd, struct epoll_event *events,
int maxevents, int timeout); 委托内核检测
maxevents:委托检测个数
*events:传出参数是个数组, 得到所检测的结果会存放在一个数组里面
*/
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <sys/epoll.h>
#include <string.h>
#include <arpa/inet.h>
#include <fcntl.h>
int main(int argc, char *argv[])
{
if(argc < 2)
{
printf("./a.out port\n");
exit(1);
}
// 创建socket
int sfd = socket(AF_INET, SOCK_STREAM, 0);
if(sfd == -1)
{
printf("socket err");
exit(1);
}
// 设置端口复用
int flags = 1;
int opt = setsockopt(sfd, SOL_SOCKET, SO_REUSEPORT, &flags, sizeof(flags));
if(opt == -1)
{
perror("setsockopt err");
exit(1);
}
// 绑定 服务端 IP 和 port struct sockaddr_in server;
struct sockaddr_in server;
memset(&server, 0x00, sizeof(server));
server.sin_addr.s_addr = htonl(INADDR_ANY);
server.sin_family = AF_INET;
server.sin_port = htons(atoi(argv[1]));
int ret = bind(sfd, (struct sockaddr *)&server, sizeof(server));
if(ret == -1)
{
perror("bind err");
exit(1);
}
// 设置监听
ret = listen(sfd, 128);
if(ret == -1)
{
perror("listen err");
exit(1);
}
// 客户端连接以及数据发送请求处理
struct sockaddr_in client;
socklen_t client_addr_len = sizeof(client);
// 创建eopll 树的根节点
int epfd = epoll_create(3000);
// 初始化 epoll 树
struct epoll_event ev;
ev.data.fd = sfd;
ev.events = EPOLLIN;
// 将服务端添加进 ePoll 树
ret = epoll_ctl(epfd, EPOLL_CTL_ADD, sfd, &ev);
if(ret == -1)
{
perror("epoll_ctl err");
exit(1);
}
struct epoll_event all[3000];
// epoll 与 客户端 连接、消息数据处理.
while(1)
{
// 委托 epoll 检查
int epno = epoll_wait(epfd, all, sizeof(all) / sizeof(all[0]), -1);
if(epno == -1)
{
perror("epoll_wait err");
exit(1);
}
// 遍历 委托检查返回的个数
for(int i = 0; i < epno; ++i)
{
int fd = all[i].data.fd;
// 判断是否有 新连接
if(fd == sfd)
{
printf("与客户端连接成功\n");
int cfd = accept(sfd, (struct sockaddr *)&client, &client_addr_len);
if(cfd == -1)
{
perror("accept err");
exit(1);
}
// 设置非阻塞模式
int flag = fcntl(cfd, F_GETFL);
flag |= SOCK_NONBLOCK;
fcntl(cfd, flag);
struct epoll_event temp;
temp.data.fd = cfd;
// 非阻塞边沿模式
temp.events = EPOLLIN | EPOLLET;
// 将得到的新连接 加入 epoll树
ret = epoll_ctl(epfd, EPOLL_CTL_ADD, cfd, &temp);
if(ret == -1)
{
perror("epoll_ctl err + 95");
exit(1);
}
}
// 客户端是否发送数据
else
{
// 只进行 读 事件
if(!(all[i].events & EPOLLIN))
{
continue;
}
// 接受客户端发送的数据
char buf[BUFSIZ] = {0};
ret = recv(fd, &buf, sizeof(buf), 0);
if(ret == -1)
{
perror("recv err");
exit(1);
}
else if(ret == 0)
{
printf("与客户端连接断开\n");
// 将 客户端 从 epoll 树删除
ret = epoll_ctl(epfd, EPOLL_CTL_DEL, fd, &ev);
if(ret == -1)
{
perror("epoll ctl del err");
exit(1);
}
close(fd);
}
else
{
printf("recv the data %s", buf);
send(fd, buf, ret, 0);
}
}
}
}
close(sfd);
return 0;
}