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

歡迎訪問 生活随笔!

生活随笔

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

编程问答

Epoll例子的使用

發布時間:2023/12/20 编程问答 31 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Epoll例子的使用 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

第一部分:Epoll簡介

問題?: ?Select,Poll和Epoll的區別

答案?:?

Epoll和Select的區別



1.? 遍歷方式的區別 。select判斷是否有事件發生是遍歷的,而epoll是事件響應的,一旦句柄上有事件來了,就馬上選出來。

2.?數目的區別。select一般由一個內核參數(1024)限制了監聽的句柄數,但是epoll通常受限于打開文件的數目,通常會打得多。

3.?epoll自身,還有兩種觸發方式。水平觸發和邊緣觸發。邊沿觸發的效率更高(高了不少,但是編程的時候要小心處理每個時間,防止漏掉處理某些事件)。


Select


select()系統調用提供一個機制來實現同步多元I/O:


#include?<sys/time.h>
#include?<sys/types.h>
#include?<unistd.h>

int?select?(int?n,?fd_set?*readfds,?fd_set?*writefds,?fd_set?*exceptfds,?struct?timeval?*timeout);

FD_CLR(int?fd,?fd_set?*set);
FD_ISSET(int?fd,?fd_set?*set);
FD_SET(int?fd,?fd_set?*set);
FD_ZERO(fd_set?*set);


調用select()將阻塞,直到指定的文件描述符準備好執行I/O,或者可選參數timeout指定的時間已經過去。

select()成功返回時,每組set都被修改以使它只包含準備好I/O的文件描述符。例如,假設有兩個文件描述符,值分別是7和9,被放在readfds中。當select()返回時,如果7仍然在set中,則這個文件描述符已經準備好被讀取而不會阻塞。如果9已經不在set中,則讀取它將可能會阻塞(我說可能是因為數據可能正好在select返回后就可用,這種情況下,下一次調用select()將返回文件描述符準備好讀取)。

第一個參數n,等于所有set中最大的那個文件描述符的值1
select()返回時,timeout參數的狀態在不同的系統中是未定義的,因此每次調用select()之前必須重新初始化timeout和文件描述符set。實際上,當前版本的Linux會自動修改timeout參數,設置它的值為剩余時間。因此,如果timeout被設置為5秒,然后在文件描述符準備好之前經過了3秒,則這一次調用select()返回時tv_sec將變為2
因為文件描述符set是靜態創建的,它們對文件描述符的最大數目強加了一個限制,能夠放進set中的最大文件描述符的值由FD_SETSIZE指定。在Linux中,這個值是1024。本章后面我們還將看到這個限制的衍生物。
返回值和錯誤代碼
select()?成功時返回準備好I/O的文件描述符數目,包括所有三個set。如果提供了timeout,返回值可能是0;錯誤時返回-1,并且設置errno為下面幾個值之一:
EBADF:?給某個set提供了無效文件描述符。
EINTR::等待時捕獲到信號,可以重新發起調用。
EINVAL::參數n為負數,或者指定的timeout非法。
ENOMEM::不夠可用內存來完成請求。


Poll


select()不一樣,poll()沒有使用低效的三個基于位的文件描述符set,而是采用了一個單獨的結構體pollfd數組,由fds指針指向這個組pollfd結構體定義如下:

#include?<sys/poll.h>

int?poll?(struct?pollfd?*fds,?unsigned?int?nfds,?int?timeout);
struct?pollfd?{
????int?fd;?/*?file?descriptor?*/
????short?events;?/*?requested?events?to?watch?*/
????short?revents;?/*?returned?events?witnessed?*/
};

每一個pollfd結構體指定了一個被監視的文件描述符,可以傳遞多個結構體,指示poll()監視多個文件描述符。每個結構體的events域是監視該文件描述符的事件掩碼,由用戶來設置這個域。revents域是文件描述符的操作結果事件掩碼。內核在調用返回時設置這個域。events域中請求的任何事件都可能在revents域中返回。合法的事件如下:

POLLIN:有數據可讀。
POLLRDNORM:有普通數據可讀。
POLLRDBAND:有優先數據可讀。
POLLPRI:有緊迫數據可讀。
POLLOUT:寫數據不會導致阻塞。
POLLWRNORM:寫普通數據不會導致阻塞。
POLLWRBAND:寫優先數據不會導致阻塞。
POLLMSG:SIGPOLL消息可用。

此外,revents域中還可能返回下列事件:
POLLER:指定的文件描述符發生錯誤。
POLLHUP:指定的文件描述符掛起事件。
POLLNVAL:指定的文件描述符非法。

