朴素、Select、Poll和Epoll网络编程模型实现和分析——Poll模型
? ? ? ? 在《樸素、Select、Poll和Epoll網絡編程模型實現和分析——Select模型》中,我們分析了它只能支持1024個連接同時處理的原因。但是在有些需要同時處理更多連接的情況下,1024個連接往往是不夠的,也就是不能夠高并發。那么這個時候我們就可以采用本文介紹的Poll模型。(轉載請指明出于breaksoftware的csdn博客)
? ? ? ? 在使用Poll模型之前,我們需要定義一個保存連接信息的數組
struct pollfd fds[FDS_COUNT];
? ? ? ? 之后創建異步監聽socket、綁定端口和監聽端口等行為和《樸素、Select、Poll和Epoll網絡編程模型實現和分析——Select模型》一文中一模一樣,本文就不列出代碼了。我們把創建的socket信息賦值給fds數組的第一個元素,并且設定我們需要關注的事件POLLIN——可讀。
int timeout;int cur_fds_count;int rc;int index;int expect_events;int error_events;
……// 創建socketmemset(fds, 0, sizeof(fds));error_events = POLLERR | POLLNVAL;expect_events = POLLIN;fds[0].fd = listen_sock;fds[0].events = expect_events;cur_fds_count = 1;
? ? ? ? cur_fds_count用于記錄當前fds數組中有多少個被關心的文件描述符。因為一開始我們只關心監聽socket,所以它的初始值是1。
? ? ? ? 然后我們就要在一個死循環中,不停的調用poll函數,監控我們關心的文件描述符是否發生了狀態改變
timeout = (500); while (1) {rc = poll(fds, cur_fds_count, timeout);if (rc < 0) {perror("poll error\n");exit(EXIT_FAILURE);};if (rc == 0) {//perror("poll timeout\n");};
? ? ? ? poll函數的返回值和select函數類似。如果返回小于0,則說明發生了錯誤,我們讓程序退出。如果返回了0,則說明poll函數超時。如果大于0,則說明被關心的文件描述符狀態發生了改變。但是此時,我們仍然不知道是哪個文件描述符發生了改變,所以我們要遍歷fds數組。
int cur_fds_count_temp = cur_fds_count;for (index = 0; index < cur_fds_count_temp; ++index) {
? ? ? ? 這個時候我們有必要說明下pollfd結構體的定義
struct pollfd {int fd;short events;short revents;
};
? ? ? ? pollfd中的fd是用于記錄我們關心的文件描述符;events表示我們關心的事件,如POLLIN、POLLOUT等。revents是實際發生的事件。于是我們一開始要判斷改pollfd是否發生了事件改變
if (fds[index].revents == 0) {continue;}
? ? ? ? 接著我們判斷下發生的事件是否是我們定義的出錯事件。
if (fds[index].revents & error_events) {perror("revents error");
? ? ? ? 如果出錯的是監聽socket,則我們退出程序。
if (fds[index].fd == listen_sock) {perror("listen sock error");exit(EXIT_FAILURE);}
? ? ? ? 如果出錯的不是監聽socket,則它就是客戶端接入的socket,我們將它關閉,并且將fds數組的最后一個元素覆蓋當前位置,讓數組長度減一。這個過程是最精簡的數組縮小方式,如果使用新數組去記錄,將導致效率降低。
else {close(fds[index].fd);cur_fds_count--;if (index < cur_fds_count) {memcpy(&fds[index], &fds[cur_fds_count], sizeof(fds[cur_fds_count]));memset(&fds[cur_fds_count], 0, sizeof(fds[cur_fds_count]));index--;}cur_fds_count_temp--;continue;} }
? ? ? ? 我們再看發生的事件是否是我們關心的事件,如果不是,則continue掉,繼續處理下一個pollfd。
if (!(fds[index].revents & fds[index].events)) {continue;}
? ? ? ? 經過上述篩選,剩下的就是我們要正常處理的pollfd了。和Select方式一樣,我們先看看其是否是監聽socket。如果是,則獲取客戶端接入的socket值,并記錄到數組中。
if (fds[index].fd == listen_sock) {int new_sock;new_sock = accept(listen_sock, NULL, NULL);if (new_sock < 0) {//perror("accept error");if (errno != EWOULDBLOCK) {continue;}exit(EXIT_FAILURE);}else {request_add(1);//set_block_filedes_timeout(new_sock);if (cur_fds_count + 1 < sizeof(fds)) {fds[cur_fds_count].fd = new_sock;fds[cur_fds_count].events = expect_events;cur_fds_count++;}}}
? ? ? ? 如果不是監聽socket,則是客戶端接入的socket。我們就讀取這個socket中的內容,并寫入我們的回包。
else {if (0 == server_read(fds[index].fd)) {server_write(fds[index].fd);}close(fds[index].fd);cur_fds_count--;if (index < cur_fds_count) {memcpy(&fds[index], &fds[cur_fds_count], sizeof(fds[cur_fds_count]));memset(&fds[cur_fds_count], 0, sizeof(fds[cur_fds_count]));index--;}cur_fds_count_temp--;}}}return 0;
}
? ? ? ? 如此,我們便將簡單的poll服務器給寫完了。我們看下poll模型的處理能力。采用和《樸素、Select、Poll和Epoll網絡編程模型實現和分析——樸素模型》一文中相同的環境和壓力,我們看下服務器的數據輸出
? ? ? ? 再看下客戶端的輸出
? ? ? ?可見當前環境下poll模型的處理能力大概是每秒7500次請求。
總結
以上是生活随笔為你收集整理的朴素、Select、Poll和Epoll网络编程模型实现和分析——Poll模型的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 朴素、Select、Poll和Epoll
- 下一篇: 朴素、Select、Poll和Epoll