epoll总结

Epoll

epoll操作由三个函数构成:头文件为 #include <sys/epoll.h>

#include <sys/epoll.h>

int epoll_create(int size);

int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);

int epoll_wait(int epfd, struct epoll_event * events, int maxevents, int timeout);

1. epoll_create函数
函数声明:int epoll_create(int size)
  创建一个epoll的句柄(文件描述符),size用来告诉内核这个epoll句柄能监听的socket fd个数。需要注意的是,当创建好epoll句柄后,它就会占用一个fd值,在linux下如果查看/proc/进程id/fd/,是能够看到这个fd的,所以在使用完epoll后,必须调用close()关闭,否则可能导致fd被耗尽。

2. epoll_ctl函数

函数声明:int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event)
该函数用于控制某个epoll文件描述符上的事件,可以注册事件,修改事件,删除事件。 
参数: 
epfd:由 epoll_create 生成的epoll专用的文件描述符; 
op:要进行的操作例如注册事件,可能的取值EPOLL_CTL_ADD 注册、EPOLL_CTL_MOD 修 改、EPOLL_CTL_DEL 删除 

fd:关联的文件描述符; 
event:指向epoll_event的指针; 

第四个参数的数据结构:

typedef union epoll_data { 
void *ptr;
int fd;
__uint32_t u32;
__uint64_t u64;
} epoll_data_t; 

struct epoll_event {
__uint32_t events;  /* Epoll events */
epoll_data_t data;  /* User data variable */
};

如:
struct epoll_event ev;
//设置与要处理的事件相关的文件描述符
ev.data.fd=listenfd;
//设置要处理的事件类型
ev.events=EPOLLIN|EPOLLET;
//注册epoll事件
epoll_ctl(epfd,EPOLL_CTL_ADD,listenfd,&ev);

常用的事件类型:
  1. EPOLLIN :表示对应的文件描述符可以读;
  2. EPOLLOUT:表示对应的文件描述符可以写;
  3. EPOLLPRI:表示对应的文件描述符有紧急的数据可读
  4. EPOLLERR:表示对应的文件描述符发生错误;
  5. EPOLLHUP:表示对应的文件描述符被挂断;
  6. EPOLLET:表示对应的文件描述符有事件发生;

3. epoll_wait函数

函数声明:int epoll_wait(int epfd, struct epoll_event * events, int maxevents, int timeout)
该函数用于轮询I/O事件的发生;
参数:
epfd:由epoll_create 生成的epoll专用的文件描述符;
epoll_event:用于回传代处理事件的数组;
maxevents:每次能处理的事件数;
timeout:等待I/O事件发生的超时值(单位ms);-1相当于阻塞,0相当于非阻塞。

return:返回在规定的时间内获取到IO数据的个数

epoll的工作模式

epoll对文件描述符的操作有两种模式:LT(level trigger)和ET(edge trigger)。LT模式是默认模式,LT模式与ET模式的区别如下:

LT模式:当epoll_wait检测到描述符事件发生并将此事件通知应用程序,应用程序可以不立即处理该事件。下次调用epoll_wait时,会再次响应应用程序并通知此事件。

ET模式:当epoll_wait检测到描述符事件发生并将此事件通知应用程序,应用程序必须立即处理该事件。如果不处理,下次调用epoll_wait时,不会再次响应应用程序并通知此事件。

ET模式在很大程度上减少了epoll事件被重复触发的次数,因此效率要比LT模式高。epoll工作在ET模式的时候,必须使用非阻塞套接口,以避免由于一个文件句柄的阻塞读/阻塞写操作把处理多个文件描述符的任务饿死。

