epoll相关的内核源码分析可以参考之前的一篇文章。对于多个epoll监听同一个描述符的情况,区分ET和LT模式进行分别实验测试。在LT模式下,fd文件事件只会处理文件等待队列头部的回调,而对于ET模式来说,会查找等待队列上未过期的注册事件项并响应该事件。
实验程序:
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/epoll.h>
#include <arpa/inet.h>
#include <assert.h>
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <string>
#include <string.h>
#include <fcntl.h>
#include <stdlib.h>
#include <pthread.h>
/**
* 还是需要从内核底层原理来理解这里的机制
*/
int connfd;
int epollfd1, epollfd2;
int setnonblocking(int fd)
{
int old_option = fcntl(fd, F_GETFL);
int new_option = old_option | O_NONBLOCK;
fcntl(fd, F_SETFL, new_option);
return old_option;
}
void addfd(int epollfd, int fd, bool enable_et)
{
epoll_event event;
event.data.fd = fd;
event.events = EPOLLIN;
if (enable_et)
{
event.events |= EPOLLET;
}
epoll_ctl(epollfd, EPOLL_CTL_ADD, fd, &event);
}
void modfd(int epollfd, int fd)
{
epoll_event event;
event.data.fd = fd;
event.events = EPOLLIN;
event.events |= EPOLLET;
epoll_ctl(epollfd, EPOLL_CTL_MOD, fd, &event);
}
void* thread1(void* arg)
{
char buf[1024];
epoll_event event[1024];
while (1)
{
int number = epoll_wait(epollfd1, event, 1024, -1);
for (int i = 0; i < number; i++)
{
if (event[i].events & EPOLLIN)
{
//sleep(1);
int fd = event[i].data.fd;
printf("this is thread 1\n");
memset(buf, 0, 1024);
int ret = recv(fd, buf, 24, 0);
printf("%s\n", buf);
}
}
modfd(epollfd1, connfd);
}
return NULL;
}
void* thread2(void* arg)
{
char buf[1024];
epoll_event event[1024];
while (1)
{
int number = epoll_wait(epollfd2, event, 1024, -1);
for (int i = 0; i < number; i++)
{
if (event[i].events & EPOLLIN)
{
int fd = event[i].data.fd;
printf("this is thread 2\n");
memset(buf, 0, 1024);
int ret = recv(fd, buf, 24, 0);
printf("%s\n", buf);
}
}
modfd(epollfd2, connfd);
}
return NULL;
}
int main(int argc, char* argv[])
{
if (argc <= 2)
{
printf("usage: %s ip_address port_number\n",
basename(argv[0]));
return 1;
}
const char* ip = argv[1];
int port = atoi(argv[2]);
printf("ip is %s and port is %d\n", ip, port);
int ret = 0;
struct sockaddr_in address;
bzero(&address, sizeof(address));
address.sin_family = AF_INET;
inet_pton(AF_INET, ip, &address.sin_addr);
address.sin_port = htons(port);
int listenfd = socket(PF_INET, SOCK_STREAM, 0);
assert(listenfd >= 0);
int on = 1;
if (setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0) {
printf("setsockopt failure!\n");
return 0;
}
ret = bind(listenfd, (struct sockaddr*) & address, sizeof(address));
assert(ret != -1);
ret = listen(listenfd, 5);
assert(ret != -1);
epollfd1 = epoll_create(5);
assert(epollfd1 != -1);
epollfd2 = epoll_create(5);
assert(epollfd2 != -1);
struct sockaddr_in client_address;
socklen_t client_addrlength = sizeof(client_address);
int connfd = accept(listenfd,
(struct sockaddr*) & client_address,
&client_addrlength);
if (connfd < 0) {
printf("errno is: %d\n", errno);
close(listenfd);
}
setnonblocking(connfd);
int error;
pthread_t td1, td2;
addfd(epollfd1, connfd, true);
addfd(epollfd2, connfd, true);
pthread_create(&td1, NULL, thread1, NULL);
pthread_create(&td2, NULL, thread2, NULL);
pthread_join(td1, NULL);
pthread_join(td2, NULL);
close(connfd);
close(listenfd);
return 0;
}