這些事件在events域中無意義,因為它們在合適的時候總是會從revents中返回。使用poll()select()不一樣,你不需要顯式地請求異常情況報告。


POLLIN | POLLPRI等價于select()的讀事件,POLLOUT | POLLWRBAND等價于select()的寫事件。POLLIN等價于POLLRDNORM | POLLRDBAND,而POLLOUT則等價于POLLWRNORM
例如,要同時監視一個文件描述符是否可讀和可寫,我們可以設置eventsPOLLIN | POLLOUT。在poll返回時,我們可以檢查revents中的標志,對應于文件描述符請求的events結構體。如果POLLIN事件被設置,則文件描述符可以被讀取而不阻塞。如果POLLOUT被設置,則文件描述符可以寫入而不導致阻塞。這些標志并不是互斥的:它們可能被同時設置,表示這個文件描述符的讀取和寫入操作都會正常返回而不阻塞。
timeout參數指定等待的毫秒數,無論I/O是否準備好,poll都會返回。timeout指定為負數值表示無限超時;timeout0指示poll調用立即返回并列出準備好I/O的文件描述符,但并不等待其它的事件。這種情況下,poll()就像它的名字那樣,一旦選舉出來,立即返回。
返回值和錯誤代碼
成功時,poll()返回結構體中revents域不為0的文件描述符個數;如果在超時前沒有任何事件發生,poll()返回0;失敗時,poll()返回-1,并設置errno為下列值之一:
EBADF:一個或多個結構體中指定的文件描述符無效。
EFAULT:fds指針指向的地址超出進程的地址空間。
EINTR:請求的事件之前產生一個信號,調用可以重新發起。
EINVAL:nfds參數超出PLIMIT_NOFILE值。
ENOMEM:可用內存不足,無法完成請求。



Epoll


Epoll的優點:
1.支持一個進程打開大數目的socket描述符(FD)
??? select?最不能忍受的是一個進程所打開的FD是有一定限制的,由FD_SETSIZE設置,默認值是2048。對于那些需要支持的上萬連接數目的IM服務器來說顯然太少了。這時候你一是可以選擇修改這個宏然后重新編譯內核,不過資料也同時指出這樣會帶來網絡效率的下降,二是可以選擇多進程的解決方案(傳統的?Apache方案),不過雖然Linux上面創建進程的代價比較小,但仍舊是不可忽視的,加上進程間數據同步遠比不上線程間同步的高效,所以也不是一種完美的方案。不過?epoll則沒有這個限制,它所支持的FD上限是最大可以打開文件的數目,這個數字一般遠大于2048,舉個例子,1GB內存的機器上大約是10萬左右,具體數目可以cat /proc/sys/fs/file-max察看,一般來說這個數目和系統內存關系很大。

2.IO效率不隨FD數目增加而線性下降
????傳統的select/poll另一個致命弱點就是當你擁有一個很大的socket集合,不過由于網絡延時,任一時間只有部分的socket"活躍"的,但是select/poll每次調用都會線性掃描全部的集合,導致效率呈現線性下降。但是epoll不存在這個問題,它只會對"活躍"socket進行操作---這是因為在內核實現中epoll是根據每個fd上面的callback函數實現的。那么,只有"活躍"socket才會主動的去調用?callback函數,其他idle狀態socket則不會,在這點上,epoll實現了一個""AIO,因為這時候推動力在os內核。在一些?benchmark中,如果所有的socket基本上都是活躍的---比如一個高速LAN環境,epoll并不比select/poll有什么效率,相反,如果過多使用epoll_ctl,效率相比還有稍微的下降。但是一旦使用idle connections模擬WAN環境,epoll的效率就遠在select/poll之上了。

3.使用mmap加速內核與用戶空間的消息傳遞。
????這點實際上涉及到epoll的具體實現了。無論是select,poll還是epoll都需要內核把FD消息通知給用戶空間,如何避免不必要的內存拷貝就很重要,在這點上,epoll是通過內核于用戶空間mmap同一塊內存實現的。而如果你想我一樣從2.5內核就關注epoll的話,一定不會忘記手工?mmap這一步的。


Epoll簡介:

linux的網絡編程中,很長的時間都在使用select來做事件觸發。在linux新的內核中,有了一種替換它的機制,就是epoll
相比于selectepoll最大的好處在于它不會隨著監聽fd數目的增長而降低效率。因為在內核中的select實現中,它是采用輪詢來處理的,輪詢的fd數目越多,自然耗時越多。

epoll的接口非常簡單,一共就三個函數:
1. int epoll_create(int size);
創建一個epoll的句柄,size用來告訴內核這個監聽的數目一共有多大。這個參數不同于select()中的第一個參數,給出最大監聽的fd+1的值。需要注意的是,當創建好epoll句柄后,它就是會占用一個fd值,在linux下如果查看/proc/進程id/fd/,是能夠看到這個fd的,所以在使用完epoll后,必須調用close()關閉,否則可能導致fd被耗盡。


