I/O复用函数的使用——epoll
生活随笔
收集整理的這篇文章主要介紹了
I/O复用函数的使用——epoll
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
1.epoll的接口介紹
epoll 是 Linux 特有的 I/O 復用函數。它在實現和使用上與 select、poll 有很大差異。首先,epoll 使用一組函數來完成任務,而不是單個函數。其次,epoll 把用戶關心的文件描述符上的事件放在內核里的一個事件表中,從而無需像 select 和 poll 那樣每次調用都要重復傳入文件描述符或事件集。但 epoll 需要使用一個額外的文件描述符,來唯一標識內核中的這個事件表。epoll 相關的函數如下:
? epoll_create()用于創建內核事件表
? epoll_ctl()用于操作內核事件表
? epoll_wait()用于在一段超時時間內等待一組文件描述符上的事件其各自的原型如下:
2.epoll支持的事件類型
3.epoll 的示例代碼
服務器代碼:
#define _GUN_SOURCE#include<stdio.h> #include<stdlib.h> #include<string.h> #include<unistd.h> #include<assert.h> #include<sys/socket.h> #include<arpa/inet.h> #include<sys/epoll.h>#define EPOLLSIZE 10 #define MAX_FD 128 #define DATALEN 128//初始化套接字 int InitSocket() {int sockfd = socket(AF_INET,SOCK_STREAM,0);if(sockfd == -1){return -1;}struct sockaddr_in saddr;saddr.sin_family = AF_INET;saddr.sin_port = htons(6000);saddr.sin_addr.s_addr = inet_addr("127.0.0.1");int res = bind(sockfd,(struct sockaddr*)&saddr,sizeof(saddr));if(res == -1){return -1;}res = listen(sockfd,5);if(res == -1){return -1;}return sockfd; }//處理客戶端連接 void GetClientLink(int sockfd,int epfd) {struct sockaddr_in caddr;int len = sizeof(caddr);int c = accept(sockfd,(struct sockaddr*)&caddr,&len);if(c < 0){printf("Client Link error\n");return;}else{struct epoll_event ev;ev.events = EPOLLIN | EPOLLRDHUP;ev.data.fd = c;if(epoll_ctl(epfd,EPOLL_CTL_ADD,c,&ev) == -1)//將攜帶客戶端信息的結構體ev添加到{printf("epoll_ctl add error\n");}else{printf("Client %d Link Sucess\n",c);}}}//關閉客戶端 void CloseClient(int epfd,int clifd) {struct epoll_event ev;if(epoll_ctl(epfd,EPOLL_CTL_DEL,clifd,NULL) == -1)//從內核事件表中移除clifd信息和事件{printf("epoll_ctl del error\n");}close(clifd);//服務器關閉客戶端連接,注意必須要從內核事件表中注銷客戶端事件后才能關閉//否則的話,關閉客戶端連接后,描述符就沒用了,內核事件表查不到,就沒辦法注銷事件printf("Client %d over\n",clifd); }//處理客戶端可讀信息 void DealClientData(int epfd,int clifd) {char buff[DATALEN] = {0};int n = recv(clifd,buff,DATALEN-1,0);if(n <= 0){CloseClient(epfd,clifd);return;}else{printf("%d:%s\n",clifd,buff);send(clifd,"Ok",2,0);}}void DealReadyEvent(struct epoll_event* events,int n,int sockfd,int epfd) {for(int i = 0; i < n; i++)//events攜帶就緒事件信息,總共n個{if(events[i].data.fd == sockfd)//說明有客戶端連接{GetClientLink(sockfd,epfd);}else if(events[i].events & EPOLLIN)//說明客戶端有事件,而且是可讀事件{DealClientData(epfd,events[i].data.fd);}else if(events[i].events & EPOLLOUT)//說明客戶端斷開連接{CloseClient(epfd,events[i].data.fd);}else{printf("DelReady error\n");}} }int main() {int sockfd = InitSocket();assert(sockfd != -1);//創建epoll實例,epfd為句柄,就像打開一個文件后返回文件描述符一樣int epfd = epoll_create(EPOLLSIZE);assert(epfd != -1);//從內核事件表刪除注冊事件都需要借用struct epoll_event結構體類型來當作描述符和事件的載體struct epoll_event ev;ev.data.fd = sockfd;ev.events = EPOLLIN;if(epoll_ctl(epfd,EPOLL_CTL_ADD,sockfd,&ev) == -1)//向內核事件表中注冊sockfd的事件{printf("epoll_ctl add error\n");exit(0);}while(1){//就像需要向內核事件表注冊事件一樣,這里需要struct epoll_event結構體類型獲取保存就緒事件信息struct epoll_event events[MAX_FD];int n = epoll_wait(epfd,events,MAX_FD,5000);//從內核時間表中獲取就緒事件,返回值為就緒事件個數if(n < 0)//n < 0 epoll_wait失敗{printf("epoll_wait error\n");continue;}else if(n == 0)//超過等待時間{printf("time out\n");continue;}else//有就緒事件{DealReadyEvent(events,n,sockfd,epfd);}}exit(0); }客戶端代碼在講解I/O復用函數的使用——poll博客中有,這里就不進行粘貼了。
下面是運行結果:
總結
以上是生活随笔為你收集整理的I/O复用函数的使用——epoll的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: define、const、typedef
- 下一篇: 排序算法——冒泡排序、选择排序、直接插入