目錄
socket編程的demo中使用的都是最基本的,但是一般不會真正用在項目中的代碼。而實際項目中,需要面臨復雜多變的需求環境,比如有多個socket連接,或者服務需要監聽的時候,可能有很多socket連接進來。面對這種情況,最直接最簡單的想法是,一個socket連接創建一個線程去處理。當然,在socket連接數較少的情況下,這種方式無可厚非,但是如果連接數量較大,就會出現意外情況。
我們都知道,linux下一個線程默認所占的內存是8M(可以使用ulimit -s查看),那么加入,1000個socket連接,建立1000線程,光線程的開銷就高達8G多,更遑論其他業務還要使用內存了。
而且,在很多情況下,socket建立連接之后,并不是要一直通信,而是間隔通信,那么占用一個獨立的線程來“照顧”這個連接顯得很不明智。
針對這種情況,就需要采用多路復用機制,所謂多路復用,就是一個進程見識多個socket描述符,一旦某個socket描述符就緒(可讀寫或者異常)了,就會通知應用程序,進行相應的處理。
目前的多路復用機制有三種,select、poll 和 epoll。這三種機制各有優劣
頭文件和函數聲明:
#include <sys/select.h>int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);FD_ZERO(int fd, fd_set* fds) // 清空集合FD_SET(int fd, fd_set* fds) // 將給定的描述符加入集合FD_ISSET(int fd, fd_set* fds) // 判斷指定描述符是否在集合中 FD_CLR(int fd, fd_set* fds) // 將給定的描述符從文件中刪除
描述:
監聽多個文件描述符的屬性變化。
函數返回后,需要便利fd_set來找到就緒的描述符
參數說明:
nfds: 需要監聽的描述符的范圍,一般是最大描述符+1,比如,現在需要監聽 0/1/2/3/4/5 這幾個描述符,則參數設置為6,在linux下,值最大是1024
readfds: 監聽到的可讀的描述符的set,所有可讀的描述符都會存儲到這里
writefds: 監聽到的可寫的描述符的set
exceptfds: 監聽到的異常的描述符set
timeout: select 方法的超時時間,這個參數決定 select 的運行機制,可能有三種值
返回值:
select 返回有事件的描述符數量,可以在對應的set中找到具體的描述符,錯誤則返回-1
優點: 跨平臺
缺點:
頭文件和函數聲明:
#include <poll.h>int poll (struct pollfd *fds, unsigned int nfds, int timeout);struct pollfd { int fd; /* 描述符 */ short events; /* 需要監聽的事件 */ short revents; /* 實際發生的事件 */};
函數描述: 監聽多個文件描述符的屬性變化,和select類似,但是有很大區別,使用一個 pollfd 指針來替代 select 的三個set的功能
參數描述:
fd: 結構體指針,可以傳入多個結構體,每個結構體都是一個被監聽的描述符
nfds: 指定傳入的結構體的數量
timeout: 超時時間,單位毫秒,-1 表示阻塞
返回值: 返回有事件的描述符數量,函數返回后,需要輪詢來找到發生事件的描述符,錯誤則返回-1
pollfd 結構體:
? fd: 表示描述符
? events: 需要監聽的事件掩碼,取值如下
? revents: 實際發生的事件掩碼,取值如下
宏定義 | 可作events的值 | 可作revents的值 | 說明 |
---|---|---|---|
POLLIN | y | y | 數據可讀 |
POLLRDNORM | y | y | 普通數據可讀 |
POLLRDBAND | y | y | 優先數據可讀 |
POLLPRI | y | y | 緊迫帶數據可讀 |
POLLOUT | y | y | 數據可寫,不會阻塞 |
POLLWRNORM | y | y | 普通數據可寫,不會阻塞 |
POLLWRBAND | y | y | 優先級帶數據可寫,不會阻塞 |
POLLMSGSIGPOLL | y | y | 消息可用 |
非法事件
宏定義 | 可作events的值 | 可作revents的值 | 說明 |
---|---|---|---|
POLLERR | y | 發生錯誤 | |
POLLHUP | y | 發生掛起 | |
POLLNVAL | y | 描述不是打開的文件 |
POLLIN | POLLPRI 等價于 select 的讀事件,而 POLLIN 等價于 POLLRDNORM | POLLRDBAND
POLLOUT | POLLWRBAND 等價于 select 的寫事件,而 POLLOUT 等價于 POLLWRNORM
這些事件不是互斥的,可以同時設置
優缺點:
和 select 相比,poll 沒有了數量的限制,但是數量太大也會影響效率
poll 同樣有著將傳入的描述符從用戶態復制到內核態的缺點,開銷隨著描述符數量的增大而線性增大
epoll是后來提出的,作為 select 和 poll 的增強版本
epoll 的操作需要三個接口,頭文件和聲明如下:
#include <sys/epoll.h> int epoll_create(int size); int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event); int epoll_wait(int epfd, struct epoll_event * events, int maxevents, int timeout);
int epoll_create(int size);
創建一個 epoll 專用的描述符,size 為監聽的數目
size 參數并沒有限制 epoll 監聽的描述符的最大限制,而是作為內部分配數據結構的一個建議,linux自動2.6.8版本之后,該參數被忽略,只要大于0就行
返回一個描述符,使用結束時,需要close(),錯誤則返回-1
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
epoll 的事件注冊,告訴 epoll 要監聽哪些事件
返回0表示注冊成功,-1表示失敗
參數:
epfd: epoll 專用的描述符,由epoll_create() 返回
op: 該函數的作用,即注冊(EPOLL_CTL_ADD)、修改(EPOLL_CTL_MOD)和刪除(EPOLL_CTL_DEL)
fd: 需要監聽的描述符
event: 告訴內核要監聽什么事件,聲明如下:
// 保存觸發事件的某個文件描述符相關的數據(與具體使用方式有關) typedefunionepoll_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 */ };
epoll_event 中的 events 可以是以下宏定義的集合
宏定義 | 說明 |
---|---|
EPOLLIN | 表示對應的文件描述符可以讀(包括對端 SOCKET 正常關閉) |
EPOLLOUT | 表示對應的文件描述符可以寫 |
EPOLLPRI | 表示對應的文件描述符有緊急的數據可讀(這里應該表示有帶外數據到來) |
EPOLLERR | 表示對應的文件描述符發生錯誤 |
EPOLLHUP | 表示對應的文件描述符被掛斷 |
EPOLLET | 將 EPOLL 設為邊緣觸發(Edge Trigger)模式,這是相對于水平觸發(Level Trigger)來說的 |
EPOLLONESHOT | 只監聽一次事件,當監聽完這次事件之后,如果還需要繼續監聽這個 socket 的話,需要再次把這個 socket 加入到 EPOLL 隊列里 |
int epoll_wait(int epfd, struct epoll_event * events, int maxevents, int timeout);
等待 epoll 監聽下的 IO 事件
參數:
epfd: epoll 描述符
events: epoll 會把發生的事件賦值到events中
maxevents: 表明這個 events 的大小
timeout: 超時時間,單位毫秒,-1表示阻塞
返回值: 返回需要處理的事件數目,0表示超時未有事件,-1表示失敗
FD_ZERO(fd_set *fdset);//清空一個描述符集合FD_SET(fd_set *fdset, int fd);//添加fd到描述符集合中FD_CLR(fd_set *fdset, int fd);//從描述符集合中刪除一個fdFD_ISSET(int fd,fd_set *fdset);//檢查fd是否在描述符集合中
epoll 的工作模式有兩種:LT(level trigger) 和 ET(edge trigger),默認LT模式,區別如下:
LT模式: 當 epoll_wait 檢測到描述符事件發生,并通知應用程序,應用程序可以不利己處理該事件,下次調用 epoll_wait 時,還是會通知此事件
ET模式: 當 epoll_wait 檢測到描述符事件發生,并通知應用程序,應用程序必須立即處理該事件。如果不處理,則下次調用時,不會再次通知此事件
LT模式:
默認模式,支持阻塞和非阻塞方式,內核會通知事件發生,若果不做任何操作,則內核下次還是會通知,編程不易出錯,select/poll都是這種模式
ET模式:
告訴你工作模式,只支持非阻塞。在這種模式下,當描述符從未就緒變為就緒時,內核通過epoll告訴你。然后它會假設你知道文件描述符已經就緒,并且不會再為那個文件描述符發送更多的就緒通知,直到你做了某些操作導致那個文件描述符不再為就緒狀態了(比如,你在發送,接收或者接收請求,或者發送接收的數據少于一定量時導致了一個EWOULDBLOCK 錯誤)。但是請注意,如果一直不對這個fd作IO操作(從而導致它再次變成未就緒),內核不會發送更多的通知(only once)
ET模式在很大程度上減少了epoll事件被重復觸發的次數,因此效率要比LT模式高。epoll工作在ET模式的時候,必須使用非阻塞套接口,以避免由于一個文件句柄的阻塞讀/阻塞寫操作把處理多個文件描述符的任務餓死。
在 select/poll中,進程只有在調用一定的方法后,內核才對所有監視的文件描述符進行掃描,而epoll事先通過epoll_ctl()來注冊一 個文件描述符,一旦基于某個文件描述符就緒時,內核會采用類似callback的回調機制,迅速激活這個文件描述符,當進程調用epoll_wait() 時便得到通知。(此處去掉了遍歷文件描述符,而是通過監聽回調的的機制。這正是epoll的魅力所在。)
如果沒有大量的idle -connection或者dead-connection,epoll的效率并不會比select/poll高很多,但是當遇到大量的idle- connection,就會發現epoll的效率大大高于select/poll。
168186.html
本文由 貴州做網站公司 整理發布,部分圖文來源于互聯網,如有侵權,請聯系我們刪除,謝謝!
網絡推廣與網站優化公司(網絡優化與推廣專家)作為數字營銷領域的核心服務提供方,其價值在于通過技術手段與策略規劃幫助企業提升線上曝光度、用戶轉化率及品牌影響力。這...
在當今數字化時代,公司網站已成為企業展示形象、傳遞信息和開展業務的重要平臺。然而,對于許多公司來說,網站建設的價格是一個關鍵考量因素。本文將圍繞“公司網站建設價...
在當今的數字化時代,企業網站已成為企業展示形象、吸引客戶和開展業務的重要平臺。然而,對于許多中小企業來說,高昂的網站建設費用可能會成為其發展的瓶頸。幸運的是,隨...
蘋果CEO喬布斯身價多少?世界排第幾?如果是凈值的話,蘋果首席執行官史蒂夫·喬布斯以83億美元的凈值在《福布斯》2011年全球富豪榜上排名第110位。與此同時,喬布斯在美國富豪中排名第34位。喬布斯怎么能持有蘋果一半以上的股份?當喬布斯持有蘋果公司11.3%的650萬股股份時,他在被股東趕出蘋果公司后賣掉了這11.3%的股份?,F在喬布斯回來時持有蘋果公司不到1%的股份,大約550萬美元的股份,只占...
二級路由器設置教程?1. 我們登錄到第二路由器頁面。2. 然后單擊路由器設置按鈕進入設置頁面。3. 在設置頁面中,單擊LAN端口設置。4. 進入LAN設置頁面后,我們選擇手動設置。5. 進入手動設置頁面后,我們將路由器的LAN地址更改為其他網段。6. 更改完成后,單擊保存按鈕。然后將WAN端口設置為與主路由器連接。二級路由wan口如何設置?不要設置靜態,實現二級路由器的構造:第一個路由器的廣域網端...
懷舊服保險絲哪里獲???可以在諾莫瑞根和荊棘谷的各種機械怪物后面投下引信,這些怪物的等級在30到40之間。魔獸世界懷舊服保險絲怎么得?荊棘谷(41.2,43.3)的微型礦車掉引信概率最高。而灼人峽谷(34.7,51.9)的柯朗克下降率略低,但相對較高。魔獸工程圖紙保險絲哪買?地點:東泉谷王勇鎮工程供應商。如何獲?。簭墓こ坦烫幉少?。過程:點擊U,打開你的信譽列表,殺死王勇鎮的工程供應商。然后跑路,...