2. int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
epoll的事件注冊函數,它不同與select()是在監聽事件時告訴內核要監聽什么類型的事件,而是在這里先注冊要監聽的事件類型。第一個參數是epoll_create()[上面一個函數]的返回值,第二個參數表示動作,用三個宏來表示:
EPOLL_CTL_ADD:注冊新的fdepfd中;
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隊列里


3. int epoll_wait(int epfd, struct epoll_event * events, int maxevents, int timeout);
等待事件的產生,類似于select()調用。參數events用來從內核得到事件的集合,maxevents告之內核這個events有多大,這個maxevents的值不能大于創建epoll_create()時的size,參數timeout是超時時間(毫秒,0會立即返回,-1將不確定,也有說法說是永久阻塞)。該函數返回需要處理的事件數目,如返回0表示已超時。


令人高興的是,2.6內核的epoll比其2.5開發版本的/dev/epoll簡潔了許多,所以,大部分情況下,強大的東西往往是簡單的。唯一有點麻煩是epoll有2種工作方式:
LT和ET(水平觸發和邊緣觸發)

LT(level triggered)是缺省的工作方式,并且同時支持block和no-block socket.在這種做法中,內核告訴你一個文件描述符是否就緒了,然后你可以對這個就緒的fd進行IO操作。如果你不作任何操作,內核還是會繼續通知你的,所以,這種模式編程出錯誤可能性要小一點。傳統的select/poll都是這種模型的代表。 ET (edge-triggered)是高速工作方式,只支持no-block socket。在這種模式下,當描述符從未就緒變為就緒時,內核通過epoll告訴你。然后它會假設你知道文件描述符已經就緒,并且不會再為那個文件描述符發送更多的就緒通知,直到你做了某些操作導致那個文件描述符不再為就緒狀態了(比如,你在發送,接收或者接收請求,或者發送接收的數據少于一定量時導致了一個EWOULDBLOCK 錯誤)。但是請注意,如果一直不對這個fd作IO操作(從而導致它再次變成未就緒),內核不會發送更多的通知(only once),不過在TCP協議中,ET模式的加速效用仍需要更多的benchmark確認。

第二部分:Epoll的三個例子

epoll用到的所有函數都是在頭文件sys/epoll.h中聲明的,下面簡要說明所用到的數據結構和函數:
所用到的數據結構


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?*/
};
* This source code was highlighted by?YcdoiT. ( style: Vs )

結構體epoll_event 被用于注冊所感興趣的事件和回傳所發生待處理的事件,其中epoll_data 聯合體用來保存觸發事件的某個文件描述符相關的數據,例如一個client連接到服務器,服務器通過調用accept函數可以得到于這個client對應的socket文件描述符,可以把這文件描述符賦給epoll_data的fd字段以便后面的讀寫操作在這個文件描述符上進行。epoll_event 結構體的events字段是表示感興趣的事件和被觸發的事件可能的取值為:EPOLLIN :表示對應的文件描述符可以讀; EPOLLOUT:表示對應的文件描述符可以寫; EPOLLPRI:表示對應的文件描述符有緊急的數據可讀(這里應該表示有帶外數據到來); EPOLLERR:表示對應的文件描述符發生錯誤; EPOLLHUP:表示對應的文件描述符被掛斷; EPOLLET:表示對應的文件描述符有事件發生; 所用到的函數: 1、epoll_create函數 函數聲明:int epoll_create(int size)? 該函數生成一個epoll專用的文件描述符,其中的參數是指定生成描述符的最大范圍(我覺得這個參數和select函數的第一個參數應該是類似的但是該怎么設置才好,我也不太清楚)。 2、epoll_ctl函數 函數聲明:int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event) 該函數用于控制某個文件描述符上的事件,可以注冊事件,修改事件,刪除事件。 參數:epfd:由 epoll_create 生成的epoll專用的文件描述符; op:要進行的操作例如注冊事件,可能的取值: EPOLL_CTL_ADD 注冊; EPOLL_CTL_MOD 修改; EPOLL_CTL_DEL 刪除 fd:關聯的文件描述符; event:指向epoll_event的指針; 如果調用成功返回0,不成功返回-1 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事件發生的超時值; 返回發生事件數。
例子1

#include?<iostream>
#include?<sys/socket.h>
#include?<sys/epoll.h>
#include?<netinet/in.h>
#include?<arpa/inet.h>
#include?<fcntl.h>
#include?<unistd.h>
#include?<stdio.h>

