栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 系统运维 > 运维 > Linux

手写一个epoll服务器 超详细解释

Linux 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

手写一个epoll服务器 超详细解释

首先必须谈谈epollin和epollout事件触发的条件 没看这个 整个epoll就是懵的

1、EPOLLOUT事件触发的条件?

(1)、一次write操作,写满了发送缓冲区,返回错误码为EAGAIN(11)。

(2)、对端读取了一些数据,又重新可写了,此时会触发EPOLLOUT。

简单地说:内核缓冲区由不可写变为可写时会触发,才会触发一次,所以叫边缘触发。

暴力方法:直接调用epoll_ctl()重新设置一下event就可以了, event跟原来的设置一模一样都行(但必须包含EPOLLOUT),关键是重新设置,就会马上触发一次EPOLLOUT事件。

2、EPOLLIN事件触发的条件?

对端有数据写入时才会触发。

epoll还分为LT和ET 即水平触发和边缘触发

LT就是只要满足条件我就会一直触发事件 而ET是你满足一次条件我触发一次事件

LT模式会一直触发EPOLLOUT,当缓冲区有数据时会一直触发EPOLLIN
ET模式会在连接建立后触发一次EPOLLOUT,当收到数据时会触发一次EPOLLIN
LT模式触发EPOLLIN时可以按需读取数据,残留了数据还会再次通知读取
ET模式触发EPOLLIN时必须把数据读取完,否则即使来了新的数据也不会再次通知了
LT模式的EPOLLOUT会一直触发,所以发送完数据记得删除,否则会产生大量不必要的通知
ET模式的EPOLLOUT事件若数据未发送完需再次注册,否则不会再有发送的机会
通常发送网络数据时不会依赖EPOLLOUT事件,只有在缓冲区满发送失败时会注册这个事件,期待被通知后再次发送

下面就是epoll服务器的代码 

#include 
#include 
#include 

#include 
#include 
#include 
#include 

#include 
#include 


struct sockitem {
	int sockfd;
	int (*callback)(int fd, int events, void *arg);`

}
//等会epoll传入的指针需要用到的结构体 用来回调

//  ./epoll 8080

int recv_cb(int fd, int events, void *arg) {

}
//具体的回调函数

int accept_cb(int fd, int events, void *arg) {

	int clientfd = accept();

	struct sockitem *si = (struct sockitem*)malloc(sizeof(struct sockitem));
	si->sockfd = clientfd;
	si->callback = recv_cb;
	//
	epoll_ctl()
}
//具体的回调函数
int main(int argc, char *argv[]) {

	if (argc < 2) {
		return -1;
	}

	int port = atoi(argv[1]);//

	int sockfd = socket(AF_INET, SOCK_STREAM, 0);
	if (sockfd < 0) {
		return -1;
	}

	struct sockaddr_in addr;
	memset(&addr, 0, sizeof(struct sockaddr_in));

	addr.sin_family = AF_INET;
	addr.sin_port = htons(port);
	addr.sin_addr.s_addr = INADDR_ANY;

	if (bind(sockfd, (struct sockaddr*)&addr, sizeof(struct sockaddr_in)) < 0) {
		return -2;
	}

	if (listen(sockfd, 5) < 0) {
		return -3;
	}
	//上面都是最基本的socket流程 不想过多讲述
	// 从这里开始epoll
	//首先创建一个epoll 参数无所谓01
	int epfd = epoll_create(1);
	//接下来创建epoll需要的结构体 该结构体用来像epoll内注册io事件和获取一些信息
	struct epoll_event ev, events[512] = {0};
	//ev用来临时加入epoll events用来获取已经被写入或被读取数据的io
	ev.events = EPOLLIN;//帮我监听epollin事件
	ev.data.fd = sockfd; //文件描述符是sockfd
	//epoll_event有个最大的好处就是他允许你带一个参数进去 那么这里我们就传入我们自定义的结构体 等下就能自动调用回调函数

	struct sockitem *si = (struct sockitem*)malloc(sizeof(struct sockitem));
	si->sockfd = sockfd;
	si->callback = accept_cb;
	//上面是填写结构体的值 没啥好说的
	//注意第二个值是一个回调函数 准备回调的
	ev.data.ptr = si;
	//将指针也写入epoll_event结构体

	epoll_ctl(epfd, EPOLL_CTL_ADD, sockfd, &ev);
	//将最开始用来listen的结构体传入epoll中开始监测
	
	while (1) {
		//写一个循环不断的去epoll中拿被改写过的IO
		int nready = epoll_wait(epfd, events, 512, 50);
		//这里的意思就是我去epfd对应的epoll中去拿 最多拿512个 给我放到首地址为events的地方 最长等待时间是50ms
		//此时间将四舍五入为系统时钟的粒度,并且内核调度延迟意味着阻塞间隔可能会少量溢出。
		if (nready < -1) {
			break;
		}
		//没收到的话 直接break掉

		int i = 0;
		for (i = 0;i < nready;i ++) {

			if (events[i].events & EPOLLIN) {
				//如果是epollin事件
				struct sockitem *si = (struct sockitem*)events[i].data.ptr;
				si->callback(events[i].data.fd, events[i].events, si);


			}

			if (events[i].events & EPOLLOUT) {
				//如果是epollout事件
	           //代码就不写了 跟上面流程是一样的
				

			}

		}

	}

}

 但是以上代码明显有几个思考的点 比如多线程下如何同时使用epoll 文件描述符之间如何传递?

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/974355.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号