本文主要是介绍服务器epoll初用,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
epoll是Linux下多路复用IO接口select/poll的增强版本,它能显著提高程序在大量并发连接中只有少量活跃的情况下的系统CPU利用率,因为它会复用文件描述符集合来传递结果而不用迫使开发者每次等待事件之前都必须重新准备要被侦听的文件描述符集合,另一点原因就是获取事件的时候,它无须遍历整个被侦听的描述符集,只要遍历那些被内核IO事件异步唤醒而加入Ready队列的描述符集合就行了。
1 | nfds=epoll_wait(kdpfd,events,maxevents,-1); |
epoll的事件注册函数,它不同与select()是在监听事件时告诉内核要监听什么类型的事件,而是在这里先注册要监听的事件类型。第一个参数是epoll_create()的返回值,第二个参数表示动作,用三个宏来表示:
EPOLL_CTL_ADD:注册新的fd到epfd中;
EPOLL_CTL_MOD:修改已经注册的fd的监听事件;
EPOLL_CTL_DEL:从epfd中删除一个fd;
第三个参数是需要监听的fd,第四个参数是告诉内核需要监听什么事,struct epoll_event结构如下:
struct epoll_event {
__uint32_t events; /* Epoll events */
epoll_data_t data; /* User data variable */
};
events可以是以下几个宏的集合:
EPOLLIN :表示对应的文件描述符可以读(包括对端SOCKET正常关闭);
EPOLLOUT:表示对应的文件描述符可以写;
EPOLLPRI:表示对应的文件描述符有紧急的数据可读(这里应该表示有带外数据到来);
EPOLLERR:表示对应的文件描述符发生错误;
EPOLLHUP:表示对应的文件描述符被挂断;
EPOLLET: 将EPOLL设为边缘触发(Edge Triggered)模式,这是相对于水平触发(Level Triggered)来说的。
EPOLLONESHOT:只监听一次事件,当监听完这次事件之后,如果还需要继续监听这个socket的话,需要再次把这个socket加入到EPOLL队列里
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/epoll.h>#define MAXLINE 80
#define SERV_PORT 8000
int main(void)
{struct sockaddr_in servaddr,cliaddr;struct epoll_event ev,event[20];//定义epoll事件结构体socklen_t cliaddr_len;int listenfd,connfd,epollfd,nfds,newfd;char buf[MAXLINE];char str[INET_ADDRSTRLEN];int i,n,on=1,j;listenfd=socket(AF_INET,SOCK_STREAM,0);epollfd=epoll_create(1024);//创建一个epoll句柄if(epollfd==-1){printf("epoll_create error...\n");exit(0);}ev.data.fd=listenfd;ev.events=EPOLLIN;//对应文件描述符可以读if(epoll_ctl(epollfd,EPOLL_CTL_ADD,listenfd,&ev)==-1){printf("epoll_ctl error...\n");exit(0);}bzero(&servaddr,sizeof(servaddr));servaddr.sin_family=AF_INET;servaddr.sin_addr.s_addr=htonl(INADDR_ANY);servaddr.sin_port=htons(SERV_PORT);setsockopt(listenfd,SOL_SOCKET,SO_REUSEADDR,&on,sizeof(on));if(bind(listenfd,(struct sockaddr *)&servaddr,sizeof(servaddr))!=0){printf("bind error...\n");exit(0);}listen(listenfd,20);printf("accept connections...\n");while(1){nfds=epoll_wait(epollfd,event,20,-1);printf("nfds=%d\n",nfds);if(nfds==-1){printf("epoll_wait failed...\n");exit(0);}for(j=0;j<nfds;j++){newfd=event[j].data.fd;if(newfd==listenfd){//如果是主socket的事件的话,则表示有新连接进入了,进行新连接的处理。cliaddr_len=sizeof(cliaddr);connfd=accept(listenfd,(struct sockaddr *)&cliaddr,&cliaddr_len);printf("received from %s at PORT %d\n",inet_ntop(AF_INET,&cliaddr.sin_addr,str,sizeof(str)),ntohs(cliaddr.sin_port));ev.data.fd=connfd;epoll_ctl(epollfd,EPOLL_CTL_ADD,connfd,&ev);//设置好event之后,将这个新的event通过epoll_ctl加入到epoll的监听队列里面,这里用EPOLL_CTL_ADD来加一个新的epoll事件,通过EPOLL_CTL_DEL来减少一个过EPOLL_CTL_MOD来改变一个事件的监听方式。}else{n=read(newfd,buf,MAXLINE);if(n<=0){close(newfd);}for(i=0;i<n;i++)buf[i]=toupper(buf[i]); write(connfd,buf,n);}}}close(epollfd);//关闭epollfdreturn 0;
}
这篇关于服务器epoll初用的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!