#define?MAXLINE?10
#define?OPEN_MAX?100
#define?LISTENQ?20
#define?SERV_PORT?5555
#define?INFTIM?1000

void?setnonblocking(int?sock)
{
????int?opts;
????opts?=?fcntl(sock,?F_GETFL);
????if(opts?<?0)
????{
????????perror("fcntl(sock,GETFL)");
????????exit(1);
????}
????opts?=?opts?|?O_NONBLOCK;
????if(fcntl(sock,?F_SETFL,?opts)?<?0)
????{
????????perror("fcntl(sock,SETFL,opts)");
????????exit(1);
????}
}

int?main()
{
????int?i,?maxi,?listenfd,?connfd,?sockfd,?epfd,?nfds;
????ssize_t?n;
????char?line[MAXLINE];
????socklen_t?clilen;
????//聲明epoll_event結構體的變量,ev用于注冊事件,數組用于回傳要處理的事件
????struct?epoll_event?ev,?events[20];
????//生成用于處理accept的epoll專用的文件描述符
????epfd?=?epoll_create(256);

????struct?sockaddr_in?clientaddr;
????struct?sockaddr_in?serveraddr;
????listenfd?=?socket(AF_INET,?SOCK_STREAM,?0);
????//把socket設置為非阻塞方式
????setnonblocking(listenfd);
????//設置與要處理的事件相關的文件描述符
????ev.data.fd?=?listenfd;
????//設置要處理的事件類型
????ev.events?=?EPOLLIN?|?EPOLLET;
????//注冊epoll事件
????epoll_ctl(epfd,?EPOLL_CTL_ADD,?listenfd,?&ev);

????bzero(&serveraddr,?sizeof(serveraddr));
????serveraddr.sin_family?=?AF_INET;

????char?*local_addr?=?"200.200.200.204";
????inet_aton(local_addr,?&(serveraddr.sin_addr));?//htons(SERV_PORT);
????serveraddr.sin_port?=?htons(SERV_PORT);
????bind(listenfd,?(sockaddr?*)&serveraddr,?sizeof(serveraddr));
????listen(listenfd,?LISTENQ);

????maxi?=?0;
????for?(?;?;?)
????{
????????//等待epoll事件的發生
????????nfds?=?epoll_wait(epfd,?events,?20,?500);
????????//處理所發生的所有事件
????????for(i?=?0;?i?<?nfds;?++i)
????????{
????????????if(events[i].data.fd?==?listenfd)
????????????{

????????????????connfd?=?accept(listenfd,?(sockaddr?*)&clientaddr,?&clilen);
????????????????if(connfd?<?0)
????????????????{
????????????????????perror("connfd<0");
????????????????????exit(1);
????????????????}
????????????????setnonblocking(connfd);

????????????????char?*str?=?inet_ntoa(clientaddr.sin_addr);
????????????????std::cout?<<?"connect?from?"?<?_u115???tr?<<?std::endl;
????????????????//設置用于讀操作的文件描述符
????????????????ev.data.fd?=?connfd;
????????????????//設置用于注測的讀操作事件
????????????????ev.events?=?EPOLLIN?|?EPOLLET;
????????????????//注冊ev
????????????????epoll_ctl(epfd,?EPOLL_CTL_ADD,?connfd,?&ev);
????????????}
????????????else?if(events[i].events?&?EPOLLIN)
????????????{
????????????????if?(?(sockfd?=?events[i].data.fd)?<?0)?continue;
????????????????if?(?(n?=?read(sockfd,?line,?MAXLINE))?<?0)
????????????????{
????????????????????if?(errno?==?ECONNRESET)
????????????????????{

????????????????????????close(sockfd);
????????????????????????events[i].data.fd?=?-1;
????????????????????}
????????????????????else
????????????????????????std::cout?<<?"readline?error"?<<?std::endl;
????????????????}
????????????????else?if?(n?==?0)
????????????????{
????????????????????close(sockfd);
????????????????????events[i].data.fd?=?-1;
????????????????}
????????????????//設置用于寫操作的文件描述符
????????????????ev.data.fd?=?sockfd;
????????????????//設置用于注測的寫操作事件
????????????????ev.events?=?EPOLLOUT?|?EPOLLET;
????????????????//修改sockfd上要處理的事件為EPOLLOUT
????????????????epoll_ctl(epfd,?EPOLL_CTL_MOD,?sockfd,?&ev);
????????????}
????????????else?if(events[i].events?&?EPOLLOUT)
????????????{
????????????????sockfd?=?events[i].data.fd;
????????????????write(sockfd,?line,?n);
????????????????//設置用于讀操作的文件描述符
????????????????ev.data.fd?=?sockfd;
????????????????//設置用于注測的讀操作事件
????????????????ev.events?=?EPOLLIN?|?EPOLLET;
????????????????//修改sockfd上要處理的事件為EPOLIN
????????????????epoll_ctl(epfd,?EPOLL_CTL_MOD,?sockfd,?&ev);
????????????}

????????}

????}
}
* This source code was highlighted by?YcdoiT. ( style: Vs )



