資料整理 socket之select函式

2021-04-29 18:02:46 字數 3734 閱讀 9433

select在socket程式設計中還是比較重要的,它能夠監視我們需要監視的檔案描述符的變化情況——讀寫或是異常。

select的函式格式(unix系統下的伯克利socket程式設計,和windows下的略有區別,體現兩個方面:一是select函式的第乙個引數,在windows下可以忽略,但在linux下必須設為最大檔案描述符加1;二是結構fd_set在兩個系統裡定義不一樣):

int select(int maxfdp,fd_set *readfds,fd_set *writefds,fd_set *errorfds,struct timeval *timeout);

先說明兩個結構體:

第一,struct fd_set可以理解為乙個集合,這個集合中存放的是檔案描述符(file descriptor),即檔案控制代碼,這可以是我們所說的普通意義的檔案,當然unix下任何裝置、管道、fifo等都是檔案形式,全部包括在內,所以毫無疑問乙個socket就是乙個檔案,socket控制代碼就是乙個檔案描述符。fd_set集合可以通過一些巨集由人為來操作,比如清空集合 fd_zero(fd_set *),將乙個給定的檔案描述符加入集合之中fd_set(int ,fd_set *),將乙個給定的檔案描述符從集合中刪除fd_clr(int ,fd_set*),檢查集合中指定的檔案描述符是否可以讀寫fd_isset(int ,fd_set* )。一會兒舉例說明。

第二,struct timeval是乙個大家常用的結構,用來代表時間值,有兩個成員,乙個是秒數,另乙個是毫秒數。

具體解釋select的引數:

int maxfdp是乙個整數值,是指集合中所有檔案描述符的範圍,即所有檔案描述符的最大值加1,不能錯!在windows中這個引數的值無所謂,可以設定不正確。

fd_set *readfds是指向fd_set結構的指標,這個集合中應該包括檔案描述符,我們是要監視這些檔案描述符的讀變化的,即我們關心是否可以從這些檔案中讀取資料了,如果這個集合中有乙個檔案可讀,select就會返回乙個大於0的值,表示有檔案可讀,如果沒有可讀的檔案,則根據timeout引數再判斷是否超時,若超出timeout的時間,select返回0,若發生錯誤返回負值。可以傳入null值,表示不關心任何檔案的讀變化。

fd_set *writefds是指向fd_set結構的指標,這個集合中應該包括檔案描述符,我們是要監視這些檔案描述符的寫變化的,即我們關心是否可以向這些檔案中寫入資料了,如果這個集合中有乙個檔案可寫,select就會返回乙個大於0的值,表示有檔案可寫,如果沒有可寫的檔案,則根據timeout引數再判斷是否超時,若超出timeout的時間,select返回0,若發生錯誤返回負值。可以傳入null值,表示不關心任何檔案的寫變化。

fd_set *errorfds同上面兩個引數的意圖,用來監視檔案錯誤異常。

struct timeval* timeout是select的超時時間,這個引數至關重要,它可以使select處於三種狀態,第一,若將null以形參傳入,即不傳入時間結構,就是將select置於阻塞狀態,一定等到監視檔案描述符集合中某個檔案描述符發生變化為止;第二,若將時間值設為0秒0毫秒,就變成乙個純粹的非阻塞函式,不管檔案描述符是否有變化,都立刻返回繼續執行,檔案無變化返回0,有變化返回乙個正值;第三,timeout的值大於0,這就是等待的超時時間,即 select在timeout時間內阻塞,超時時間之內有事件到來就返回了,否則在超時後不管怎樣一定返回,返回值同上述。

返回值:

負值:select錯誤 正值:某些檔案可讀寫或出錯 0:等待超時,沒有可讀寫或錯誤的檔案

在有了select後可以寫出像樣的網路程式來!舉個簡單的例子,就是從網路上接受資料寫入乙個檔案中。

#include

#include

#define port       5150

