TCP_网络编程(二) epoll的管理reactor

本文详细介绍了epoll在TCP网络编程中的应用,包括如何使用epoll、ET与LT的工作模式,以及其底层的红黑树和消息队列原理。同时,文章探讨了epoll与reactor模式的关系,阐述了reactor模式的组件及其实现方法,强调了在封装reactor时对监听和连接fd的处理。
摘要由CSDN通过智能技术生成

epoll

epoll如何使用

1、epoll在服务器底层使用时,一定会和一个死循环一起。
2、关闭一个fd时,记得也要从epoll中删除。

close(clientfd);

ev.events = EPOLLIN;
ev.data.fd = clientfd;
epoll_ctl(epfd, EPOLL_CTL_DEL, clientfd, &ev);

3、
EPOLLIN触发条件:
处于可读状态。
从不可读状态变为可读状态。
EPOLLOUT触发条件:

EPOLLOUT触发条件:
处于可写状态。
从不可写状态变为可写状态。

”可读事件"表示有数据到来,"可写事件"表示内核缓冲区有剩余发送空间,“错误事件“表示socket发生了一些网络错误。
面试题:
fd可读可写的条件?

4、一个fd可能同时出现可读又可写状态,比如一个fd可写事件进行发送数据时,send返回-1,此时代表数据没发完,要继续发送。就要给ev.events |= EPOLLOUT,其他地方还会判断epollin,所以有同时in out出现的情况。

epoll的et与lt

LT:为水平触发,这个是默认的,recvbuf中有数据就会一直触发;
ET:为边缘触发,recvbuf中从无到有数据,才触发。
通常ET+循环使用,来处理小块数据。若处理大块数据时,会只处理这一个fd,耗时较长。
LT用来处理大块数据。

epoll的底层原理

红黑树+消息队列.

epoll的介绍,和使用函数?

答:eopll其实就是典型的io多路复用,最大的特点就是支持高并发。
Epoll_create():创建一个epoll对象,且内部初始化了红黑树和双向链表;
红黑树记录的是要观察监听事件的socket;
双向链表中记录的是所有有数据/有事件的socket
Epoll_ctl():将socket和与此socket相关的事件添加到epoll对象中去,就可以通过epoll对象来监视这个socket上的数据往来;
EPOLL_CTL_ADD:等价于往红黑树中增加节点;
EPOLL_CTL_DEL:等价于从红黑树中删除节点;
EPOLL_CTL_MOD:等价于修改已有的红黑树节点;可增加减少读写事件
Epoll_wait():阻塞一小段时间等待事件发生,返回事件集合。其实就是从双向链表中把节点数据考出去,然后从链表中删除。

reactor

epoll的io管理,reactor,即反应堆。
Reactor 模式是用于同步 I/O,中心思想是将所有要处理的 I/O 事件注册到一个中心 I/O 多路复用器上,同时主线程/进程阻塞在多路复用器上;
一旦有 I/O 事件到来或是准备就绪(文件描述符或 socket 可读、写),多路复用器返回并将事
先注册的相应 I/O 事件分发到对应的处理器中
Reactor 模型有三个重要的组件:
 多路复用器:由操作系统提供,在 linux 上一般是 select, poll, epoll 等系统调用。
 事件分发器:将多路复用器中返回的就绪事件分到对应的处理函数中。epoll_wait
 事件处理器:负责处理特定事件的处理函数。

如何封装成reactor

1、服务器端fd有两种,分别为:监听的fd和连接进来的fd,二者都要放入epoll进行监听;
2、

代码实现

struct sockitem { //可以存储半包数据
	int sockfd; // 
	int (*callback)(int fd, int events, void *arg);

	char recvbuffer[1024]; // recvbuf存储数据,避免数据没有接受完整
	char sendbuffer[1024]; //


	// 

};

// mainloop / eventloop --> epoll -->  
struct reactor { //与反应堆相关的全局变量

	int epfd;
	struct epoll_event events[512];
	

};


struct reactor *eventloop = NULL;


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


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

	struct sockitem *si = (struct sockitem*)arg;

	send(fd, "hello\n", 6, 0); //

	//发送完后要将fd的状态设为原样
	struct epoll_event ev;
	ev.events = EPOLLIN | EPOLLET;
	//ev.data.fd = clientfd;
	si->sockfd = fd;
	si->callback = recv_cb;
	ev.data.ptr = si;

	epoll_ctl(eventloop->epfd, EPOLL_CTL_MOD, fd, &ev);

}

