epoll程式設計例項客戶端 網路程式設計 epoll

2021-10-16 18:39:58 字數 4207 閱讀 4721

前言

前面講了io多路復用的api,select和poll的缺點是效能不夠,客戶端連線越多效能下降越明顯,epoll的出現解決了這個問題,引用the linux programming inte***ce的乙個統計對比如下:

fd數量 poll cpu時間(秒) select cpu時間(秒) epoll cpu時間(秒)

10 0.61 0.73 0.41

100 2.9 3.0 0.42

1000 35 35 0.53

10000 990 930 0.66

可以看出fd達到100個以後,select/poll就非常慢了,而epoll即使達到10000個也表現得非常好,因為:每次呼叫select/poll,核心必須檢查所有傳進來的描述符;而對於epoll,每次呼叫epoll_ctl,核心會把相關資訊與底層的檔案描述關聯起來,當io事件就緒時,核心把資訊加到epoll的就緒列表裡。隨後呼叫epoll_wait,核心只需把就緒列表中的資訊提取出來返回即可。

每次呼叫select/poll,都要把待監控的所有檔案描述符傳給核心,函式返回時,核心要把描述符返回並標識哪些就緒,得到結果後還要逐個判斷所有描述符,才能確定哪些有事件;epoll在呼叫epoll_ctl時就已經維護著監控的列表,epoll_wait不需要傳入任何資訊,並且返回的結果只包含就緒的描述符,這樣就不用去判斷所有描述符。

從概念上理解epoll是這樣的,把要監控的fd的io事件註冊給epoll(呼叫epoll_ctl),然後呼叫epoll的api等待事件到達(呼叫epoll_wait),核心可能對每個fd維護著乙個讀和寫的緩衝區,那麼:如果我監控讀事件,並且讀緩衝區有資料了,epoll_wait就會返回,此時我可以呼叫read讀資料。

如果我監控寫事件,並且寫快取區未滿,epoll_wait也會返回,此時我可以呼叫write寫資料。

如果fd發生了一些錯誤,epoll_wait也會返回,此時我根據返回的標誌位,就可以知道。

如果我監控讀事件 並且有客戶端連線進來,epoll_wait就會返回,此時我可以呼叫accept接受客戶端。

epoll的api介紹int epoll_create(int size);

建立乙個epoll例項,返回代表例項的檔案描述符(fd),size自linux 2.6.8以後忽略,但必須大於0.

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

epoll控制介面,epfd就是epoll的檔案描述符,fd是要操作的檔案描述符,op有如下幾種:epoll_ctl_add 註冊fd的事件,事件型別在event指定。

epoll_ctl_mod 修改已註冊的fd事件。

epoll_ctl_del 刪除fd的事件。

epoll_event有乙個events成員,指定要註冊的事件型別,比較重要的幾個:epollin fd可讀事件

epollout fd可寫事件

epollerr fd發生錯誤,這個事件總是會被監控,不必手動增加

epollhup fd被掛起時,這個事件總是會被監控,不必手動增加,這通常發生在socket異常關閉時,此時read返回0,然後正常的清理socket資源。

epoll_event還有乙個epoll_data_t成員,由外部設定自定義資料,以方便後續處理。int epoll_wait(int epfd, struct epoll_event * events, int maxevents, int timeout);

等待事件發生,如果沒有事件發生,執行緒會被掛起,maxevents指定最大事件數,events外部傳入的事件陣列,長度應當等於maxevents,當事件發生時,epoll會把事件資訊填到這裡, timeout指定等待的最大時間, 0表示馬上返回,-1表示無限等待。

epoll_wait返回等待到的事件數,返回時,遍歷events對fd進行處理。 當epoll不再使用時,應該呼叫close關閉epollfd。

水平觸發和邊緣觸發

epoll觸發事件有兩種模式,預設的叫水平觸發(lt),另一種叫邊緣觸發(et):lt模式:只要fd的讀緩衝區不空,或寫緩衝區不滿,epoll_wait就會一直觸發事件(也就是返回)。

et模式:當被監控的fd狀態變化時(從未就緒變成就緒狀態),事件觸發一次。此後核心不再通知,除非有新的事件到來。

// 多謝 @黃蔚 的指正,原來et模式的描述有誤,仔細閱讀過man文件後已修正過來。