#define msgsize    1024

#pragma comment(lib, "ws2_32.lib")

int    g_itotalconn = 0;

socket g_clisocketarr[fd_setsize];

dword winapi workerthread(lpvoid lpparameter);

int main()

return 0; }

dword winapi workerthread(lpvoid lpparam)

;  

char           szmessage[msgsize];    

while (true)  

// we only care read event

ret = select(0, &fdread, null, null, &tv);

if (ret == 0)

for (i = 0; i < g_itotalconn; i++)

} else

} //if

}//for

}//while    

return 0; }

伺服器的幾個主要動作如下:

1.建立監聽套接字,繫結,監聽;

2.建立工作者執行緒;

3.建立乙個套接字陣列,用來存放當前所有活動的客戶端套接字,每accept乙個連線就更新一次陣列;

4.接受客戶端的連線。

這裡有一點需要注意的,就是我沒有重新定義fd_setsize巨集,所以伺服器最多支援的併發連線數為64。而且,這裡決不能無條件的ccept,伺服器應該根據當前的連線數來決定

是否接受來自某個客戶端的連線。一種比較好的實現方案就是採用wsaaccept函式,而且讓wsaaccept**自己實現的condition function。

如下所示:

int callback conditionfunc(lpwsabuf lpcallerid,lpwsabuf lpcallerdata, lpqos lpsqos,lpqos lpgqos,lpwsabuf lpcalleeid, lpwsabuf lpcalleedata,group far *

g,dword dwcallbackdata)

工作者執行緒裡面是乙個死迴圈,一次迴圈完成的動作是:

1.將當前所有的客戶端套接字加入到讀集fdread中;

2.呼叫select函式;

3.檢視某個套接字是否仍然處於讀集中,如果是,則接收資料。如果接收的資料長度為0,或者發生wsaeconnreset錯誤,則表示客戶端套接字主動關閉,這時需要將伺服器中

對應的套接字所繫結的資源釋放掉,然後調整我們的套接字陣列(將陣列中最後乙個套接字挪到當前的位置上)。

除了需要有條件接受客戶端的連線外,還需要在連線數為0的情形下做特殊處理,因為如果讀集中沒有任何套接字,select函式會立刻返回,這將導致工作者執行緒成為乙個毫無

停頓的死迴圈,cpu的佔用率馬上達到100%。

關係到套接字列表的操作都需要使用迴圈,在輪詢的時候,需要遍歷一次,再新的一輪開始時,將列表加入佇列又需要遍歷一次.也就是說,select在工作一次時,需要至少遍歷2次

列表,這是它效率較低的原因之一.

在大規模的網路連線方面,還是推薦使用iocp或epoll模型.但是select模型可以使用在諸如對戰類遊戲上,比如類似星際這種,因為它小巧易於實現,且對戰類遊戲的網路連線量

並不大. 對於select模型想要突破windows 64個限制的話,可以採取分段輪詢,一次輪詢64個.例如套接字列表為128個,在第一次輪詢時,將前64個放入佇列中用select進行狀態查詢,

待本次操作全部結束後.將後64個再加入輪詢佇列中進行輪詢處理.這樣處理需要在非阻塞式下工作.以此類推,select也能支援無限多個.

socket程式設計使用poll替換select

poll函式在處理併發時,無fd setsize限制 伺服器端 include include include include include include 使用signal函式 include 使用wait函式 include include include include include de...

MySQL許可權篇之DELETE及SELECT

delete許可權,該許可權僅僅能刪除表中的記錄。但是要注意如果僅僅只授予了delete許可權。比如 mysql show grants for ut01 grants for ut01 grant usage on to ut01 1 row in set 0.00 sec mysql grant...

socket函式 整理

include int connect int sockfd,const struct sockaddr server addr,socklen t addrlen 為了理解connect函式,我們需要對connect函式的功能進行介紹。connect函式的功能可以用一句話來概括,就是完成面向連線的...