欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

简单的epoll模型

程序员文章站 2024-03-23 10:18:16
...

 水平触发模式

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <ctype.h>
#include <sys/epoll.h>

int main(int argc,const char* argv[]){
	if(argc < 2){
		printf("eg: ./a.out port\n");
		exit(1);
	}

	struct sockaddr_in serv_addr;
	socklen_t serv_len = sizeof(serv_addr);
	int port = atoi(argv[1]);

	//创建套接字
	int lfd = socket(AF_INET,SOCK_STREAM,0);
	//初始化socket_in
	memset(&serv_addr,0,serv_len);
	serv_addr.sin_family = AF_INET;					//地址族
	serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);	//监听本机所有的IP
	serv_addr.sin_port = htons(port); 				//设置端口

	//绑定端口和IP
	bind(lfd,(struct sockaddr*)&serv_addr,serv_len);

	//设置同时监听的最大个数
	listen(lfd,36);
	printf("start accept ...\n");

	struct sockaddr_in client_addr;
	socklen_t cli_len = sizeof(client_addr);

	//创建epoll树根节点
	int epfd = epoll_create(2000);
	//初始化epoll树
	struct epoll_event ev;
	ev.events = EPOLLIN;	//边沿模式;
	ev.data.fd = lfd;
	epoll_ctl(epfd,EPOLL_CTL_ADD,lfd,&ev);
	struct epoll_event all[2000];	
	while(1){
		//使用epoll通知内核fd文件IO检测
		int ret = epoll_wait(epfd,all,sizeof(all)/sizeof(all[0]),-1);
		int i;
		for(i=0;i<ret;++i){
			//遍历all数组中的前ret个元素
			int fd = all[i].data.fd;
			//判断是否有新连接
			if(fd == lfd){
				//有新连接
				int cfd = accept(lfd,(struct sockaddr*)&client_addr,&cli_len);
				if(cfd == -1){
					perror("accept error");
					exit(-1);
				}
				//将新得到的cfd上epoll树
				ev.events = EPOLLIN;
				ev.data.fd = cfd;
				epoll_ctl(epfd,EPOLL_CTL_ADD,cfd,&ev);
				//打印客户端信息
				char ip[64] = {0};
				printf("New Client IP: %s, Port: %d\n", 
					inet_ntop(AF_INET,&client_addr.sin_addr.s_addr,ip,sizeof(ip)),
					ntohs(client_addr.sin_port));
			}else{
				//处理已连接的客户端发过来的数据
				if(!all[i].events & EPOLLIN){
					//如果没有读的操作那么跳过
					continue;
				}
				//读数据
				char buf[1024] = {0};
				int len = recv(fd,buf,sizeof(buf),0);
				if(len == -1){
					perror("recv error");
					exit(1);
				}else if(len ==0){
					//客户端关闭
					printf("client disconnected...\n");
					//下树
					ret = epoll_ctl(epfd,EPOLL_CTL_DEL,fd,NULL);
					if(ret == -1){
						perror("epoll_ctl - del error");
						exit(1);
					}
					close(fd);
				}else{
					//正常情况
					printf("recv buf: %s\n",buf);
					//回给客户端数据
					write(fd,buf,len);
				}
			}
		}
	}

	close(lfd);
	return 0;
}

 边沿模式

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <ctype.h>
#include <sys/epoll.h>

int main(int argc,const char* argv[]){
	if(argc < 2){
		printf("eg: ./a.out port\n");
		exit(1);
	}

	struct sockaddr_in serv_addr;
	socklen_t serv_len = sizeof(serv_addr);
	int port = atoi(argv[1]);

	//创建套接字
	int lfd = socket(AF_INET,SOCK_STREAM,0);
	//初始化socket_in
	memset(&serv_addr,0,serv_len);
	serv_addr.sin_family = AF_INET;					//地址族
	serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);	//监听本机所有的IP
	serv_addr.sin_port = htons(port); 				//设置端口

	//绑定端口和IP
	bind(lfd,(struct sockaddr*)&serv_addr,serv_len);

	//设置同时监听的最大个数
	listen(lfd,36);
	printf("start accept ...\n");

	struct sockaddr_in client_addr;
	socklen_t cli_len = sizeof(client_addr);

	//创建epoll树根节点
	int epfd = epoll_create(2000);
	//初始化epoll树
	struct epoll_event ev;
	ev.events = EPOLLIN | EPOLLET;	//边沿模式;
	ev.data.fd = lfd;
	epoll_ctl(epfd,EPOLL_CTL_ADD,lfd,&ev);
	struct epoll_event all[2000];	
	while(1){
		//使用epoll通知内核fd文件IO检测
		int ret = epoll_wait(epfd,all,sizeof(all)/sizeof(all[0]),-1);
		int i;
		for(i=0;i<ret;++i){
			//遍历all数组中的前ret个元素
			int fd = all[i].data.fd;
			//判断是否有新连接
			if(fd == lfd){
				//有新连接
				int cfd = accept(lfd,(struct sockaddr*)&client_addr,&cli_len);
				if(cfd == -1){
					perror("accept error");
					exit(1);
				}
				//将新得到的cfd上epoll树
				ev.events = EPOLLIN | EPOLLET;	//边沿模式;
				ev.data.fd = cfd;
				epoll_ctl(epfd,EPOLL_CTL_ADD,cfd,&ev);
				//打印客户端信息
				char ip[64] = {0};
				printf("New Client IP: %s, Port: %d\n", 
					inet_ntop(AF_INET,&client_addr.sin_addr.s_addr,ip,sizeof(ip)),
					ntohs(client_addr.sin_port));
			}else{
				//处理已连接的客户端发过来的数据
				if(!all[i].events & EPOLLIN){
					//如果没有读的操作那么跳过
					continue;
				}
				//读数据
				char buf[1024] = {0};
				int len = recv(fd,buf,sizeof(buf),0);
				if(len == -1){
					perror("recv error");
					exit(-1);
				}else if(len ==0){
					//客户端关闭
					printf("client disconnected...\n");
					//下树
					ret = epoll_ctl(epfd,EPOLL_CTL_DEL,fd,NULL);
					if(ret == -1){
						perror("epoll_ctl - del error");
						exit(1);
					}
					close(fd);
				}else{
					//正常情况
					printf("recv buf: %s\n",buf);
					//回给客户端数据
					write(fd,buf,len);
				}
			}
		}
	}

	close(lfd);
	return 0;
}

 边沿非阻塞模式

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <ctype.h>
#include <sys/epoll.h>
#include <fcntl.h>
#include <errno.h>


