如果select或epoll检测到描述符可读,之后并不读取数据,那么下次调用select或epoll_wait时是否还会触发描述符可读事件
1. select
select函数结论:只要文件描述符可读或可写,select就会一直检测到事件并返回
select测试:
#include <sys/socket.h>
#include <string.h>
#include <sys/time.h>
#include <netinet/in.h>
#include <sys/ioctl.h>
#include <stdlib.h>
#include <errno.h>
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/select.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
#define TRUE 1
#define FALSE 0
int main(int argc, char *argv[])
{
int i, len, rc;
int listen_sd, new_sd = 0;
char buffer[80];
struct sockaddr_in server_addr;
fd_set master_set;
// Listen
listen_sd = socket(AF_INET, SOCK_STREAM, 0);
if (listen_sd < 0)
{
perror("socket() failed");
exit(-1);
}
// Bind
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(atoi(argv[1]));
rc = bind(listen_sd, (struct sockaddr *) &server_addr, sizeof(server_addr));
if (rc < 0)
{
perror("bind() failed\n");
close(listen_sd);
exit(-1);
}
// Listen
rc = listen(listen_sd, 32);
if (rc < 0)
{
perror("listen() failed\n");
close(listen_sd);
exit(-1);
}
new_sd = accept(listen_sd, NULL, NULL);
// Intialize sd set
FD_ZERO(&master_set);
FD_SET(new_sd, &master_set);
do
{
printf("Waiting on select()...\n");
rc = select(new_sd + 1, &master_set, NULL, NULL, NULL);
if (rc < 0)
{
perror(" select() failed\n");
break;
}
if (rc == 0)
{
printf(" select() timed out. End program.\n");
break;
}
printf("recv select\n");
if (FD_ISSET(new_sd, &master_set))
{
#if 0
rc = recv(new_sd, buffer, sizeof(buffer), 0);
if (rc < 0)
{
perror(" recv() failed\n");
break;
}
// The connection has been closed by the client
if (rc == 0)
{
printf(" Connection closed\n");
break;
}
printf("recv:%s\n", buffer);
#else
printf("do nothing...\n");
#endif
}
}
while(1);
return 0;
}
结果为会一直触发
Waiting on select()...
recv select
do nothing...
Waiting on select()...
recv select
do nothing...
Waiting on select()...
recv select
do nothing...
Waiting on select()...
recv select
do nothing...
Waiting on select()...
recv select
do nothing...
Waiting on select()...
recv select
2. epoll
说明: epoll 对文件描述符的操作有两种模式:LT(level trigger)和 ET(edge trigger)。LT 模式是默认模式,LT 模式与 ET 模式的区别如下:
LT模式: 电平触发,当 epoll_wait 检测到描述符事件发生并将此事件通知应用程序,应用程序可以不立即处理该事件。下次调用 epoll_wait 时,会再次响应应用程序并通知此事件。
ET模式: 边沿触发,当 epoll_wait 检测到描述符事件发生并将此事件通知应用程序,应用程序必须立即处理该事件。如果不处理,下次调用 epoll_wait 时,不会再次响应应用程序并通知此事件。
epoll详解:https://blog.csdn.net/qq_19923217/article/details/81943705
测试结论:epoll默认模式(LT模式)时,若检测到描述符可读后不读取出数据,那么下次调用epoll_wait还会触发可读事件
ET模式模式时,若检测到描述符可读后不读取出数据,下次调用epoll_wait不会在触发可读事件
**注:**两种模式的测试代码仅ev.events = EPOLLIN | EPOLLET;此行不同,LT模式删去EPOLLET即可
#define SERVER_PORT 8888
#define MAXDATASIZE 100
#define SERVER_IP "192.168.1.15"
int main()
{
int sockfd, numbytes;
char buf[MAXDATASIZE];
struct sockaddr_in server_addr;
if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
{
perror("socket");
exit(1);
}
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(SERVER_PORT);
server_addr.sin_addr.s_addr = inet_addr(SERVER_IP);
bzero(&(server_addr.sin_zero),sizeof(server_addr.sin_zero));
if (connect(sockfd, (struct sockaddr *)&server_addr,sizeof(struct sockaddr_in)) == -1)
{
perror("connect error");
exit(1);
}
struct epoll_event ev;
struct epoll_event events[10];
int epollfd = epoll_create(10);
ev.events = EPOLLIN | EPOLLET;
ev.data.fd = sockfd;
epoll_ctl(epollfd, EPOLL_CTL_ADD, sockfd, &ev);
while(1)
{
printf("epoll wait...\n");
int ret = epoll_wait(epollfd, events, 10, -1);
printf("ret: %d\n", ret);
}
close(sockfd);
return 0;
}