测试代码:
服务器代码:server.cpp

  8 #include <stdio.h>
  9 #include <stdlib.h>
 10 #include <string.h>
 11 #include <netinet/in.h>
 12 #include <sys/types.h>
 13 #include <sys/socket.h>
 14 #include <unistd.h>
 15 #include <sys/epoll.h>
 16 #include <arpa/inet.h>
 17 #include <iostream>
 18 #include <netdb.h>
 19 
 20 #define MAX_DATA_SIZE 4096
 21 #define SERVER_PORT 8000
 22 #define CON_QUEUE 20
 23 #define MAX_EVENTS 10
 24 
 25 void acceptConn(int sockfd, int epollfd);
 26 void recvHandle(int clientfd);
 27 
 28 int main()
 29 {
 30    int sockfd;
 31    struct sockaddr_in servaddr;
 32 
 33    if((sockfd = ::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) == -1)
 34    {
 35      perror("创建socket错误");
 36      exit(-1);
 37    }
 38 
 39    //设置套接口为可重用状态
 40    int reuse = 1;  //0关闭 ,1 开启 
 41    if(-1 == ::setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)))
 42    {
 43       perror("不能设置套接口为可重用状态");
 44       ::close(sockfd);
 45       exit(-1);
 46    }
 47    //设置套接口发送接收缓冲,并且服务器的必须在accept之前设置
 48    socklen_t recv_size =  4 * 1024;
 49    if(-1 == ::setsockopt(sockfd, SOL_SOCKET, SO_RCVBUF, &recv_size, sizeof(recv_size)))
 50    {
 51        perror("设置套接口接收缓冲错误");
 52        ::close(sockfd);
 53        exit(-1);
 54    }
 55    if(-1 == ::setsockopt(sockfd, SOL_SOCKET, SO_SNDBUF, &recv_size, sizeof(recv_size)))
 56    {
 57        perror("设置套接口接收缓冲错误");
 58        ::close(sockfd);
 59        exit(-1);
 60    }
 61 
 62    bzero(&servaddr, sizeof(servaddr));
 63    servaddr.sin_family = AF_INET;
 64    servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
 65    servaddr.sin_port = htons(SERVER_PORT);
 66 
 67    int ret = ::bind(sockfd, (struct sockaddr*)&servaddr, sizeof(servaddr));
 68    if(-1 == ret)
 69    {
 70        perror("bind 错误");
 71       ::close(sockfd);
 72       exit(-1);
 73    }
 74    if(::listen(sockfd,CON_QUEUE) == -1)
 75    {
 76        perror("监听失败!");
 77        ::close(sockfd);
 78        exit(-1);
 79    }
 80 
 81    //epoll 初始化
 82    int epollfd; //epoll 描述符
 83    struct epoll_event eventlist[MAX_EVENTS];
 84 
 85    epollfd = epoll_create(1);
 86    struct epoll_event event;
 87    event.events = EPOLLIN|EPOLLET;
 88    event.data.fd = sockfd;
 89    if(epoll_ctl(epollfd, EPOLL_CTL_ADD, sockfd, &event) < 0)
 90    {
 91        printf("epoll 加入失败 fd:%d\n",sockfd);
 92        exit(-1);
 93    }
 94   
 95    printf("server init success!\n");
 96    while(1)
 97    {
 98       int timeout = 10000;
 99 
100       //epoll_wait处理
101       //ret会返回在规定的时间内获取到IO数据的个数,并把获取到的event保存在eventList中,注意在每次执行该函数时eventList都会清空,由epoll_wait函数填写。
102      //而不清除已经EPOLL_CTL_ADD到epollfd描述符的其他加入的文件描述符。
103     //epoll里面的文件描述符要手动通过EPOLL_CTL_DEL进行删除。
104       int ret = epoll_wait(epollfd, &eventlist[0], MAX_EVENTS, timeout);
105       if(ret < 0)
106       {
107           perror("epoll error\n");
108           break;
109       }
110      else if(ret == 0)
111      {
112          //超时
113          printf("epoll_wait 等待超时\n");
114          continue;
115      }
116      printf("ret = %d\n",ret); 
117      for(int i = 0; i < ret; ++i)
118       { 
119           if(eventlist[i].events & EPOLLERR || !(eventlist[i].events & EPOLLIN))
120           {
121               printf("epoll error");
122               ::close(eventlist[i].data.fd);
123               exit(-1);
124           }
125           
126           if(eventlist[i].data.fd == sockfd)
127           {
128              acceptConn(sockfd, epollfd);
129             // handle(eventlist[i].data.fd);
130           }
131 
132           else if(eventlist[i].events & EPOLLIN) //接收消息
133           {
134         //    acceptConn(sockfd, epollfd);
135               recvHandle(eventlist[i].data.fd);
136           }
137       } 
138 
139    }
140    ::close(epollfd);
141    ::close(sockfd);     
142    return 0;
143 }
144 
145 //接收客户端的连接,addr返回的地址,返回的客户端套接口
146 /*int accept(int sock, struct sockaddr_in *addr) 
147 { 
148     socklen_t len = sizeof(struct sockaddr_in); 
149     bzero(addr, sizeof(struct sockaddr_in)); 
150     struct epoll_event ev; 
151     int rc = epoll_wait(kdpfd, &ev, 1, 2000);
152     //这里kdpfd is the library function epoll_create( int size)的reture value.Here is called by epoll_create(1).You can man it.
153     if(1 == rc && (ev.events & EPOLLIN)) 
154         return TEMP_FAILURE_RETRY(::accept(sock, (struct sockaddr*)addr, &len)); 
155     return -1; 
156 }*/
157 
158 void acceptConn(int sockfd, int epollfd)
159 {
160    struct sockaddr_in cliaddr;
161    socklen_t len = sizeof(struct sockaddr_in);
162    bzero(&cliaddr,len);
163    int confd = ::accept(sockfd, (struct sockaddr*)&cliaddr, &len);
164    if(confd < 0)
165    {
166      printf("accept 错误\n");
167      exit(-1);
168    }
169    printf("sockfd:%d\n",sockfd);
170   	 /* else
171    	{
172      	  int v = getpeername(confd,(sockaddr*) &cliaddr,&len);
173      	  if(v == -1)
174           	 std::cout<<"获取客户端的地址和端口失败!"<<std::endl;
175      	  std::cout<<"address:"<<inet_ntoa(cliaddr.sin_addr)<<" port:"<<htons(cliaddr.sin_port)<<std::endl; 
176      	  int recvlen= 0;
177      	  char recvBuf[MAX_DATA_SIZE];
178       	 memset(recvBuf, 0, sizeof(recvBuf));
179      	  recvlen = ::recv(confd, (char*)recvBuf, MAX_DATA_SIZE, MSG_NOSIGNAL );
180     	   if(recvlen == 0)
181        	    return;
182      	  if(recvlen < 0)
183        	{
184         	   printf("server recv error\n");
185        	    exit(-1);
186       	 }
187       	 printf("接收到的数据:%s\n",recvBuf);
188 	   }*/
189         struct epoll_event event;
190         event.data.fd=confd;
191         event.events=EPOLLIN;
192         epoll_ctl(epollfd,EPOLL_CTL_ADD,confd,&event);
193 }
194 
195 void recvHandle(int clientfd)
196 {
197     struct sockaddr_in cliaddr;
198     socklen_t len = sizeof(struct sockaddr_in);
199     bzero(&cliaddr,len);
200 
201     int v = getpeername(clientfd,(sockaddr*) &cliaddr,&len);
202     if(v == -1)
203         std::cout<<"获取客户端的地址和端口失败!"<<std::endl;
204     std::cout<<"address:"<<inet_ntoa(cliaddr.sin_addr)<<" port:"<<htons(cliaddr.sin_port)<<std::endl; 
205 
206     int recvlen= 0;
207     char recvBuf[MAX_DATA_SIZE];
208     memset(recvBuf, 0, sizeof(recvBuf));
209     recvlen = ::recv(clientfd, (char*)recvBuf, MAX_DATA_SIZE, MSG_NOSIGNAL );   
210     if(recvlen == 0)
211         return;
212     if(recvlen < 0)
213     {
214         printf("server recv error\n");
215         exit(-1);
216     }
217     printf("接收到来自clientfd:%d的数据:%s\n", clientfd,recvBuf);
218     return;
219 }