例子2


/*

*\?服務器端的源代碼

*/

#include?<netinet/in.h>
#include?<sys/types.h>
#include?<sys/socket.h>
#include?<fcntl.h>
#include?<iostream>
#include?<signal.h>
#include?<sys/epoll.h>

#define?MAXFDS?256
#define?EVENTS?100
#define?PORT?8888

int?epfd;
bool?setNonBlock(int?fd)
{
????int?flags?=?fcntl(fd,?F_GETFL,?0);
????flags?|=?O_NONBLOCK;
????if(-1?==?fcntl(fd,?F_SETFL,?flags))
????????return?false;
????return?true;
}

int?main(int?argc,?char?*argv[],?char?*evp[])
{
????int?fd,?nfds,?confd;
????int?on?=?1;
????char?*buffer[512];
????struct?sockaddr_in?saddr,?caddr;
????struct?epoll_event?ev,?events[EVENTS];

????if(-1?==?socket(AF_INET,?SOCKSTREAM),?0)
????{
????????std::cout?<<?"創建套接字出錯啦"?<<?std::endl;
????????return?-1;
????}


????struct?sigaction?sig;
????sigemptyset(&sig.sa_mask);
????sig_handler?=?SIG_IGN;
????sigaction(SIGPIPE,?&N?>?sig,?NULL);

????epfd?=?epoll_create(MAXFDS);

????setsockopt(fd,?SOL_SOCKET,?SO_REUSEADDR,?&on,?sizeof(on));

????memset(&saddr,?0,?sizeof(saddr));
????saddr.sin_family?=?AF_INET;
????saddr.sin_port?=?htons((short)(PORT));
????saddr.sin_addr.s_addr?=?INADDR_ANY;
????if(-1?==?bind(fd,?(struct?sockaddr?*)&saddr,?sizeof(saddr)))
????{
????????std::cout?<<?"套接字不能綁定到服務器上"?<<?std::endl;
????????return?-1;
????}

????if(-1?==?listen(fd,?32))
????{
????????std::cout?<<?"監聽套接字的時候出錯了"?<<?std::endl;
????????return?-1;
????}

????ev.data.fd?=?fd;
????ev.events?=?EPOLLIN;
????epoll_ctl(epfd,?EPOLL_CTL_ADD,?fd,?&ev);

????while(true)
????{
????????nfds?=?epoll_wait(epfd,?&events,?MAXFDS,?0);

????????for(int?i?=?0;?i?<?nfds;?++?i)
????????{
????????????if(fd?==?events[i].data.fd)
????????????{
????????????????memset(&caddr,?sizeof(caddr));
????????????????cfd?=?accept(fd,?(struct?sockaddr?*)&caddr,?&sizeof(caddr));
????????????????if(-1?==?cfd)
????????????????{
????????????????????std::cout?<<?"服務器接收套接字的時候出問題了"?<<?std::endl;
????????????????????break;
????????????????}

????????????????setNonBlock(cfd);

????????????????ev.data.fd?=?cfd;
????????????????ev.events?=?EPOLLIN;
????????????????epoll_ctl(epfd,?EPOLL_CTL_ADD,?cfd,?&ev);
????????????}
????????????else?if(events[i].data.fd?&?EPOLLIN)
????????????{
????????????????bzero(&buffer,?sizeof(buffer));
????????????????std::cout?<<?"服務器端要讀取客戶端發過來的消息"?<<?std::endl;
????????????????ret?=?recv(events[i].data.fd,?buffer,?sizeof(buffer),?0);
????????????????if(ret?<?0)
????????????????{
????????????????????std::cout?<<?"服務器收到的消息出錯了"?<<?endl;
????????????????????return?-1;
????????????????}
????????????????std::cout?<<?"接收到的消息為:"?<<?(char?*)?buffer?<<?std::endl;
????????????????ev.data.fd?=?events[i].data.fd;
????????????????ev.events?=?EPOLLOUT;
????????????????epoll_ctl(epfd,?EPOLL_CTL_MOD,?events[i].data.fd,?&ev);
????????????}
????????????else?if(events[i].data.fd?&?EPOLLOUT)
????????????{
????????????????bzero(&buffer,?sizeof(buffer));
????????????????bcopy("The?Author@:?magicminglee@Hotmail.com",?buffer,?sizeof("The?Author@:?magicminglee@Hotmail.com"));
????????????????ret?=?send(events[i].data.fd,?buffer,?strlen(buffer));
????????????????if(ret?<?0)
????????????????{
????????????????????std::cout?<<?"服務器發送消息給客戶端的時候出錯啦"?<<?std::endl;
????????????????????return?-1;
????????????????}
????????????????ev.data.fd?=?events[i].data.fd;
????????????????epoll_ctl(epfd,?EPOLL_CTL_DEL,?ev.data.fd,?&ev);
????????????}
????????}
????}
????if(fd?>?0)
????{
????????shutdown(fd,?SHUT_RDWR);
????????close(fd);
????}
}
* This source code was highlighted by?YcdoiT. ( style: Vs )



