日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

I/O复用函数的使用——epoll

發布時間:2024/4/17 编程问答 40 豆豆
生活随笔 收集整理的這篇文章主要介紹了 I/O复用函数的使用——epoll 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

1.epoll的接口介紹

epoll 是 Linux 特有的 I/O 復用函數。它在實現和使用上與 select、poll 有很大差異。首先,epoll 使用一組函數來完成任務,而不是單個函數。其次,epoll 把用戶關心的文件描述符上的事件放在內核里的一個事件表中,從而無需像 select 和 poll 那樣每次調用都要重復傳入文件描述符或事件集。但 epoll 需要使用一個額外的文件描述符,來唯一標識內核中的這個事件表。epoll 相關的函數如下:
? epoll_create()用于創建內核事件表
? epoll_ctl()用于操作內核事件表
? epoll_wait()用于在一段超時時間內等待一組文件描述符上的事件其各自的原型如下:

#include <sys/epoll.h>int epoll_create( int size); /* epoll_create()成功返回內核事件表的文件描述符,失敗返回-1 size 參數現在并不起作用,只是給內核一個提示,告訴它事件表需要多大。 */int epoll_ctl( int epfd, int op, int fd, struct epoll_event *event); /* epoll_ctl()成功返回 0,失敗返回-1 epfd 參數指定要操作的內核事件表的文件描述符 fd 參數指定要操作的文件描述符 op 參數指定操作類型: EPOLL_CTL_ADD 往內核事件表中注冊 fd 上的事件 EPOLL_CTL_MOD 修改 fd 上的注冊事件 EPOLL_CTL_DEL 刪除 fd 上的注冊事件 event 參數指定事件,它是 epoll_event 結構指針類型, epoll_event 的定義如下: struct epoll_event {_uint32_t events; // epoll 事件epoll_data_t data; // 用戶數據 }; 其中,events 成員描述事件類型,epoll 支持的事件類型與 poll 基本相同,表示epoll 事件的宏是在 poll 對應的宏前加上‘E’,比如 epoll 的數據可讀事件是EPOLLIN。但是 epoll 有兩個額外的事件類型--EPOLLET 和 EPOLLONESHOT。 data 成員用于存儲用戶數據,是一個聯合體,其定義如下: typedef union epoll_data {void *ptr;int fd;uint32_t u32;uint64_t u64; }epoll_data_t; 其中 fd 成員使用的最多,它指定事件所從屬的目標文件描述符。 */int epoll_wait( int epfd, struct epoll_event *events, int maxevents, int timeout); /* epoll_wait()成功返回就緒的文件描述符的個數,失敗返回-1,超時返回 0 epfd 參數指定要操作的內核事件表的文件描述符 events 參數是一個用戶數組,這個數組僅僅在 epoll_wait 返回時保存內核檢測到的所有就緒事件,而不像 select 和 poll 的數組參數那樣既用于傳入用戶注冊的事件,又用于輸出內核檢測到的就緒事件。這就極大地提高了應用程序索引就緒文件描述符的效率。 maxevents 參數指定用戶數組的大小,即指定最多監聽多少個事件,它必須大于0 timeout 參數指定超時時間,單位為毫秒,如果 timeout 為 0,則 epoll_wait 會立即返回,如果 timeout 為-1,則 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的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。