客户端代码:client.cpp

  9 #include <stdio.h>
 10 #include <stdlib.h>
 11 #include <string.h>
 12 #include <netinet/in.h>
 13 #include <sys/types.h>
 14 #include <sys/socket.h>
 15 #include <unistd.h>
 16 #include <netdb.h>
 17 #include <error.h>
 18 #include <arpa/inet.h>
 19 #include <iostream>
 20 
 21 #define MAX_DATA_SIZE 4096
 22 #define SERVER_PORT 8000
 23 
 24 int main()
 25 {
 26    int sockfd;
 27    struct sockaddr_in servaddr;
 28    int pid;
 29    char sendBuf[MAX_DATA_SIZE],revBuf[MAX_DATA_SIZE];
 30    int send_size,recv_size;
 31 
 32    sockfd = ::socket(AF_INET,SOCK_STREAM, IPPROTO_TCP);
 33    bzero(&servaddr,sizeof(servaddr));
 34    servaddr.sin_family = AF_INET;
 35    servaddr.sin_port = htons(SERVER_PORT);
 36    servaddr.sin_addr.s_addr = inet_addr("192.168.97.246");
 37 
 38    if(::connect(sockfd,(struct sockaddr*)&servaddr,sizeof(struct sockaddr_in)) == -1)
 39    {
 40       perror("connect 失败,请检查端口和ip地址!");
 41       exit(-1);
 42    }
 43 
 44    while(1)
 45    {
 46        printf("请输入:");
 47        fgets(sendBuf,MAX_DATA_SIZE,stdin);
 48        send_size = ::send(sockfd, sendBuf, MAX_DATA_SIZE, MSG_NOSIGNAL);
 49        if(send_size < 0)
 50        {
 51            perror("send error");
 52        }
 53        memset(sendBuf, 0, sizeof(sendBuf));
 54    }
 55    return 0;
 56 }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值