int main(int argc,const char* argv[]){
	if(argc < 2){
		printf("eg: ./a.out port\n");
		exit(1);
	}

	struct sockaddr_in serv_addr;
	socklen_t serv_len = sizeof(serv_addr);
	int port = atoi(argv[1]);

	//创建套接字
	int lfd = socket(AF_INET,SOCK_STREAM,0);
	//初始化socket_in
	memset(&serv_addr,0,serv_len);
	serv_addr.sin_family = AF_INET;					//地址族
	serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);	//监听本机所有的IP
	serv_addr.sin_port = htons(port); 				//设置端口

	//绑定端口和IP
	bind(lfd,(struct sockaddr*)&serv_addr,serv_len);

	//设置同时监听的最大个数
	listen(lfd,36);
	printf("start accept ...\n");

	struct sockaddr_in client_addr;
	socklen_t cli_len = sizeof(client_addr);

	//创建epoll树根节点
	int epfd = epoll_create(2000);
	//初始化epoll树
	struct epoll_event ev;
	ev.events = EPOLLIN;	
	ev.data.fd = lfd;
	epoll_ctl(epfd,EPOLL_CTL_ADD,lfd,&ev);
	struct epoll_event all[2000];	
	while(1){
		//使用epoll通知内核fd文件IO检测
		int ret = epoll_wait(epfd,all,sizeof(all)/sizeof(all[0]),-1);
		int i;
		for(i=0;i<ret;++i){
			//遍历all数组中的前ret个元素
			int fd = all[i].data.fd;
			//判断是否有新连接
			if(fd == lfd){
				//有新连接
				int cfd = accept(lfd,(struct sockaddr*)&client_addr,&cli_len);
				if(cfd == -1){
					perror("accept error");
					exit(-1);
				}
				//设置cfd为非阻塞模式
				int flag = fcntl(cfd,F_GETFL);
				flag |= O_NONBLOCK;
				fcntl(cfd,F_SETFL,flag);
				//将新得到的cfd上epoll树
				ev.events = EPOLLIN | EPOLLET; //边沿模式
				ev.data.fd = cfd;
				epoll_ctl(epfd,EPOLL_CTL_ADD,cfd,&ev);
				//打印客户端信息
				char ip[64] = {0};
				printf("New Client IP: %s, Port: %d\n", 
					inet_ntop(AF_INET,&client_addr.sin_addr.s_addr,ip,sizeof(ip)),
					ntohs(client_addr.sin_port));
			}else{
				//处理已连接的客户端发过来的数据
				if(!all[i].events & EPOLLIN){
					//如果没有读的操作那么跳过
					continue;
				}
				//读数据
				char buf[1024] = {0};
				int len;
				while((len = recv(fd,buf,sizeof(buf),0))>0){
					write(STDOUT_FILENO,buf,len);
					//发送给客户端
					send(fd,buf,len,0);
				}

				if(len == -1){
					if(errno == EAGAIN){
						printf("\n缓冲区读完\n");
					}else{
						perror("recv error");
						exit(1);
					}
				}else if(len ==0){
					//客户端关闭
					printf("client disconnected...\n");
					//下树
					ret = epoll_ctl(epfd,EPOLL_CTL_DEL,fd,NULL);
					if(ret == -1){
						perror("epoll_ctl - del error");
						exit(1);
					}
					close(fd);
				}
			}
		}
	}

	close(lfd);
	return 0;
}

可以使用nc进行测试