Linux下EPoll通訊模型簡析
EPoll基於I/O的事件通知機制,由系統通知使用者那些SOCKET觸發了那些相關I/O事件,事件中包含對應的檔案描述符以及事件型別,這樣應用程式可以針對事件以及事件的source做相應的處理(Acception,Read,Write,Error)。相比原先的SELECT模型(使用者主動依次檢查SOCKET),變成被動等待系統告知處於活躍狀態的SOCKET,效能提升不少(不需要依次遍歷所有的SOCKET,而只是對活躍SOCKET進行事件處理)。
基本步驟:
擅長對大量併發使用者的請求進行及時處理,完成伺服器與客戶端的資料互動。一個簡單實現步驟如下:
(1) 建立偵聽socket:ListenSock,將該描述符設定為非阻塞模式,呼叫Listen()函式在該套接字上偵聽連線請求。
(2) 使用epoll_create()函式建立檔案描述,設定可管理的最大socket描述符數目。
(3) 將ListenSock註冊進EPoll中進行監測
(4) EPoll監視啟動,epoll_wait()等待epoll事件發生。
(5)如果epoll事件表明有新的連線請求,則呼叫accept()函式,並將新建立連線新增到EPoll中。若為讀寫或者報錯等,呼叫對應的Handle進行處理。
(6) 繼續監視,直至停止。
上訴過程只是一個簡單的線性例項,在實際的應用過程中,為了提高監視效率,常常將EPOLL監聽到的事件交給其他專門的任務執行緒進行處理,以提高EPoll監視的效率。
主要涉及API
1.EPoll建立
int epoll_create(int size)
該函式生成一個epoll專用檔案描述符,其中的引數是指定生成描述符的最大範圍。在linux-2.4.32核心中根據size大小初始化雜湊表的大小,在linux2.6.10核心中該引數無用,使用紅黑樹管理所有的檔案描述符,而不是hash.
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
epoll_event 結構體的events欄位是表示感興趣的事件,取值為:
EPOLLIN:表示對應的檔案描述符可以讀;
EPOLLOUT:表示對應的檔案描述符可以寫;
EPOLLPRI:表示對應的檔案描述符有緊急的資料可讀;
EPOLLERR:表示對應的檔案描述符發生錯誤;
EPOLLHUP:表示對應的檔案描述符被結束通話;
EPOLLET:表示對應的檔案描述符有事件發生;
3、事件等待函式
int epoll_wait(int epfd,struct epoll_event * events,intmaxevents,int timeout)
該函式用於輪詢I/O事件的發生;
引數: epfd:由epoll_create 生成的epoll專用的檔案描述符;
epoll_event:用於回傳等待處理的事件陣列;
maxevents:每次能處理的事件數;
timeout:等待I/O事件發生的超時值(ms);-1永不超時,直到有事件產生才觸發,0立即返回
主要資料結構:
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 */
};
一般我們在程式設計時,利用event變數儲存事件對應的檔案描述符以及事件型別。
例項程式碼
伺服器段程式碼
int EPollServer()
{
int srvPort = 6888;
initSrvSocket(srvPort);
/* 建立 epoll 控制程式碼,把監聽socket加入到epoll集合裡 */
epollfd = epoll_create(MAX_EVENTS);
struct epoll_event event;
event.events = EPOLLIN | EPOLLET;
event.data.fd = srvfd;
if ( epoll_ctl(epollfd, EPOLL_CTL_ADD, srvfd, &event) < 0 )
{
printf(“epoll Add Failed: fd=%d\n”, srvfd);
return -1;
}
printf( “epollEngine startup:port %d”, srvPort);
while(1)
{
/*等待事件發生*/
int nfds = epoll_wait(epollfd, eventList, MAX_EVENTS, -1);
if ( nfds == -1 )
{
printf( “epoll_wait”);
continue;
}
/* 處理所有事件 */
int n = 0;
for (; n < nfds; n++)
handleEvent(eventList + n);
}
close(epollfd);
close(srvfd);
};
在事件處理handleEvent中(分為連線事件處理以及資料接收傳送事件)
void handleEvent(struct epoll_event* pEvent)
{
if (pEvent->data.fd == srvfd)
{
AcceptConn(srvfd);
}else{
RecvData(pEvent->data.fd);
SendData(pEvent->data.fd);
epoll_ctl(epollfd, EPOLL_CTL_DEL, pEvent->data.fd, pEvent);
}
}
//從標準輸入讀取資料,傳送給伺服器端,伺服器端在原樣返回,客戶端再接收並予以顯示
void handle(int sockfd)
{
char sendline[MAXLINE];
char recvline[MAXLINE];
int n;
for (;;) {
if (fgets(sendline, MAXLINE, stdin) == NULL)
break;
if (read(STDIN_FILENO, sendline, MAXLINE) == 0)
break;
n = write(sockfd, sendline, strlen(sendline));
n = read(sockfd, recvline, MAXLINE);
if (n == 0) {
printf(“echoclient: server terminatedprematurely\n”);
break;
}
write(STDOUT_FILENO, recvline, n);
//如果用標準庫的快取流輸出有時會出現問題
//fputs(recvline, stdout);
}
}
執行結果(Linux下截圖麻煩,直接複製控制檯結果)
客戶端:
administrator@ubuntu:~$ ./echoclient
welcome to echoclient
123456
123456
伺服器端:
administrator@ubuntu:~/source/EPollProject$ ./EPoll
epollEngine startup port 6888
handleEvent function, HANDLE: 3, EVENT is 1
Accept Connection: 5
handleEvent function, HANDLE: 5, EVENT is 1
RecvData function
SOCKET HANDLE: 5: CONTENT: 123456
content is 123456
SendData function
SendData: 123456
注:
1.此處只是學習了EPoll基本模型,在實際應用中,為了提高EPoll模型的監視效率,一般在監視執行緒中只做監視,不過事件處理工作,而是將事件交付其他執行緒處理。
2. 為了提高事件處理的效率,所以我們儘量避免在有事件時開闢執行緒處理,處理完關閉,一般在系統啟動時會建立執行緒池,將事件交與執行緒池中的空閒執行緒進行處理。在事件的處理過程中不會有縣城的建立、銷燬等操作。效率也提高了。相關文章
- Linux下Epoll簡介Linux
- Epoll 模型簡介模型
- Epoll模型詳解模型
- C/S通訊模型與B/S通訊模型介紹模型
- Linux企業級開發技術(3)——epoll企業級開發之epoll模型Linux模型
- 頁面間通訊與資料共享解決方案簡析
- 淺析vue中的元件通訊Vue元件
- 簡析Windows訊息機制Windows
- Linux 下的程式間通訊:套接字和訊號Linux
- 網路程式設計學習——Linux epoll多路複用模型程式設計Linux模型
- 網路通訊2:TCP簡單通訊TCP
- 485通訊協議問題淺析協議
- SMS簡訊通API——(3)SMS簡訊通資費標準API
- IO多路複用與epoll機制淺析
- Linux 下的程式間通訊:共享儲存Linux
- linux系統下poll和epoll核心原始碼剖析Linux原始碼
- 簡單區分WiFi通訊和WLAN通訊WiFi
- 高階IO模型之kqueue和epoll模型
- Linux 下的程式間通訊:使用管道和訊息佇列Linux佇列
- 詳解linux下的串列埠通訊開發Linux串列埠
- linux檔案系統簡析Linux
- 程式間通訊簡介
- 簡單的Socket通訊
- Linux系統下/proc/[pid] 目錄各檔案簡析Linux
- Flash下載工具Flashdown破解簡析!
- ET框架6.0分析三、網路通訊框架
- Node.js之網路通訊模組淺析Node.js
- Linux程式間通訊Linux
- 程式間通訊——LINUXLinux
- Linux TCP通訊示例LinuxTCP
- linux 串列埠通訊Linux串列埠
- 從linux原始碼看epollLinux原始碼
- Linux中epoll用法總結Linux
- 通過完整示例來理解如何使用 epoll
- Linux LED驅動原始碼簡析Linux原始碼
- socket 完成簡單的通訊
- SwiftNIO初探-簡單UDP通訊SwiftUDP
- IO通訊模型(三)多路複用IO模型