epoll读写事件触发的条件

欢迎加QQ学习交流群309798848

LT模式

EPOLLIN触发条件:

  1. 处于可读状态。
  2. 从不可读状态变为可读状态。

EPOLLOUT触发条件:

  1. 处于可写状态。
  2. 从不可写状态变为可写状态。

说白了,LT模式就是能读的时候就可读,能写的时候就可写。这并不是废话。

什么叫可读状态?什么叫不可读状态?

一个水杯里有水就是可读状态,水杯里没水就是不可读状态。读数据相当于喝水,喝(读)到水杯空了就不能喝(读)了。当朋友又给我到了一点水,这时候就从不可读变为可读了。(喝水等于读对方发来的数据)

什么叫可写状态?什么叫不可泄状态?

一个水杯未装满水就是可写状态,装满了水就是不可写状态。写数据相当于给客人倒水,客人水杯满了就不能再给他倒(写)了。当客人喝了一点,这时候就从不可写变了可写了。(倒水等于给对方发送数据)

就绪事件包含EPOLLIN的条件:

  • 刚建立连接
  • 有数据可读
  • 断开连接

就绪事件包含EPOLLOUT的条件:

  • 内核写缓冲区未满,可写

ET模式

LT模式比较好理解,关键是ET模式。

EPOLLIN触发条件:

  1. 从不可读状态变为可读状态。
  2. 内核接收到新发来的数据。

EPOLLOUT触发条件:

  1. 从不可写状态变为可写状态。

就绪事件包含EPOLLIN的条件:

  • 刚建立连接
  • 内核接收到新数据
  • 断开连接

就绪事件包含EPOLLOUT的条件:

  • 每次注册EPOLLOUT,下一次epoll_wait返回就绪事件包含EPOLLOUT(刚注册当然可写)
  • 内核写缓冲区从不可写变成可写
  • 同时注册了EPOLLIN和EPOLLOUT,如果接收到新数据时可写则就绪事件既包含EPOLLIN也包含EPOLLOUT

下面的demo用来验证ET模式同时注册了EPOLLIN和EPOLLOUT的结果

#include <sys/socket.h>
#include <sys/epoll.h>
#include <iostream>
#include <vector>
#include <stdlib.h>
#include <netinet/in.h>	//sockaddr_in
#include <arpa/inet.h>	//inet_addr
#include <unistd.h>	//read
#include <errno.h>	//errno
#include <fcntl.h>  //fcntl
#include <memory.h> //memset

using namespace std;

static const short s_listenPort = 6666;

#define enableEpollEtMode 1

#define exitif(s, errStr) do { \
    if(s) \
    { \
        if(listenFd != -1) \
        { \
            close(listenFd); \
            listenFd = -1; \
        } \
        if(epollfd != -1) \
        { \
            close(epollfd); \
            epollfd = -1; \
        } \
        cout << errStr << endl; \
        exit(0); \
    } \
} while(0);

void errorHanding(const string& errStr)
{
	cout << errStr << endl;
	exit(0);
}

int listenFd = -1;
int epollfd = -1;