//  ./epoll 8080

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

	//int clientfd = events[i].data.fd;
	struct sockitem *si = (struct sockitem*)arg;
	struct epoll_event ev;

	char buffer[1024] = {0};
	int ret = recv(fd, buffer, 1024, 0);
	if (ret < 0) {

		if (errno == EAGAIN || errno == EWOULDBLOCK) { //
			return -1;
		} else {
			
		}

		

		ev.events = EPOLLIN;
		//ev.data.fd = fd;
		epoll_ctl(eventloop->epfd, EPOLL_CTL_DEL, fd, &ev);

		close(fd);

		free(si);
		

	} else if (ret == 0) { //

		// 
		printf("disconnect %d\n", fd);

		

		ev.events = EPOLLIN;
		//ev.data.fd = fd;
		epoll_ctl(eventloop->epfd, EPOLL_CTL_DEL, fd, &ev);

		close(fd);

		free(si);
		
	} else {

		printf("Recv: %s, %d Bytes\n", buffer, ret);

		// fd --> full 
		// fd epollout 
		//ret = send(fd, buffer, ret, 0);
		//if (ret == -1) {

		// }
		// 
		// 

		//此处解析http、websocket协议数据

		//当待发送的数据过大要多次send时,
		struct epoll_event ev;
		ev.events = EPOLLOUT | EPOLLET;
		//ev.data.fd = clientfd;
		si->sockfd = fd;
		si->callback = send_cb;
		ev.data.ptr = si;

		epoll_ctl(eventloop->epfd, EPOLL_CTL_MOD, fd, &ev);

	}

}

int accept_cb(int fd, int events, void *arg) {
	struct sockaddr_in client_addr;
	memset(&client_addr, 0, sizeof(struct sockaddr_in));
	socklen_t client_len = sizeof(client_addr);
	
	int clientfd = accept(fd, (struct sockaddr*)&client_addr, &client_len);
	if (clientfd <= 0) return -1;

	char str[INET_ADDRSTRLEN] = {0};
	printf("recv from %s at port %d\n", inet_ntop(AF_INET, &client_addr.sin_addr, str, sizeof(str)),
		ntohs(client_addr.sin_port));

	struct epoll_event ev;
	ev.events = EPOLLIN | EPOLLET; //accept接收后先关注客户端是否有数据进来,来处理,所以设置epollin可读
	//ev.data.fd = clientfd;

	struct sockitem *si = (struct sockitem*)malloc(sizeof(struct sockitem));
	si->sockfd = clientfd;
	si->callback = recv_cb;
	ev.data.ptr = si;
	
	epoll_ctl(eventloop->epfd, EPOLL_CTL_ADD, clientfd, &ev);
	
	return clientfd;
}

void *worker_thread(void *arg) {

	while (1) {

		int nready = epoll_wait(eventloop->epfd, eventloop->events, 512, -1);
		if (nready < -1) {
			break;
		}
// listen 
// 
		int i = 0;
		for (i = 0;i < nready;i ++) {



			if (eventloop->events[i].events & EPOLLIN) {
				//printf("sockitem\n");
				struct sockitem *si = (struct sockitem*)eventloop->events[i].data.ptr;
				si->callback(si->sockfd, eventloop->events[i].events, si);


			}

			if (eventloop->events[i].events & EPOLLOUT) {

				struct sockitem *si = (struct sockitem*)eventloop->events[i].data.ptr;
				si->callback(si->sockfd, eventloop->events[i].events, si);

			}


		}

	}

}

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;
	}

	
	eventloop = (struct reactor*)malloc(sizeof(struct reactor));
	// epoll opera

	eventloop->epfd = epoll_create(1);


	struct epoll_event ev;
	ev.events = EPOLLIN;
	//ev.data.fd = sockfd; //int idx = 2000;
	

	struct sockitem *si = (struct sockitem*)malloc(sizeof(struct sockitem));
	si->sockfd = sockfd;
	si->callback = accept_cb;
	ev.data.ptr = si;
	
	epoll_ctl(eventloop->epfd, EPOLL_CTL_ADD, sockfd, &ev);

	//主线程负责accept,子线程负责clientfd
	pthread_t id;
	pthread_create(&id, NULL, worker_thread, NULL);
	

	//pthread_cond_waittime();
	while(1) {

		

	}
	

}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值