重疊模型是一種異步IO模型。多執行緒結構中就考慮到採用異步的方式進行設備讀寫操作,即我們告訴系統對設備的讀寫數據,而同時應用程式的其他代碼繼續執行,直到獲取設備操作完畢的系統通知。
基本介紹
- 中文名:重疊IO模型
- 所屬學科:計算機
模型簡介,網路編程套用,編程步驟,
模型簡介
說到重疊模型首先還是提一下異步IO比較好,因為從本質上講,重疊模型也是一種異步IO模型。
我們知道,相對於計算機執行的其他操作而言,設備IO(檔案、管道、套接字等)是比較慢的。於是在多執行緒結構中就考慮到採用異步的方式進行設備讀寫操作,即我們告訴系統對設備的讀寫數據,而同時應用程式的其他代碼繼續執行,直到獲取設備操作完畢的系統通知。
在進行異步IO時,我們先向系統發出IO請求,作業系統佇列化各種IO請求,並在內部完成操作,當系統在處理IO請求時,我們的執行緒可以返回繼續執行,當作業系統處理完IO請求之後,通知我們數據操作(傳送、接收、出錯)完畢。
Windows提供了四種異步IO技術,機制幾乎是相同的,區別在於通知結果的方式不同:
1、使一個設備核心對象變為有信號
Windows將設備句柄看作可同步的對象,即它可以處於有信號或處於無信號狀態,當創建設備句柄、以異步的方式傳送IO請求時,該句柄處於無信號狀態,當異步IO完成之後,該句柄受信,通過WaitForSingleobject或WaitForMultipleObjects函式可以判斷設備操作何時完成。該技術只能用於一個設備只傳送一個IO請求,否則,若一個設備對應多個操作,當句柄受信時無法判斷是該設備的那個操作完成。
2、使一個事件核心對象變為有信號
針對每個I/O操作綁定一個核心事件對象,並將等待事件等待函式等待該事件的受信,當I/O操作完成後系統使得與該操作綁定的事件受信,從而判斷那個操作完成。該技術解決了使一個設備核心對象變為有信號技術中一個設備只能對應一個操作的不足。
3、警告I/O
在該技術中,當發出設備IO請求時,同時要求我們傳遞一個被稱為完成例程的回調函式,當IO請求完成時調用該回調函式完成我們需要處理的工作。該技術允許單個設備同時進行多個I/O請求。
4、完成連線埠
完成連線埠技術多用於處理大規模的請求,通過內在的進程池技術可以達到很高的性能,此時暫不做深入討論,若欲知後事如何,請自己看,或等下回完成連線埠部分分解。
好,至此,刀磨的差不多了,估計也飄了~~~~,乾正事了。
網路編程套用
網路編程中的重疊IO模型理論
在編程中可以有兩種方法管理I/O請求:
1、事件對象通知(eventobjectnotification)—對應上面的2
2、完成例程(completionroutines)——對應上面的3
這裡只討論第一種情況,具體思路就是正對每個套接字的每個操作綁定一個事件,然後將所有的事件組成事件數據,運用事件等待函式等待事件的發生,並根據事件與操作的對應關係對與之對應的操作進行處理。
下面說一下實際編程中所用到的數據結構及函式。
1、WSAOVERLAPPED結構
這個結構自然是重疊模型里的核心,用於綁定套接字、操作以及操作對應的事件。
typedefstruct_WSAOVERLAPPED{
DWORDInternal;
DWORDInternalHigh;
DWORDOffset;//檔案操作中想要開始的低偏移量,網路中不用
DWORDOffsetHigh;//檔案操作中想要開始的高偏移量,網路中不用
WSAEVENThEvent;//網路編程中需要關注的參數,用來關聯WSAEvent對象
}WSAOVERLAPPED,*LPWSAOVERLAPPED;
2.WSARecv系列函式
在重疊模型中,傳送接收等函式均由WSASend、WSARecv、WSASendTo、WSARecvFrom等函式代替,通過該函式將I/O操作與WSAOVERLAPPED結構綁定起來,也既是與一個特定的事件綁定在一起,當系統完成操作之後與該操作綁定的事件受信。
intWSARecv(
SOCKETs,//當然是投遞這個操作的套接字
LPWSABUFlpBuffers,//接收緩衝區,是WSABUF結構構成的數組
DWORDdwBufferCount,//數組中WSABUF結構的數量
LPDWORDlpNumberOfBytesRecvd,//如果接收操作立即完成,返回函式調用所接收到的位元組數
LPDWORDlpFlags,//說來話長了,我們這裡設定為0即可
LPWSAOVERLAPPEDlpOverlapped,//“綁定”的重疊結構
LPWSAOVERLAPPED_COMPLETION_ROUTINElpCompletionRoutine);//完成例程中將會用到的參數,我們這裡設定為NULL
返回值:
WSA_IO_PENDING:最常見的返回值,這是說明我們的WSARecv操作成功了,但是I/O操作還沒有完成,所以我們就需要綁定一個事件來通知我們操作何時完成
3、WSAWaitForMultipleEvents函式
該函式類似於執行緒中常用的WaitForMultipleObjects函式,都是在等待事件的觸發。我們將WSARecv等操作上綁定的LPWSAOVERLAPPED數據結構中的事件組稱事件數據,在該函式上等待。
DWORDWSAWaitForMultipleEvents(
DWORDcEvents,//等候事件的總數量
constWSAEVENT*lphEvents,//事件數組的指針
BOOLfWaitAll,//當設定為TRUE,事件數組中所有事件被傳信的時候函式才會返回
//FALSE則任何一個事件被傳信函式都要返回,此時設為FALSE
DWORDdwTimeout,//逾時時間,如果逾時,函式會返回WSA_WAIT_TIMEOUT
//如果設定為0,函式會立即返回
BOOLfAlertable);//在完成例程中會用到這個參數,這裡我們先設定為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函式
我們利用該函式來查詢重疊操作的結果,定義如下:
BOOLWSAGetOverlappedResult(
SOCKETs,//SOCKET,需要操作的套接字
LPWSAOVERLAPPEDlpOverlapped,//我們想要查詢結果的那個重疊結構的指針
LPDWORDlpcbTransfer,//本次重疊操作的實際接收(或傳送)的位元組數
BOOLfWait,//設定為TRUE,除非重疊操作完成,否則函式不會返回
//設定FALSE,而且操作仍處於掛起狀態,那么函式就會返回FALSE
//此處採用的是設備核心對象受信方式,等待套接字受信。
LPDWORDlpdwFlags);//指向DWORD的指針,負責接收結果標誌
註:如果WSAGetOverlappedResult完成以後,第三個參數返回是0,則說明通信對方已經關閉連線,我們這邊的SOCKET,Event之類的也就可以關閉了。
編程步驟
1、創建一個套接字,開始在指定的連線埠上監聽連線請求。
2、接收一個入站的連線請求。
3、為接受的套接字創建新的WSAOVERLAPPED結構,並分配事件對象句柄。
4、以WSAOVERLAPPED結構為參數,在套接字上投遞WSARecv調用。
5、將所有接受套接字的事件組建事件數組,並調用WSAWaitForMultipleEvents函式,等待與重疊調用關聯在一起的事件受信。
6、使用WSAGetOverlappedResult函式,判斷重疊調用的返回狀態。
7、重新組建事件數組。
8、在套接字上重投遞WSARecv請求。
9、重複5~8。
例子:初步封裝了OverLapped類
/*SockObject*/
#include
classSockObject
{
public:
SOCKETm_sock;
intm_operatNum;
public:
SockObject(void);
SockObject(SOCKETmySock);
SockObject(SockObject&mySockObject);
~SockObject(void);
};
SockObject::SockObject(SOCKETmySock)
{
m_sock=mySock;
m_operatNum=0;
}
SockObject::SockObject(SockObject&mySockObject)
{
m_sock=mySockObject.m_sock;
m_operatNum=mySockObject.m_operatNum;
}
/******************************************************************************
*數據結構名稱:OverObject
*功能:記錄一個套接字的一個操作、一個事件和一個重疊I/O的關聯
*****************************************************************************/
classOverObject
{
public:
SOCKETm_sock;/*綁定的套接字*/
OVERLAPPEDm_overlapped;/*綁定的重疊I/O*/
char*m_buf;/*用於存放數據的緩衝區*/
intm_len;/*緩衝區的長度*/
intm_operation;/*套接字針對的操作*/
public:
booloperator==(constOverObject&myOverObject)const;
public:
OverObject(void);
OverObject(constOverObject&myOverObject);
OverObject(SOCKETmySock,intmyLen);
~OverObject(void);
};
/*定義指向重疊對象的指針類型*/
typedefOverObject*PtrOverObject;
OverObject::~OverObject(void)
{
deletem_buf;
}
/********************************************************************************
*函式介紹:本函式是OverObject類的複製構造函式。
*********************************************************************************/
OverObject::OverObject(constOverObject&myOverObject)
{
m_sock=myOverObject.m_sock;
m_overlapped=myOverObject.m_overlapped;
m_buf=newchar[myOverObject.m_len];
strcpy(m_buf,myOverObject.m_buf);
m_len=myOverObject.m_len;
m_operation=myOverObject.m_operation;
}
/********************************************************************************
*函式介紹:本函式是OverObject類帶參數的構造函式。
*********************************************************************************/
OverObject::OverObject(SOCKETmySock,intmyLen)
{
m_sock=mySock;
m_buf=newchar[myLen];
m_len=myLen;
m_overlapped.hEvent=::WSACreateEvent();
}
/********************************************************************************
*函式介紹:本函式是OverObject類的==運算符重載函式
*********************************************************************************/
boolOverObject::operator==(constOverObject&myOverObject)const
{
if(this->m_sock==myOverObject.m_sock&&this->m_operation==myOverObject.m_operation&&this->m_len==myOverObject.m_len&&!strcmp(this->m_buf,myOverObject.m_buf))
{
coutoverObjects;/*需要維護的所有重疊I/O*/
vectoreventArray;/*所有重疊I/O所對應的事件組成的數組,作為等待函式的參數*/
vectorsockArray;/*需要維護的所有套接字,當操作為零時關閉套接字*/
public:
list::iteratorGetOverObject(SOCKETmySock,intmyLen);
voidFreeOverObject(list::iteratormyPtrOverObject);
list::iteratorOverlapped::FindOverObject(HANDLEmyEvent);
voidRebuildEventArray();
voidCreateAcceptEvent();
voidSetAcceptEvent();
voidResetAcceptEvent();
boolIsAcceptEvent(intindex);
boolPostRecv(list::iteratormyPtrOverObject);
boolPostSend(list::iteratormyPtrOverObject);
boolPostAccept(list::iteratormyPtrOverObject);
Overlapped(void);
~Overlapped(void);
voidInitSocket();
};
/********************************************************************************
*函式介紹:創建重疊對象的類對象,並插入重疊對象鍊表中。
*********************************************************************************/
list::iteratorOverlapped::GetOverObject(SOCKETmySock,intmyLen)
{
OverObjectlocalOverObject(mySock,myLen);
overObjects.push_back(localOverObject);
eventArray.push_back(localOverObject.m_overlapped.hEvent);
list::iteratorret=overObjects.end();
ret--;
returnret;
}
/********************************************************************************
*函式介紹:釋放重疊對象鍊表中指定的重疊對象。
*********************************************************************************/
voidOverlapped::FreeOverObject(list::iteratormyPtrOverObject)
{
overObjects.erase(myPtrOverObject);
}
/********************************************************************************
*函式介紹:從重疊對象列表中查找指定事件所對應的重疊對象。
*********************************************************************************/
list::iteratorOverlapped::FindOverObject(HANDLEmyEvent)
{
list::iteratorlocalIerator;
for(localIerator=overObjects.begin();localIerator!=overObjects.end();localIerator++)
{
if(localIerator->m_overlapped.hEvent==myEvent)
{
break;
}
}
returnlocalIerator;
}
/********************************************************************************
*函式介紹:遍歷重疊對象列表,重建重疊對象列表所對應的事件數組。
*********************************************************************************/
voidOverlapped::RebuildEventArray()
{
eventArray.clear();
list::iteratoroverObjIterator;
overObjIterator=overObjects.begin();
for(overObjIterator;overObjIterator!=overObjects.end();++overObjIterator)
{
eventArray.push_back(overObjIterator->m_overlapped.hEvent);
}
}
/********************************************************************************
*函式介紹:投放接受操作,即將指定套接字的Recv操作與重疊I/O對象關聯起來。
*********************************************************************************/
boolOverlapped::PostRecv(list::iteratormyPtrOverObject)
{
myPtrOverObject->m_operation=OP_READ;
DWORDdwBytes;
DWORDdwFlags=0;
WSABUFbuf;
buf.buf=myPtrOverObject->m_buf;
buf.len=myPtrOverObject->m_len;
memset(buf.buf,0,buf.len);
if(::WSARecv(myPtrOverObject->m_sock,&buf,1,&dwBytes,&dwFlags,&myPtrOverObject->m_overlapped,NULL)!=NO_ERROR)
{
if(::WSAGetLastError()!=WSA_IO_PENDING)
{
returnfalse;
}
}
returntrue;
}
/********************************************************************************
*函式介紹:投放傳送操作,即將指定套接字的Send操作與重疊I/O對象關聯起來。
*********************************************************************************/
boolOverlapped::PostSend(list::iteratormyPtrOverObject)
{
myPtrOverObject->m_operation=OP_WRITE;
DWORDdwBytes;
DWORDdwFlags=0;
WSABUFbuf;
buf.buf=myPtrOverObject->m_buf;
buf.len=myPtrOverObject->m_len;
if(::WSASend(myPtrOverObject->m_sock,&buf,1,&dwBytes,dwFlags,&myPtrOverObject->m_overlapped,NULL)!=NO_ERROR)
{
if(::WSAGetLastError()!=WSA_IO_PENDING)
{
returnfalse;
}
}
returntrue;
}
/********************************************************************************
*函式介紹:創建accept函式完成事件,用於處理accepe後等待函式的等待事件句柄數組發
生變化,若此時無事件觸發,等待事件句柄數組仍以原來的事件句柄數組為依
據等待。
*********************************************************************************/
voidOverlapped::CreateAcceptEvent()
{
//標誌套接字,用於觸發accept完成事件。
SOCKETmyClient=::WSASocket(AF_INET,SOCK_STREAM,IPPROTO_TCP,NULL,0,WSA_FLAG_OVERLAPPED);
list::iteratoracceptIterator=GetOverObject(myClient,512);
RebuildEventArray();
}
/********************************************************************************
*函式介紹:當accept函式完成時,重置accept所對應的事件,從而使得等待函式能夠在改
變後的事件句柄數組上等待。
*********************************************************************************/
voidOverlapped::SetAcceptEvent()
{
::setevent(eventArray.front());
}
voidOverlapped::ResetAcceptEvent()
{
::ResetEvent(eventArray.front());
}
boolOverlapped::IsAcceptEvent(intindex)
{
if(index==0)
{
returntrue;
}
else
{
returnfalse;
}
}
/*主程式*/
#include"Overlapped.h"
#include
#include
boolOperateFunction(PtrOverObjectmyPtrOverObject);
UINTWINAPIServerThread(PVOIDpvParam);
OverlappedmyOverlapped;
intmain()
{
WSADATAwsaData;
if(WSASTARTUP(MAKEWORD(2,2),&wsaData)!=0)
{
cout::iteratorlocalIterator=myOverlapped.GetOverObject(myClient,512);
myOverlapped.PostRecv(localIterator);
myOverlapped.RebuildEventArray();
myOverlapped.SetAcceptEvent();
}
charch;
cin>>ch;
return0;
}
/********************************************************************************
*函式介紹:數據處理函式,按照不同操作類型,處理數據的傳送或接受。
*********************************************************************************/
boolOperateFunction(list::iteratormyOverObjectIterator)
{
DWORDdwTrans;
DWORDdwFlags;
BOOLret=::WSAGetOverlappedResult(myOverObjectIterator->m_sock,&(myOverObjectIterator->m_overlapped),&dwTrans,false,&dwFlags);
if(!ret)
{
if(myOverObjectIterator->m_sock!=INVALID_SOCKET)
{
closesocket(myOverObjectIterator->m_sock);
}
coutm_operation)
{
caseOP_READ:/*接收數據完成*/
if(dwTrans>0)
{
coutm_buf::iteratorlocalIterator=myOverlapped.GetOverObject(myOverObjectIterator->m_sock,512);
localIterator->m_len=myOverObjectIterator->m_len;
strcpy(localIterator->m_buf,myOverObjectIterator->m_buf);
myOverlapped.PostSend(localIterator);
myOverlapped.RebuildEventArray();
returntrue;
}
else
{
closesocket(myOverObjectIterator->m_sock);
myOverlapped.FreeOverObject(myOverObjectIterator);
myOverlapped.RebuildEventArray();
cout0)
{
returntrue;
}
else
{
closesocket(myOverObjectIterator->m_sock);
myOverlapped.FreeOverObject(myOverObjectIterator);
myOverlapped.RebuildEventArray();
returnfalse;
}
break;
}
}
/********************************************************************************
*函式介紹:服務執行緒函式,平常處於等待狀態,完成數據處理。
*********************************************************************************/
UINTWINAPIServerThread(PVOIDpvParam)
{
while(true)
{
intindex;
DWORDeventNum=(DWORD)(myOverlapped.eventArray.size());
index=::WSAWaitForMultipleEvents(eventNum,&(myOverlapped.eventArray),false,WSA_INFINITE,false);
if(index==WSA_WAIT_FAILED)
{
cout::iteratornowIterator=(myOverlapped.FindOverObject(myOverlapped.eventArray[index]));
if(nowIterator!=NULL)
{
boolret=OperateFunction(nowIterator);
if(ret)
{
::WSAResetEvent(myOverlapped.eventArray[index]);
myOverlapped.PostRecv(nowIterator);
}
}
}
}
return0;
}