採用多程序與多執行緒的方法來實現併發伺服器時,監聽的工作由server應用程式自身通過accept函式不斷去監聽。當客戶端連線較多時,這種方法會大大降低程式執行效率,消耗cpu資源(cpu需要在不同進/執行緒中切換執行)。
多程序與多執行緒實現併發伺服器方法可以參考以下兩篇文章:
因為以上兩種方法的侷限性所以出現了採用多路io轉接的方式來設計伺服器,該類伺服器實現的思想為應用程式本身不在監聽客戶端連線,轉而由核心來代替監聽。主要使用的方法有三種,其一為select()
函式。
select
能監聽的檔案描述符個數受限於fd_setsize
,一般為1024,單純改變程序開啟的檔案描述符個數不能改變select監聽檔案個數
解決1024以下個客戶端時使用select
十分合適,但是如果鏈結客戶端過多,select
採用的輪詢模型,會大大降低伺服器響應效率,應採用其他方法。
int select(int nfds,fd_set* readfds,fd_set* writefds,fd_set* exceptfds,struct timeval* timeout);
引數
null,永遠等下去
設定timeval,等待固定時間
設定timeval裡時間均為0,檢查描述字後立即返回,輪詢
想要看的更詳細,請參考man手冊
//fd_set型別為位圖集合
void
fd_clr
(int fd, fd_set *set)
;//將fd從set集合中清除出去,相當於將對應位置為0
intfd_isset
(int fd, fd_set *set)
;//判斷fd是否在set集合中,返回1為在集合中,0為假,即不在集合中
void
fd_set
(int fd, fd_set *set)
;//將fd設定到set集合中,相當於將對應位置為1
void
fd_zero
(fd_set *set)
;//將檔案描述符集合清空,位圖清空相當於全部置為0
服務端**實現
特別要舉例說的是rset
引數,在呼叫select
函式前呼叫了rset = allset
語句,假如此時賦值後rset
中有fd1、fd2、fd3三個需要被監聽的檔案描述符,但是這三個檔案描述符中只有fd2檔案描述符滿足了讀的監聽條件,那麼在呼叫完select
函式後,select
函式會將fd1、fd3檔案描述符從rset
集合中剔除,rset
集合中只會存在滿足讀監聽條件的檔案描述符。
同樣地,readfds
、writefds
、exceptfds
三個傳入傳出引數皆是如此。
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define port 8888
#define ip "127.0.0.1"
intmain()
//3、監聽
listen
(sfd,
128)
;//定義乙個客戶端陣列,用來存放監聽檔案描述符,以免select到時候循壞0~1023個檔案描述符
int client[fd_setsize]
;//將陣列內值全部初始化為-1
for(
int i =
0; i < fd_setsize;i++
) fd_set rset,allset;
//rset 用來儲存滿足讀監聽條件的檔案描述符集合,allset用來儲存要監聽的檔案描述符集合
fd_zero
(&allset)
;fd_set
(sfd,
&allset)
;int nready,i;
int index =-1
;//定義陣列下標
int maxfd = sfd;
//定義乙個最大檔案描述符,select第乙個引數:監聽的檔案描述符集裡最大的檔案描述符+1
while(1
)int sockfd;
//判斷sfd是否存在於滿足監聽條件的集合中if(
fd_isset
(sfd,
&rset)
)char buf[bufsiz]
;printf
("%sconnected...;port:%d\n"
,inet_ntop
(af_inet,
&clie_addr.sin_addr.s_addr,buf,
sizeof
(buf)),
ntohs
(clie_addr.sin_port));
//列印誰連線了上來
for(i =
0; i < fd_setsize;i++)}
//由於select()函式只能有1024個檔案描述符,所以進行判斷,如果超過了就應該退出
if(i == fd_setsize)
fd_set
(cfd,
&allset)
;//更新檔案描述符集中最大的檔案描述符
if(cfd > maxfd)
if(i > index)if(
--nready ==0)
}//遍歷client[i]陣列,檢視陣列中是否需要監聽的檔案描述符
int len;
char rwbuf[bufsiz]
;for
(i =
0; i <= index;i++
)//如果sockfd檔案描述符在rset讀檔案描述符集中,說明是要監聽的if(
fd_isset
(sockfd,
&rset)
)else
if(len >0)
write
(sockfd,rwbuf,len);}
if(--nready ==0)
}}}close
(sfd)
;return0;
}
I O多路轉接 epoll伺服器
在前面的兩篇部落格中,我們介紹了最早期的select和改進版的poll 但是,他兩都沒有改進的就是,想要快速知道事件就緒並沒有得到改進,兩個全部是遍歷陣列,我們都知道它的時間複雜度就是o n 效率不是很高,時間複雜度達到o 1 才是高效的 epoll是linux特有的i o復用函式,它在實現和使用上...
多路IO轉接伺服器實現方法二 poll 函式
相較於多路io轉接伺服器實現方法一 select 函式,使用poll 函式的優點有 檔案描述符的上限可以突破1024 select 函式監聽集合與返回的滿足監聽條件的集合為乙個集合,而poll函式將監聽集合與符合監聽條件的集合實現了分離 搜尋滿足條件的檔案描述符的範圍縮小了 但,poll函式不能實現...
多路IO轉接伺服器實現方法三 epoll 函式
epoll是linux下多路復用介面select poll的增強版本。它能顯著提高程式在大量併發連線中只有少量活躍的情況下的系統cpu利用率,因為它會復用檔案描述符集合來傳遞結果而不用每次等待時間之前都必須重新準備要被監聽的檔案描述符集合 獲取事件的時候,它無須遍歷整個被監聽的檔案描述符集,只要遍歷...