標籤:style blog http 使用 os strong io 資料
“身為一個初學者,時常能體味到初學者入門的艱辛,所以總是想抽空作點什麼來盡我所能的協助那些需要協助的人。我也希望大家能把自己的所學和他人一起分享,不要去鄙視別人索取時的貪婪,因為最應該被鄙視的是不肯付出時的吝嗇。”
----- 題記 By PiggyXP(小豬)
前 言
其實我首先應該道歉,因為7月份的時候曾信誓旦旦的說要寫一套關於SOCKET所有模型的入門文章以及配套代碼,不過沒想到後天竟然被美女所迷出去度假了,剛剛回來不久。。。。。。-_-b其實那些模型的配套代碼我已經基本寫完了,只是沒寫配套文字,不過我想還是先從稍微難一點的模型寫起吧,因為其他模型的入門畢竟要簡單一些。
不過由於也是初學者,疏漏之處還望不吝指正。
本文凝聚著筆者心血,如要轉載,請指明原作者及出處,謝謝!^_^
OK, Let’s go ! Have fun!! q^_^p
本文配套的樣本源碼
(VC.net 2003編寫的多用戶端MFC代碼,配有詳盡注釋,只是簡單的顯示一下用戶端發來的字元,稍加改進就是個聊天室了):
http://www.haha567.com/PiggyXP/OverlappedModel.rar
(unix系統,千萬注意連結大小寫)
非常感謝網路版的limin兄弟為我無償提供的空間,以及在我學習過程中給我的大力協助與支援,真的非常感謝他,感激涕零啊~~~~~T_T
也歡迎大家光臨他的Blog一起討論網路技術
http://blog.haha567.com/
(本文假設你已經具備用SOCKET簡單模型編程的能力,如果對SOCKET一無所知請關注本系列其他文章)
目錄:
1. 重疊模型的優點
2. 重疊模型的基本原理
3. 關於重疊模型的基礎知識
4. 重疊模型的實現步驟
5. 多用戶端情況的注意事項
一. 重疊模型的優點
- 可以運行在支援Winsock2的所有windows平台 ,而不像完成連接埠只是支援NT系統。
- 比起阻塞、select、WSAAsyncSelect以及WSAEventSelect等模型,重疊I/O(Overlapped I/O)模型使應用程式能達到更佳的系統效能。
因為它和這4種模型不同的是,使用重疊模型的代理程式更新緩衝區收發系統直接使用資料,也就是說,如果應用程式投遞了一個10KB大小的緩衝區來接收資料,且資料已經到達通訊端,則該資料將直接被拷貝到投遞的緩衝區。
而這4種模型種,資料到達並拷貝到單通訊端接收緩衝區中,此時應用程式會被告知可以讀入的容量。當應用程式調用接收函數之後,資料才從單通訊端緩衝區拷貝到應用程式的緩衝區,差別就體現出來了。
- 從《windows網路編程》中提供的實驗結果中可以看到,在使用了P4 1.7G Xero處理器(CPU很強啊)以及768MB的回應伺服器中,最大可以處理4萬多個SOCKET串連,在處理1萬2千個串連的時候CPU佔用率才40% 左右 ―― 非常好的效能,已經直逼完成連接埠了^_^
二. 重疊模型的基本原理
說了這麼多的好處,你一定也躍躍欲試了吧,不過我們還是要先提一下重疊模型的基本原理。
概括一點說,重疊模型是讓應用程式使用重疊資料結構(WSAOVERLAPPED),一次投遞一個或多個Winsock I/O請求。針對這些提交的請求,在它們完成之後,應用程式會收到通知,於是就可以通過自己另外的代碼來處理這些資料了。
需要注意的是,有兩個方法可以用來管理重疊IO請求的完成情況(就是說接到重疊操作完成的通知):
- 事件對象通知(event object notification)
- 完成常式(completion routines) ,注意,這裡並不是完成連接埠
而本文只是講述如何來使用事件通知的的方法實現重疊IO模型,完成常式的方法準備放到下一篇講 :) (內容太多了,一篇寫不完啊) ,如沒有特殊說明,本文的重疊模型預設就是指的基於事件通知的重疊模型。
既然是基於事件通知,就要求將windows事件對象與WSAOVERLAPPED結構關聯在一起(WSAOVERLAPPED結構中專門有對應的參數),通俗一點講,就是。。。。對了,忘了說了,既然要使用重疊結構,我們常用的send, sendto, recv, recvfrom也都要被WSASend, WSASendto, WSARecv, WSARecvFrom替換掉了, 它們的用法我後面會講到,這裡只需要注意一點,它們的參數中都有一個Overlapped參數,我們可以假設是把我們的WSARecv這樣的操作操作“綁定”到這個重疊結構上,提交一個請求,其他的事情就交給重疊結構去操心,而其中重疊結構又要與windows的事件對象“綁定”在一起,這樣我們調用完WSARecv以後就可以“坐享其成”,等到重疊操作完成以後,自然會有與之對應的事件來通知我們操作完成,然後我們就可以來根據重疊操作的結果取得我們想要德資料了。
也許說了半天你還是不大明白,那就繼續往後面看吧。。。。。。。-_-b,語言表達能力有限啊~~~
三. 關於重疊模型的基礎知識
下面來介紹並舉例說明一下編寫重疊模型的程式中將會使用到的幾個關鍵函數。
- 1. WSAOVERLAPPED結構
這個結構自然是重疊模型裡的核心,它是這麼定義的
typedef struct _WSAOVERLAPPED {
DWORD Internal;
DWORD InternalHigh;
DWORD Offset;
DWORD OffsetHigh;
WSAEVENT hEvent; // 唯一需要關注的參數,用來關聯WSAEvent對象
} WSAOVERLAPPED, *LPWSAOVERLAPPED;
我們需要把WSARecv等操作投遞到一個重疊結構上,而我們又需要一個與重疊結構“綁定”在一起的事件對象來通知我們操作的完成,看到了和hEvent參數,不用我說你們也該知道如何來來把事件對象綁定到重疊結構上吧?大致如下:
WSAEVENT event; // 定義事件
WSAOVERLAPPED AcceptOverlapped ; // 定義重疊結構
event = WSACreateEvent(); // 建立一個事件物件控點
ZeroMemory(&AcceptOverlapped, sizeof(WSAOVERLAPPED)); // 初始化重疊結構
AcceptOverlapped.hEvent = event; // Done !!
- 2. WSARecv系列函數
在重疊模型中,接收資料就要靠它了,它的參數也比recv要多,因為要用刀重疊結構嘛,它是這樣定義的:
int WSARecv(
SOCKET s, // 當然是投遞這個操作的通訊端
LPWSABUF lpBuffers, // 接收緩衝區,與Recv函數不同
// 這裡需要一個由WSABUF結構構成的數組
DWORD dwBufferCount, // 數組中WSABUF結構的數量
LPDWORD lpNumberOfBytesRecvd, // 如果接收操作立即完成,這裡會返回函數調用
// 所接收到的位元組數
LPDWORD lpFlags, // 說來話長了,我們這裡設定為0 即可
LPWSAOVERLAPPED lpOverlapped, // “綁定”的重疊結構
LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine
// 完成常式中將會用到的參數,我們這裡設定為 NULL
);
傳回值:
WSA_IO_PENDING : 最常見的傳回值,這是說明我們的WSARecv操作成功了,但是
I/O操作還沒有完成,所以我們就需要綁定一個事件來通知我們操作何時完成
舉個例子:(變數的定義順序和上面的說明的順序是對應的,下同)
SOCKET s;
WSABUF DataBuf; // 定義WSABUF結構的緩衝區
// 初始化一下DataBuf
#define DATA_BUFSIZE 5096
char buffer[DATA_BUFSIZE];
ZeroMemory(buffer, DATA_BUFSIZE);
DataBuf.len = DATA_BUFSIZE;
DataBuf.buf = buffer;
DWORD dwBufferCount = 1, dwRecvBytes = 0, Flags = 0;
// 建立需要的重疊結構
WSAOVERLAPPED AcceptOverlapped ;// 如果要處理多個操作,這裡當然需要一個
// WSAOVERLAPPED數組
WSAEVENT event; // 如果要多個事件,這裡當然也需要一個WSAEVENT數組
// 需要注意的是可能一個SOCKET同時會有一個以上的重疊請求,
// 也就會對應一個以上的WSAEVENT
Event = WSACreateEvent();
ZeroMemory(&AcceptOverlapped, sizeof(WSAOVERLAPPED));
AcceptOverlapped.hEvent = event; // 關鍵的一步,把事件控制代碼“綁定”到重疊結構上
// 作了這麼多工作,終於可以使用WSARecv來把我們的請求投遞到重疊結構上了,呼。。。。
WSARecv(s, &DataBuf, dwBufferCount, &dwRecvBytes,
&Flags, &AcceptOverlapped, NULL);
其他的函數我這裡就不一一介紹了,因為我們畢竟還有MSDN這麼個好幫手,而且在講後面的完成常式和完成連接埠的時候我還會講到一些 ^_^
- 3. WSAWaitForMultipleEvents函數
熟悉WSAEventSelect模型的朋友對這個函數肯定不會陌生,不對,其實大家都不應該陌生,這個函數與線程中常用的WaitForMultipleObjects函數有些地方還是比較像的,因為都是在等待某個事件的觸發嘛。
因為我們需要事件來通知我們重疊操作的完成,所以自然需要這個等待事件的函數與之配套。
DWORD WSAWaitForMultipleEvents(
DWORD cEvents, // 等候事件的總數量
const WSAEVENT* lphEvents, // 事件數目組的指標
BOOL fWaitAll, // 這個要多說兩句:
// 如果設定為 TRUE,則事件數目組中所有事件被傳信的時候函數才會返回
// FALSE則任何一個事件被傳信函數都要返回
// 我們這裡肯定是要設定為FALSE的
DWORD dwTimeout, // 逾時時間,如果逾時,函數會返回 WSA_WAIT_TIMEOUT
// 如果設定為0,函數會立即返回
// 如果設定為 WSA_INFINITE只有在某一個事件被傳信後才會返回
// 在這裡不建議設定為WSA_INFINITE,因為。。。後面再講吧..-_-b
BOOL fAlertable // 在完成常式中會用到這個參數,這裡我們先設定為FALSE
);
傳回值:
WSA_WAIT_TIMEOUT :最常見的傳回值,我們需要做的就是繼續Wait
WSA_WAIT_FAILED : 出現了錯誤,請檢查cEvents和lphEvents兩個參數是否有效
如果事件數目組中有某一個事件被傳信了,函數會返回這個事件的索引值,但是這個索引值需要減去預定義值 WSA_WAIT_EVENT_0才是這個事件在事件數目組中的位置。
具體的例子就先不在這裡舉了,後面還會講到
注意:WSAWaitForMultipleEvents函數只能支援由WSA_MAXIMUM_WAIT_EVENTS對象定義的一個最大值,是 64,就是說WSAWaitForMultipleEvents只能等待64個事件,如果想同時等待多於64個事件,就要 建立額外的工作者線程,就不得不去管理一個線程池,這一點就不如下一篇要講到的完成常式模型了。
- 4. WSAGetOverlappedResult函數
既然我們可以通過WSAWaitForMultipleEvents函數來得到重疊操作完成的通知,那麼我們自然也需要一個函數來查詢一下重疊操作的結果,定義如下
BOOL WSAGetOverlappedResult(
SOCKET s, // SOCKET,不用說了
LPWSAOVERLAPPED lpOverlapped, // 這裡是我們想要查詢結果的那個重疊結構的指標
LPDWORD lpcbTransfer, // 本次重疊操作的實際接收(或發送)的位元組數
BOOL fWait, // 設定為TRUE,除非重疊操作完成,否則函數不會返回
// 設定FALSE,而且操作仍處於掛起狀態,那麼函數就會返回FALSE
// 錯誤為WSA_IO_INCOMPLETE
// 不過因為我們是等待事件傳信來通知我們操作完成,所以我們這裡設
// 置成什麼都沒有作用…..-_-b 別仍雞蛋啊,我也想說得清楚一些…
LPDWORD lpdwFlags // 指向DWORD的指標,負責接收結果標誌
);
這個函數沒什麼難的,這裡我們也不需要去關注它的傳回值,直接把參數填好調用就可以了,這裡就先不舉例了
唯一需要注意一下的就是如果WSAGetOverlappedResult完成以後,第三個參數返回是 0 ,則說明通訊對方已經關閉串連,我們這邊的SOCKET, Event之類的也就可以關閉了。
Trackback: http://tb.blog.csdn.net/TrackBack.aspx?PostId=114883
[推薦本文] [點擊此處收藏本文] 發表於 2004年09月23日 10:39 PM