int main()
{
	listenFd = ::socket(AF_INET, SOCK_STREAM, 0);
	exitif(listenFd < 0, "socket");

	//复用ip和端口
	int on = 1;
	setsockopt(listenFd, SOL_SOCKET, SO_REUSEADDR, (char*)& on, sizeof on);
	setsockopt(listenFd, SOL_SOCKET, SO_REUSEPORT, (char*)& on, sizeof on);

	//将fd设为非阻塞
	int oldFlag = fcntl(listenFd, F_GETFL, 0);
	int r = fcntl(listenFd, F_SETFL, oldFlag | O_NONBLOCK);
	exitif(r, "fcntl");

	sockaddr_in localAddr;
	socklen_t addrLen = sizeof(localAddr);
	localAddr.sin_family = AF_INET;
	//localAddr.sin_addr.s_addr = inet_addr();
	string ip = "0.0.0.0";
	inet_pton(AF_INET, ip.c_str(), &localAddr.sin_addr);
	localAddr.sin_port = htons(s_listenPort);
	r = bind(listenFd, (struct sockaddr*) & localAddr, addrLen);
	exitif(r, "bind");

	r = listen(listenFd, SOMAXCONN);
	exitif(r, "listen");

	epollfd = epoll_create1(EPOLL_CLOEXEC);
	exitif(epollfd < 0, "epoll_create1");

	epoll_event listenFdInterestEvent;
	listenFdInterestEvent.events = EPOLLIN;
#ifdef enableEpollEtMode
	listenFdInterestEvent.events |= EPOLLET;
#endif //enableEpollEtMode
	listenFdInterestEvent.data.fd = listenFd;
	r = epoll_ctl(epollfd, EPOLL_CTL_ADD, listenFd, &listenFdInterestEvent);
	exitif(r, "epoll_ctl");

	vector<epoll_event> activeEventList(16);
	int timeout = 1;
	int nActive = -1;
	int acceptFd = -1;
	char readBuf[1024] = { 0 };
	char sendBuf[1024] = { 0 };

	while (1)
	{
		nActive = epoll_wait(epollfd, &activeEventList[0], activeEventList.size(), timeout);
		if (nActive < 0)
		{
			if (errno == EINTR)
				continue;

			exitif(1, "epoll_wait");
		}
		else if (nActive == 0)
		{
			continue;
		}
		else
		{
			for (size_t i = 0; i < nActive; ++i)
			{
				if (activeEventList[i].data.fd == listenFd)
				{
					//SOCK_NONBLOCK使新fd为非阻塞
					acceptFd = accept4(listenFd, NULL, NULL, SOCK_NONBLOCK | SOCK_CLOEXEC);
					exitif(acceptFd < 0, "accept4");

					epoll_event connFdEvent;
					//同时监听读写事件。此处注册了写事件,待会马上就会触发写事件
					connFdEvent.events = EPOLLIN | EPOLLOUT;
#ifdef enableEpollEtMode
					connFdEvent.events |= EPOLLET;
#endif //enableEpollEtMode
					connFdEvent.data.fd = acceptFd;
					r = epoll_ctl(epollfd, EPOLL_CTL_ADD, acceptFd, &connFdEvent);
					exitif(r, "epoll_ctl");

					cout << "new connection" << endl;
				}
				else
				{
					if (activeEventList[i].events & EPOLLIN)
					{
						int connFd = activeEventList[i].data.fd;

						memset(readBuf, 0, 1024);
						//read one byte one time
						r = read(connFd, readBuf, 1024);
						if (r < 0)
						{
							if (errno != EAGAIN && errno != EINTR)
							{
								r = epoll_ctl(epollfd, EPOLL_CTL_DEL, connFd, NULL);
								exitif(r, "epoll_ctl");
								close(connFd);
								errorHanding("read");
							}
						}
						if (r == 0)
						{
							cout << "peer closed" << endl;
							r = epoll_ctl(epollfd, EPOLL_CTL_DEL, connFd, NULL);
							exitif(r, "epoll_ctl");
							close(connFd);
						}
						else
						{
							cout << "EPOLLIN triggered,read data: " << readBuf << endl;

						}
					}
					if (activeEventList[i].events & EPOLLOUT)
					{
						cout << "EPOLLOUT triggered" << endl;
					}
					//if
					//{
					//	cout << "other event" << endl;
					//}
				}
			}
		}
	}


	close(listenFd);
	close(epollfd);

	return 0;
}

输出如图:

上面的demo在读事件触发的同时也触发了写事件。这里就绪事件包含EPOLLOUT不是因为突然从不可写变成可写,而只是通知状态更新。具体参考:Re: [Bug] epoll_wait return EPOLLOUT even with EPOLLET flag

  • 15
    点赞
  • 35
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值