select特点:
-
使用位图(数组)实现对文件描述符集合的保存,最多允许同时监测1024个文件描述符;
-
需要应用和内核层的反复数据(文件描述符集合表)拷贝;
-
返回的集合表需要遍历寻找到达的事件;
-
只能工作在水平触发模式(低速模式),不能工作在边沿触发模式(高速模式)。
poll特点:
-
使用链表实现对文件描述符集合的保存,没有了监测的文件描述符上限限制;
-
需要应用和内核层的反复数据(文件描述符集合表)拷贝;
-
返回的集合表需要遍历寻找到达的事件;
-
只能工作在水平触发模式(低速模式),不能工作在边沿触发模式(高速模式)。
epoll特点:
-
使用红黑树(二叉树)实现文件描述符集合的存储,没有文件描述符上限限制,提高查找效率;
-
将文件描述符集合创建在内核层,避免了应用层和内核层的反复数据拷贝;
-
返回的是到达事件,不需要遍历,只需要处理事件即可;
-
可工作在水平触发模式(低速模式),也可工作在边沿触发模式(高速模式)。
epoll构建流程:
-
创建文件描述符集合 : int epoll_create(int size);
-
添加关注的文件描述符:int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
-
epoll通知内核开始进行事件监测 :int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout);
-
epoll返回时,获取到达事件的结果
-
根据到达事件做任务处理
int epoll_create(int size);
功能:通知内核创建文件描述符集合
参数:
size:监测的文件描述符个数
返回值:
成功:文件描述符(代表内核创建的集合)
失败:-1
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
功能:对epoll的文件描述符集合进行操作
参数:
epfd:创建的epoll集合
op:对文件描述符集合进行的操作
EPOLL_CTL_ADD : 添加文件描述符到集合
EPOLL_CTL_MOD : 修改集合中的文件描述符
EPOLL_CTL_DEL :删除集合中的文件描述符
fd:要操作的文件描述符
event:文件描述符对应的事件
typedef union epoll_data {
void *ptr;
int fd;
uint32_t u32;
uint64_t u64;
} epoll_data_t;
struct epoll_event {
uint32_t events; /* Epoll events */
epoll_data_t data; /* User data variable */
};
events:文件描述符的事件:
EPOLLIN: 读事件
EOPLLOUT:写事件
data.fd : 关注的文件描述符
返回值:
成功:0
失败:-1
int epoll_wait(int epfd, struct epoll_event *events,
int maxevents, int timeout);
功能:通知内核开始监测文件描述符的事件
参数:
epfd:监测的文件描述符集合
events:保存返回的到达事件的结果(数组)
struct epoll_event evs[MAX_FD_CNT];
evs;
maxevents:最大的事件个数
timeout:监测的超时时间
-1 :不设置超时(一直阻塞)
返回值:
成功:到达的事件的个数
失败:-1
利用epoll构建并发服务器
cs
#include "head.h"
#define SER_PORT 50001
#define SER_IP "192.168.0.180"
int n = 1;
int init_tcp_ser()
{
int sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0)
{
perror("socket error");
return -1;
}
struct sockaddr_in seraddr;
seraddr.sin_family = AF_INET;
seraddr.sin_port = htons(SER_PORT);
seraddr.sin_addr.s_addr = inet_addr(SER_IP);
int ret = bind(sockfd, (struct sockaddr *)&seraddr, sizeof(seraddr));
if (ret < 0)
{
perror("bind error");
close(sockfd);
return -1;
}
ret = listen(sockfd, 5);
if (ret < 0)
{
perror("listen error");
close(sockfd);
return -1;
}
return sockfd;
}
int epoll_fd_add(int epfds, int fd, uint32_t events)
{
struct epoll_event ev;
ev.events = events;
ev.data.fd = fd;
int ret = epoll_ctl(epfds, EPOLL_CTL_ADD, fd, &ev);
if (ret < 0)
{
perror("add error");
return -1;
}
return 0;
}
int main()
{
struct sockaddr_in cliaddr;
socklen_t clilen = sizeof(cliaddr);
int sockfd = init_tcp_ser();
if (sockfd < 0)
{
return -1;
}
int epfds = epoll_create(n);
if (epfds < 0)
{
perror("epoll_create error");
return -1;
}
epoll_fd_add(epfds, sockfd, EPOLLIN);
struct epoll_event evs[1024];
char buff[1024] = {0};
while (1)
{
int count = epoll_wait(epfds, evs, n, -1);
if (count < 0)
{
perror("epoll_wait error");
return -1;
}
for (int i = 0; i < count; ++i)
{
if (evs[i].data.fd == sockfd)
{
int connfd = accept(sockfd, (struct sockaddr *)&cliaddr, &clilen);
if (connfd < 0)
{
perror("accept error:");
return -1;
}
epoll_fd_add(epfds, connfd, EPOLLIN);
++n;
}
else
{
memset(buff, 0, sizeof(buff));
ssize_t cnt = recv(evs[i].data.fd, buff, sizeof(buff), 0);
if (cnt < 0)
{
perror("receive error");
epoll_ctl(epfds, EPOLL_CTL_DEL, evs[i].data.fd,NULL);
close(evs[i].data.fd);
continue;
}
else if (cnt == 0)
{
printf("offline\n");
epoll_ctl(epfds, EPOLL_CTL_DEL, evs[i].data.fd,NULL);
close(evs[i].data.fd);
break;
}
printf("%s\n", buff);
}
}
}
close(sockfd);
return 0;
}