/*

*\?客戶端源代碼

*/
#include?<iostream>
#include?<netinet/in.h>
#include?<sys/types.h>
#include?<sys/socket.h>

#define?PORT?8888

int?main(int?argc,?char?*argv[],?char?*evp[])
{
????int?fd;
????int?on?=?1;
????char?*buffer[512];

????struct?sockaddr_in?seraddr;
????memset(&seraddr,?0,?sizeof(seraddr));

????if((fd?=?socket(AF_INET,?SOCK_STREAM,?0))?<?0)
????{
????????std::cout?<<?"客戶端創建套接字出錯了"?<<?std::endl;
????????return?-1;
????}

????//如果用于多次測試,那么打開下面debug選項

#ifdef?_Debug_ming
????setsockopt(fd,?SOL_SOCKET,?SO_REUSEADDR,?&on,?sizeof(on));
#endif

????seraddr.sin_port?=?htons((short)(PORT));
????seraddr.sin_family?=?AF_INET;
????seraddr.sin_addr.s_addr?=?inet_addr("127.0.0.1");//設置自己的ip吧

????//你也可以采用無阻塞連接,不過需要對連接的錯誤結果進行分析處理

????if(TEMP_FAILURE_RETRY(connect(fd,?(struct?sockaddr?*)&seraddr,?sizeof(seraddr))?<?0))
????{
????????std::cout?<<?"連接錯誤了"?<<?std::endl;
????????return?-1;
????}
????//下面就進行收發信息

????bcopy("The?Author@:?magicminglee@Hotmail.com");
????send(fd,?buffer,?strlen(buffer),?0);
????bzero(&buffer,?sizeof(buffer));
????recv(fd,?buffer,?sizeof(buffer),?0);
????exit(0);
}
* This source code was highlighted by?YcdoiT. ( style: Vs )

例子3

一個使用epoll的服務器

#include?<iostream>
#include?<sys/socket.h>
#include?<sys/epoll.h>
#include?<netinet/in.h>
#include?<arpa/inet.h>
#include?<fcntl.h>
#include?<unistd.h>
#include?<stdio.h>
#include?<errno.h>
#include?<stdlib.h>
#include?<string.h>
#include?<pthread.h>

#define?MAXLINE?1024
#define?OPEN_MAX?100
#define?LISTENQ?20
#define?SERV_PORT?5555
#define?INFTIM?1000