lt處理起來比et要簡單得多,讀事件觸發,只需要read一次,如果資料沒讀完,下次epoll_wait還會返回,寫也是一樣的;et模式就要求事件觸發時,一直讀一直寫直到明確知道已經讀寫完畢(返回eagin或ewouldblock的錯誤碼)。

水平觸發的伺服器程式大概是這樣的流程:accept乙個新連線,將這個新連線的fd加到epoll事件中,監聽epollin事件。

epollin事件到達時,read該fd中的資料。

如果要向該fd寫事件,向epoll增加epollout事件。

epollout事件到達,向fd write資料,如果資料太大無法一次寫出,那麼先保留epollout事件,下次事件到達繼續寫;如果寫出完畢,從epoll刪除epollout事件。

乙個實用的echo程式:

這一次我們要使用epoll和非阻塞socket來寫乙個真正實用的echo伺服器,呼叫fcntl函式,設定o_nonblock標誌位,即可讓socket的檔案描述符變成非阻塞模式。非阻塞模式處理起來比阻塞模式要複雜一些:read, write, accept這些函式不會阻塞,要麼成功,要麼返回-1失敗,errno記錄了失敗的原因,有幾個錯誤碼要關注:eagain 或 ewouldblock 只有非阻塞的fd才會發生,表示沒資料可讀,或沒空間可寫,或沒有客戶端可接受,下次再來吧。這兩個值可能相同也可能不同,最好一起判斷。

eintr 表示被訊號中斷,這種情況可以再一次嘗試呼叫。

其他錯誤表示真的出錯了。

向乙個fd寫資料比較麻煩,我們沒法保****把所有資料都寫完,所以需要先儲存在緩衝裡,然後向epoll增加寫事件,事件觸發時再向fd寫資料。等資料都寫完了,再把事件從epoll移除。這個程式把寫資料儲存在鍊錶中。

我們把監聽fd保留為阻塞模式,因為epoll_wait返回,可以確定一定有客戶端連線進來,所以accept一般可以成功,並不用擔心會阻塞。客戶端連線使用的是非阻塞模式,確保讀寫未完成時不會阻塞。

下面是這個程式的**,關鍵地方加了一些注釋,仔細看**比看文字描述更有用:)

#include "socket_lib h"#include #include #include #include #include

#define max_client 10000#define min_rsize 124#define backlog 128#define event_num 64

// 快取結點struct twbuffer ;

// 快取列表struct twblist ;

// 伺服器資訊struct tserver ;

// epoll增加讀事件void epoll_add(int efd, int fd, void *ud) else else

char host[ni_maxhost];

char service[ni_maxserv];

if (getnameinfo((struct sockaddr *)&claddr, addrlen, host, ni_maxhost, service, ni_maxserv, 0) == 0)

printf("client connect: fd=%d, (%s:%s)\n", cfd, host, service);

else

printf("client connect: fd=%d, (?unknown?)\n", cfd);

create_client(server, cfd);

break;

// 處理讀void handle_read(struct tserver *server, struct tclient *client) else {

if (ev.events & (epollin | epollhup)) { // read handle_read(server, ev.data.ptr);

if (ev.events & epollout) { // write handle_write(server, ev.data.ptr);

if (ev.events & epollerr) { // error handle_error(server, ev.data.ptr);

release_server(server);

return 0;

Python網路程式設計客戶端

程式設計流程 1 建立套接字 2 建立連線 3 傳送資料 資料要用二進位制個數編碼 4 接收資料,並設定資料流大小 import socket clientsocket socket.socket socket.af inet,socket.sock stream 建立連線 伺服器的位址和埠 hos...

網路聊天 客戶端(Winsock程式設計)

網路聊天 客戶端.cpp 定義控制台應用程式的入口點。include stdafx.h include include include pragma comment lib,ws2 32.lib using namespace std 接收伺服器發來的資料的程序 void recvproc sock...

python網路程式設計 HTTP客戶端

urllib和requests是python對http協議的應用,使用的兩個庫。urllib是python的標準內建庫,requests是乙個比urllib更強大的第三方庫。下面我們會使用乙個網域名稱為http httpbin.org的小型測試 來測試這兩個http客戶端。上面兩張分別是使用requ...