epoll面向io和面向事件编程


epoll

epoll是Linux操作系统提供的一种高性能I/O多路复用机制,epoll既可以面向io编程也可以面向事件编程(Reactor模式)

Reactor模式

与面向io编程(判断文件描述符)模式不同,Reactor模式是一种以面向事件思维处理并发I/O操作的设计模式,根据EPOLLIN、EPOLLOUT事件(判断是否可读、可写)对应处理I/O,实现高并发高性能的网络编程。

epoll代码实现

设置监听字sockfd

 int sockfd = socket(AF_INET, SOCK_STREAM, 0);
       struct sockaddr_in serveraddr;
       memset(&serveraddr, 0, sizeof(struct sockaddr_in));
 
       serveraddr.sin_family = AF_INET;
       serveraddr.sin_addr.s_addr = htonl(INADDR_ANY);
       serveraddr.sin_port = htons(2048);
 
       if (-1 == bind(sockfd, (struct sockaddr*)&serveraddr, sizeof(struct sockaddr))) {
           perror("bind");
           return -1;
       }
       listen(sockfd, 10);

在主函数中,设置监听套接字,绑定端口和ip地址,开始监听

面向io编程

			int epfd=epoll_create(1);
			struct epoll_event ev;
			ev.events=EPOLLIN;
			ev.data.fd=sockfd;
			epoll_ctl(epfd,EPOLL_CTL_ADD,sockfd,&ev);
			struct epoll_event events[1024]={0};

创建epoll实例epfd,事件结构体ev以及设置并初始化ev中的结构体事件集合,设置监听sockfd并加入到epfd中

   while(1){
	     int nready= epoll_wait(epfd,events,1024,-1);
	     int i=0;
	     for(i=0;i<nready;i++){
        int connfd=events[i].data.fd;
        if(sockfd==connfd){
        struct sockaddr_in clientaddr;
        socklen_t len =sizeof(clientaddr);
        int clientfd=accept(sockfd,(struct sockaddr*)&serveraddr,&len);
        ev.events=EPOLLIN |EPOLLET;
        ev.data.fd=clientfd;
        epoll_ctl(epfd,EPOLL_CTL_ADD,clientfd,&ev);
        printf("clientfd:%d\n",clientfd);
           }else if(events[i].events & EPOLLIN){
           char buffer[10]={0};
                int count=recv(connfd,buffer,10,0);
                if(count==0){
                    printf("disconnect\n");
                    epoll_ctl(epfd,EPOLL_CTL_DEL,connfd,NULL);
                   close(i);
                   continue;
                   }
                    send(connfd,buffer,count,0);
                printf("clientfd: %d,count:%d,buffer:%s\n",connfd,count,buffer);           

开始循环,等待出现就绪事件,遍历就绪事件,将i的文件描述符设为connfd,将请求的客户端文件描述符clientfd中的事件状态设为可读,并将clientfd加入到epfd中,如果显示这是这是一个就绪事件并且是可读的,则接收数据并在epfd中将connfd删除,最后输出数据。

面向事件编程(Reactor模式)

#define BUFFER_LENGTH   1024
		int accept_cb(int fd);
		int recv_cb(int fd);
		int send_cb(int fd);
		typedef int(*Rcallback)(int fd);

定义一个宏表示buffer长度,声明函数,定义回调函数类型

		struct conn_item{
		int fd;
		char rbuffer[BUFFER_LENGTH];
		int rlen;
		char wbuffer[BUFFER_LENGTH];
		int wlen;
		union{
		Rcallback accept_callback;
		Rcallback recv_callback;}recv_t;
		Rcallback send_callback;
		};

定义链接结构体,读入rbuffer,写入wbuffer,以及三个回调函数用于调用上面三个函数

		int epfd =0;
		struct conn_item connlist[1024]={0};
		int set_event(int fd,int event,int flag){
		if(flag){
	             struct epoll_event ev;
	            ev.events=event ;
	            ev.data.fd=fd;
	            epoll_ctl(epfd,EPOLL_CTL_ADD,fd,&ev);
		        }else
		        {struct epoll_event ev;
		       ev.events=event ;
		       ev.data.fd=fd;
		      epoll_ctl(epfd,EPOLL_CTL_MOD,fd,&ev);}
		      }

设置epoll实例,初始化连接结构体,设置状态改变函数

int accept_cb(int fd){
           struct sockaddr_in clientaddr;
            socklen_t len =sizeof(clientaddr);
            int clientfd=accept(fd,(struct sockaddr*)&clientaddr,&len);
            
            if(clientfd<0){return -1;}
            set_event(clientfd,EPOLLIN,1);
            connlist[clientfd].fd=clientfd;
            memset(connlist[clientfd].rbuffer,0,BUFFER_LENGTH);
             connlist[clientfd].rlen=0;
            memset(connlist[clientfd].wbuffer,0,BUFFER_LENGTH);
            connlist[clientfd].wlen=0;
            connlist[clientfd].recv_t.recv_callback=recv_cb;
            connlist[clientfd].send_callback=send_cb;
            return clientfd;
}

监听函数,功能是创造clientfd,将其设置为可读状态并接入到epoll实例中,初始化可读可写buffer,设置回调函数

int recv_cb(int fd){
               char* buffer=connlist[fd].rbuffer;
                int idx =connlist[fd].rlen;
                 int count=recv(fd,buffer+idx,BUFFER_LENGTH-idx,0);
                 if(count==0){
                  printf("disconnect\n");
                   epoll_ctl(epfd,EPOLL_CTL_DEL,fd,NULL);
                    close(fd);
                   return -1;
                    }
                    connlist[fd].rlen += count;
                    memcpy(connlist[fd].wbuffer,connlist[fd].rbuffer,connlist[fd].rlen);
                    connlist[fd].wlen=connlist[fd].rlen;
                    set_event(fd,EPOLLOUT,0);
                    return count;
                   }

接收函数:接收数据,将当前fd从epfd中去除,将rbuffer数据复制添加到wbuffer的后面(回声收发),设置fd状态为可写。

int send_cb(int fd){
          char* buffer=connlist[fd].wbuffer;
          int idx =connlist[fd].wlen;
          int count=send(fd,buffer,idx,0);
          set_event(fd,EPOLLIN,0);
          return count;
}

发送函数:将wbuffer中的数据发出,并将fd设置为可读并将其从epfd中去掉

{
       connlist[sockfd].fd=sockfd;
       connlist[sockfd].recv_t.accept_callback=accept_cb;
       epfd=epoll_create(1);
       set_event(sockfd,EPOLLIN,1);
       struct epoll_event events[1024]={0};

创建epoll实例epfd,事件结构体ev以及设置并初始化ev中的结构体事件集合,设置监听sockfd并加入到epfd中

 while(1){
     int nready= epoll_wait(epfd,events,1024,-1);
     int i=0;
     for(i=0;i<nready;i++){
        int connfd=events[i].data.fd;
           if(events[i].events & EPOLLIN){
           int count=connlist[connfd].recv_t.recv_callback(connfd);
           printf("recv count:%d<--buffer:%s\n",count,connlist[connfd].rbuffer);
           }else if(events[i].events &EPOLLOUT){
                int count=connlist[connfd].send_callback(connfd);
                 printf("send-->buffer:%s\n",connlist[connfd].wbuffer);
           }
        }
   }

开始循环,等待出现就绪事件,遍历就绪事件,如果是可读的,就接收数据,若是可写的,就写入数据,最后输出数据。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值