//線程池任務隊列結構體
struct?task
{
????int?fd;????????????//需要讀寫的文件描述符
????struct?task?*next;?//下一個任務
};
//用于保存向客戶端發送一次消息所需的相關數據
struct?user_data
{
????int?fd;
????unsigned?int?n_size;
????char?line[MAXLINE];
};
//線程的任務函數
void?*readtask(void?*args);
void?*writetask(void?*args);
//聲明epoll_event結構體的變量,ev用于注冊事件,數組用于回傳要處理的事件
struct?epoll_event?ev,?events[20];
int?epfd;
pthread_mutex_t?mutex;
pthread_cond_t?cond1;
struct?task?*readhead?=?NULL,?*readtail?=?NULL,?*writehead?=?NULL;
void?setnonblocking(int?sock)
{
????int?opts;
????opts?=?fcntl(sock,?F_GETFL);
????if(opts?<?0)
????{
????????perror("fcntl(sock,GETFL)");
????????exit(1);
????}
????opts?=?opts?|?O_NONBLOCK;
????if(fcntl(sock,?F_SETFL,?opts)?<?0)
????{
????????perror("fcntl(sock,SETFL,opts)");
????????exit(1);
????}
}
int?main()
{
????int?i,?maxi,?listenfd,?connfd,?sockfd,?nfds;
????pthread_t?tid1,?tid2;
????struct?task?*new_task?=?NULL;
????struct?user_data?*rdata?=?NULL;
????socklen_t?clilen;
????pthread_mutex_init(&mutex,?NULL);
????pthread_cond_init(&cond1,?NULL);
????//初始化用于讀線程池的線程,開啟兩個線程來完成任務,兩個線程會互斥地訪問任務鏈表
????pthread_create(&tid1,?NULL,?readtask,?NULL);
????pthread_create(&tid2,?NULL,?readtask,?NULL);
????//生成用于處理accept的epoll專用的文件描述符
????epfd?=?epoll_create(256);
????struct?sockaddr_in?clientaddr;
????struct?sockaddr_in?serveraddr;
????listenfd?=?socket(AF_INET,?SOCK_STREAM,?0);
????//把socket設置為非阻塞方式
????setnonblocking(listenfd);
????//設置與要處理的事件相關的文件描述符
????ev.data.fd?=?listenfd;
????//設置要處理的事件類型,當描述符可讀時出發,出發方式為ET模式
????ev.events?=?EPOLLIN?|?EPOLLET;
????//注冊epoll事件
????epoll_ctl(epfd,?EPOLL_CTL_ADD,?listenfd,?&ev);
????bzero(&serveraddr,?sizeof(serveraddr));
????serveraddr.sin_family?=?AF_INET;
????const?char?*local_addr?=?"127.0.0.1";
????inet_aton(local_addr,?&(serveraddr.sin_addr));?//htons(SERV_PORT);
????serveraddr.sin_port?=?htons(SERV_PORT);
????bind(listenfd,?(sockaddr?*)&serveraddr,?sizeof(serveraddr));
????//開始監聽
????listen(listenfd,?LISTENQ);
????maxi?=?0;
????for?(?;?;?)
????{
????????//等待epoll事件的發生
????????nfds?=?epoll_wait(epfd,?events,?20,?500);
????????//處理所發生的所有事件
????????for(i?=?0;?i?<?nfds;?++i)
????????{
????????????if(events[i].data.fd?==?listenfd)
????????????{
????????????????connfd?=?accept(listenfd,?(sockaddr?*)&clientaddr,?&clilen);
????????????????if(connfd?<?0)
????????????????{
????????????????????perror("connfd<0");
????????????????????exit(1);
????????????????}
????????????????setnonblocking(connfd);
????????????????const?char?*str?=?inet_ntoa(clientaddr.sin_addr);
????????????????std::cout?<<?"connec_?from?>>?"?<<?str?<<?std::endl;
????????????????//設置用于讀操作的文件描述符
????????????????ev.data.fd?=?connfd;
????????????????//設置用于注測的讀操作事件
????????????????ev.events?=?EPOLLIN?|?EPOLLET;
????????????????//注冊ev
????????????????epoll_ctl(epfd,?EPOLL_CTL_ADD,?connfd,?&ev);
????????????}
????????????else?if(events[i].events?&?EPOLLIN)
????????????{
????????????????printf("reading!\n");
????????????????if?(?(sockfd?=?events[i].data.fd)?<?0)?continue;
????????????????new_task?=?new?task();
????????????????new_task->fd?=?sockfd;
????????????????new_task->next?=?NULL;
????????????????//添加新的讀任務
????????????????pthread_mutex_lock(&mutex);
????????????????if(readhead?==?NULL)
????????????????{
????????????????????readhead?=?new_task;
????????????????????readtail?=?new_task;
????????????????}
????????????????else
????????????????{
????????????????????readtail->next?=?new_task;
????????????????????readtail?=?new_task;
????????????????}
????????????????//喚醒所有等待cond1條件的線程
????????????????pthread_cond_broadcast(&cond1);
????????????????pthread_mutex_unlock(&mutex);
????????????}
????????????else?if(events[i].events?&?EPOLLOUT)
????????????{
????????????????rdata?=?(struct?user_data?*)events[i].data.ptr;
????????????????sockfd?=?rdata->fd;
????????????????write(sockfd,?rdata->line,?rdata->n_size);
????????????????delete?rdata;
????????????????//設置用于讀操作的文件描述符
????????????????ev.data.fd?=?sockfd;
????????????????//設置用于注測的讀操作事件
????????????????ev.events?=?EPOLLIN?|?EPOLLET;
????????????????//修改sockfd上要處理的事件為EPOLIN
????????????????epoll_ctl(epfd,?EPOLL_CTL_MOD,?sockfd,?&ev);
????????????}
????????}
????}
}
void?*readtask(void?*args)
{
????int?fd?=?-1;
????unsigned?int?n;
????//用于把讀出來的數據傳遞出去
????struct?user_data?*data?=?NULL;
????while(1)
????{
????????//互斥訪問任務隊列
????????pthread_mutex_lock(&mutex);
????????//等待到任務隊列不為空
????????while(readhead?==?NULL)
????????????pthread_cond_wait(&cond1,?&mutex);?//線程阻塞,釋放互斥鎖,當等待的條件等到滿足時,它會再次獲得互斥鎖
????????fd?=?readhead->fd;
????????//從任務隊列取出一個讀任務
????????struct?task?*tmp?=?readhead;
????????readhead?=?readhead->next;
????????delete?tmp;
????????pthread_mutex_unlock(&mutex);
????????data?=?new?user_data();
????????data->fd?=?fd;
????????if?(?(n?=?read(fd,?data->line,?MAXLINE))?<?0)
????????{
????????????if?(errno?==?ECONNRESET)
????????????????close(fd);
????????????else
????????????????std::cout?<<?"readline?error"?<<?std::endl;
????????????if(data?!=?NULL)?delete?data;
????????}
????????else?if?(n?==?0)
????????{
????????????//客戶端關閉了,其對應的連接套接字可能也被標記為EPOLLIN,然后服務器去讀這個套接字
????????????//結果發現讀出來的內容為0,就知道客戶端關閉了。
????????????close(fd);
????????????printf("Client?close?connect!\n");
????????????if(data?!=?NULL)?delete?data;
????????}
????????else
????????{
????????????std::cout?<<?"read?from?client:?"?<<?data->line?<<?std::endl;
????????????data->n_size?=?n;
????????????//設置需要傳遞出去的數據
????????????ev.data.ptr?=?data;
????????????//設置用于注測的寫操作事件
????????????ev.events?=?EPOLLOUT?|?EPOLLET;
????????????//修改sockfd上要處理的事件為EPOLLOUT
????????????epoll_ctl(epfd,?EPOLL_CTL_MOD,?fd,?&ev);
????????}
????}
}
* This source code was highlighted by?YcdoiT. ( style: Vs )
========================================================= 給出一個簡單的客戶端吧,從《Linux編程技術詳解》書中拷貝而來。
#include?<stdio.h>
#include?<sys/types.h>
#include?<sys/socket.h>
#include?<sys/un.h>
#include?<netdb.h>
#include?<unistd.h>

int?main(int?argc,?char?*argv[])
{
????int?connect_fd;
????int?ret;
????char?snd_buf[1024];
????int?i;
????int?port;
????int?len;

????static?struct?sockaddr_in?srv_addr;
????if(argc?!=?3)
????{
????????printf("Usage:?%s?server_ip_address?port\n",?argv[0]);
????????return?1;
????}
????port?=?atoi(argv[2]);
????connect_fd?=?socket(PF_INET,?SOCK_STREAM,?0);
????if(connect_fd?<?0)
????{
????????perror("cannot?create?communication?socket");
????????return?1;
????}
????memset(&srv_addr,?0,?sizeof(srv_addr));
????srv_addr.sin_family?=?AF_INET;
????srv_addr.sin_addr.s_addr?=?inet_addr(argv[1]);
????srv_addr.sin_port?=?htons(port);
????ret?=?connect(connect_fd,?(struct?sockaddr?*)&srv_addr,?sizeof(srv_addr));
????if(ret?==?-1)
????{
????????perror("cannot?connect?to?the?server");
????????close(connect_fd);
????????return?1;
????}
????memset(snd_buf,?0,?1024);
????while(1)
????{
????????write(STDOUT_FILENO,?"input?message:",?14);
????????bzero(snd_buf,?1024);
????????len?=?read(STDIN_FILENO,?snd_buf,?1024);
????????if(snd_buf[0]?==?'@')
????????????break;
????????if(len?>?0)
????????????write(connect_fd,?snd_buf,?len);
????????len?=?read(connect_fd,?snd_buf,?len);
????????if(len?>?0)
????????????printf("Message?from?server:?%s\n",?snd_buf);
????}
????close(connect_fd);
????return?0;
}
* This source code was highlighted by?YcdoiT. ( style: Vs )

=========================================== ecy@ecy-geek:~/C$ ./epoll_server? connec_ from >> 127.0.0.1 reading! read from client: ni hao ya ya ya ya ya
reading! read from client: hello world
reading! Client close connect!
ecy@ecy-geek:~/C$ pstree | grep epoll |-gnome-terminal-+-bash---epoll_server---2*[{epoll_server}]
ecy@ecy-geek:~/C$ ./p13.5 127.0.0.1 5555 input message:ni hao ya ya ya ya ya Message from server: ni hao ya ya ya ya ya
input message:hello world Message from server: hello world
input message:@

總結

以上是生活随笔為你收集整理的Epoll例子的使用的全部內容,希望文章能夠幫你解決所遇到的問題。

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