重叠IO模型-异步IO
重疊IO模型-異步IO???
http://laiba.tianya.cn/laiba/CommMsgs?cmm=959&tid=2701316824681802728
說到重疊模型首先還是提一下異步IO比較好,因為從本質(zhì)上講,重疊模型也是一種異步IO模型。
我們知道,相對于計算機執(zhí)行的其他操作而言,設(shè)備IO(文件、管道、套接字等)是比較慢的。于是在多線程結(jié)構(gòu)中就考慮到采用異步的方式進(jìn)行設(shè)備讀寫操作,即我們告訴系統(tǒng)對設(shè)備的讀寫數(shù)據(jù),而同時應(yīng)用程序的其他代碼繼續(xù)執(zhí)行,直到獲取設(shè)備操作完畢的系統(tǒng)通知。
在進(jìn)行異步IO時,我們先向系統(tǒng)發(fā)出IO請求,操作系統(tǒng)隊列化各種IO請求,并在內(nèi)部完成操作,當(dāng)系統(tǒng)在處理IO請求時,我們的線程可以返回繼續(xù)執(zhí)行,當(dāng)操作系統(tǒng)處理完IO請求之后,通知我們數(shù)據(jù)操作(發(fā)送、接收、出錯)完畢。
Windows提供了四種異步IO技術(shù),機制幾乎時相同的,區(qū)別在于通知結(jié)果的方式不同:
1、使一個設(shè)備內(nèi)核對象變?yōu)橛行盘?/p>
Windows將設(shè)備句柄看作可同步的對象,即它可以處于有信號或處于無信號狀態(tài),當(dāng)創(chuàng)建設(shè)備句柄、以異步的方式發(fā)送IO請求時,該句柄處于無信號狀態(tài),當(dāng)異步IO完成之后,該句柄受信,通過WaitForSingleobject或WatiForMultipleObjects函數(shù)可以判斷設(shè)備操作合適完成。該技術(shù)只能用于一個設(shè)備只發(fā)送一個IO請求,否則,若一個設(shè)備對應(yīng)多個操作,當(dāng)句柄受信時無法判斷是該設(shè)備的那個操作完成。
2、使一個事件內(nèi)核對象變?yōu)橛行盘?/p>
針對每個I/O操作綁定一個內(nèi)核事件對象,并將等待事件等待函數(shù)等待該事件的受信,當(dāng)I/O操作完成后系統(tǒng)使得與該操作綁定的事件受信,從而判斷那個操作完成。該技術(shù)解決了使一個設(shè)備內(nèi)核對象變?yōu)橛行盘柤夹g(shù)中一個設(shè)備只能對應(yīng)一個操作的不足。
3、警告I/O
在該技術(shù)中,當(dāng)發(fā)出設(shè)備IO請求時,同時要求我們傳遞一個被稱為完成例程的回調(diào)函數(shù),當(dāng)IO請求完成時調(diào)用該回調(diào)函數(shù)完成我們需要處理的工作。該技術(shù)允許單個設(shè)備同時進(jìn)行多個I/O請求。
4、完成端口
完成端口技術(shù)多用于處理大規(guī)模的請求,通過內(nèi)在的進(jìn)程池技術(shù)可以達(dá)到很高的性能,此時暫不做深入討論,若預(yù)知后事如何,請自己看,或等下回完成端口部分分解。
好,至此,刀磨的差不多了,估計也飄了~~~~,干正事了。
重疊IO模型-網(wǎng)絡(luò)編程中的重疊IO模型理論 ??
在編程中可以有兩種方法管理I/O請求:
1、事件對象通知(eventobjectnotification)—對應(yīng)上面的2
2、完成例程(completionroutines)——對應(yīng)上面的3
這里只討論第一種情況,具體思路就是正對每個套接字的每個操作綁定一個事件,然后將所有的事件組成事件數(shù)據(jù),運用事件等待函數(shù)等待事件的發(fā)生,并根據(jù)事件與操作的對應(yīng)關(guān)系對與之對應(yīng)的操作進(jìn)行處理。
下面說一下實際編程中所用到的數(shù)據(jù)結(jié)構(gòu)及函數(shù)。
1、WSAOVERLAPPED結(jié)構(gòu)
這個結(jié)構(gòu)自然是重疊模型里的核心,用于綁定套接字、操作以及操作對應(yīng)的事件。
typedefstruct_WSAOVERLAPPED{
DWORDInternal;
DWORDInternalHigh;
DWORDOffset;//文件操作中想要開始的低偏移量,網(wǎng)絡(luò)中不用
DWORDOffsetHigh;//文件操作中想要開始的高偏移量,網(wǎng)絡(luò)中不用
WSAEVENThEvent;//網(wǎng)絡(luò)編程中唯一需要關(guān)注的參數(shù),用來關(guān)聯(lián)WSAEvent對象
}WSAOVERLAPPED,*LPWSAOVERLAPPED;
2.WSARecv系列函數(shù)
在重疊模型中,發(fā)送接收等函數(shù)均由WSASend、WSARecv、WSASendTo、WSARecvFrom等函數(shù)代替,通過該函數(shù)將I/O操作與WSAOVERLAPPED結(jié)構(gòu)綁定起來,也既是與一個特定的事件綁定在一起,當(dāng)系統(tǒng)完成操作之后與該操作綁定的事件受信。
intWSARecv(
SOCKETs,//當(dāng)然是投遞這個操作的套接字
LPWSABUFlpBuffers,//接收緩沖區(qū),是WSABUF結(jié)構(gòu)構(gòu)成的數(shù)組
DWORDdwBufferCount,//數(shù)組中WSABUF結(jié)構(gòu)的數(shù)量
LPDWORDlpNumberOfBytesRecvd,//如果接收操作立即完成,返回函數(shù)調(diào)用所接收到的字節(jié)數(shù)
LPDWORDlpFlags,//說來話長了,我們這里設(shè)置為0即可
LPWSAOVERLAPPEDlpOverlapped,//“綁定”的重疊結(jié)構(gòu)
LPWSAOVERLAPPED_COMPLETION_ROUTINElpCompletionRoutine);//完成例程中將會用到的參數(shù),我們這里設(shè)置為NULL
返回值:
WSA_IO_PENDING:最常見的返回值,這是說明我們的WSARecv操作成功了,但是I/O操作還沒有完成,所以我們就需要綁定一個事件來通知我們操作何時完成
3、WSAWaitForMultipleEvents函數(shù)
該函數(shù)類似于線程中常用的WaitForMultipleObjects函數(shù),都是在等待事件的觸發(fā)。我們將WSARecv等操作上綁定的LPWSAOVERLAPPED數(shù)據(jù)結(jié)構(gòu)中的事件組稱事件數(shù)據(jù),在該函數(shù)上等待。
DWORDWSAWaitForMultipleEvents(
DWORDcEvents,//等候事件的總數(shù)量
constWSAEVENT*lphEvents,//事件數(shù)組的指針
BOOLfWaitAll,//當(dāng)設(shè)置為TRUE,事件數(shù)組中所有事件被傳信的時候函數(shù)才會返回
//FALSE則任何一個事件被傳信函數(shù)都要返回,此時設(shè)為FALSE
DWORDdwTimeout,//超時時間,如果超時,函數(shù)會返回WSA_WAIT_TIMEOUT
//如果設(shè)置為0,函數(shù)會立即返回
BOOLfAlertable);//在完成例程中會用到這個參數(shù),這里我們先設(shè)置為FALSE
返回值:
WSA_WAIT_TIMEOUT:最常見的返回值,等待超時,我們需要做的就是繼續(xù)Wait
WSA_WAIT_FAILED:出現(xiàn)了錯誤,請檢查cEvents和lphEvents兩個參數(shù)是否有效
如果事件數(shù)組中有某一個事件受信了,函數(shù)會返回這個事件的索引值,但是這個索引值需要減去預(yù)定義值WSA_WAIT_EVENT_0才是這個事件在事件數(shù)組中的位置。
注:WSAWaitForMultipleEvents函數(shù)只能支持由WSA_MAXIMUM_WAIT_EVENTS對象定義的一個最大值64,就是說WSAWaitForMultipleEvents只能等待64個事件,如果想同時等待多于64個事件,就要創(chuàng)建額外的工作者線程,就需要通過線程池管理了。
4、WSAGetOverlappedResult函數(shù)
我們利用該函數(shù)來查詢重疊操作的結(jié)果,定義如下:
BOOLWSAGetOverlappedResult(
SOCKETs,//SOCKET,需要操作的套接字
LPWSAOVERLAPPEDlpOverlapped,//我們想要查詢結(jié)果的那個重疊結(jié)構(gòu)的指針
LPDWORDlpcbTransfer,//本次重疊操作的實際接收(或發(fā)送)的字節(jié)數(shù)
BOOLfWait,//設(shè)置為TRUE,除非重疊操作完成,否則函數(shù)不會返回
//設(shè)置FALSE,而且操作仍處于掛起狀態(tài),那么函數(shù)就會返回FALSE
//此處采用的是設(shè)備內(nèi)核對象受信方式,等待套接字受信。
LPDWORDlpdwFlags);//指向DWORD的指針,負(fù)責(zé)接收結(jié)果標(biāo)志
注:如果WSAGetOverlappedResult完成以后,第三個參數(shù)返回是0,則說明通信對方已經(jīng)關(guān)閉連接,我們這邊的SOCKET,Event之類的也就可以關(guān)閉了。
重疊IO模型-編程步驟 ??
1、創(chuàng)建一個套接字,開始在指定的端口上監(jiān)聽連接請求。
2、接收一個入站的連接請求。
3、為接受的套接字創(chuàng)建新的WSAOVERLAPPED結(jié)構(gòu),并分配事件對象句柄。
4、以WSAOVERLAPPED結(jié)構(gòu)為參數(shù),在套接字上投遞WSARecv調(diào)用。
5、將所有接受套接字的事件組建事件數(shù)組,并調(diào)用WSAWaitForMultipleEvents函數(shù),等待與重疊調(diào)用關(guān)聯(lián)在一起的事件受信。
6、使用WSAGetOverlappedResult函數(shù),判斷重疊調(diào)用的返回狀態(tài)。
7、重新組建事件數(shù)組。
8、在套接字上重投遞WSARecv請求。
9、重復(fù)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;
}
/******************************************************************************
*數(shù)據(jù)結(jié)構(gòu)名稱:OverObject
*功能:記錄一個套接字的一個操作、一個事件和一個重疊I/O的關(guān)聯(lián)
*****************************************************************************/
classOverObject
{
public:
SOCKETm_sock;/*綁定的套接字*/
OVERLAPPEDm_overlapped;/*綁定的重疊I/O*/
char*m_buf;/*用于存放數(shù)據(jù)的緩沖區(qū)*/
intm_len;/*緩沖區(qū)的長度*/
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;
}
/********************************************************************************
*函數(shù)介紹:本函數(shù)是OverObject類的復(fù)制構(gòu)造函數(shù)。
*********************************************************************************/
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;
}
/********************************************************************************
*函數(shù)介紹:本函數(shù)是OverObject類帶參數(shù)的構(gòu)造函數(shù)。
*********************************************************************************/
OverObject::OverObject(SOCKETmySock,intmyLen)
{
m_sock=mySock;
m_buf=newchar[myLen];
m_len=myLen;
m_overlapped.hEvent=::WSACreateEvent();
}
/********************************************************************************
*函數(shù)介紹:本函數(shù)是OverObject類的==運算符重載函數(shù)
*********************************************************************************/
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;/*需要維護(hù)的所有重疊I/O*/
vectoreventArray;/*所有重疊I/O所對應(yīng)的事件組成的數(shù)組,作為等待函數(shù)的參數(shù)*/
vectorsockArray;/*需要維護(hù)的所有套接字,當(dāng)操作為零時關(guān)閉套接字*/
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();
};
/********************************************************************************
*函數(shù)介紹:創(chuàng)建重疊對象的類對象,并插入重疊對象鏈表中。
*********************************************************************************/
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;
}
/********************************************************************************
*函數(shù)介紹:釋放重疊對象鏈表中指定的重疊對象。
*********************************************************************************/
voidOverlapped::FreeOverObject(list::iteratormyPtrOverObject)
{
overObjects.erase(myPtrOverObject);
}
/********************************************************************************
*函數(shù)介紹:從重疊對象列表中查找指定事件所對應(yīng)的重疊對象。
*********************************************************************************/
list::iteratorOverlapped::FindOverObject(HANDLEmyEvent)
{
list::iteratorlocalIerator;
for(localIerator=overObjects.begin();localIerator!=overObjects.end();localIerator++)
{
if(localIerator->m_overlapped.hEvent==myEvent)
{
break;
}
}
returnlocalIerator;
}
/********************************************************************************
*函數(shù)介紹:遍歷重疊對象列表,重建重疊對象列表所對應(yīng)的事件數(shù)組。
*********************************************************************************/
voidOverlapped::RebuildEventArray()
{
eventArray.clear();
list::iteratoroverObjIterator;
overObjIterator=overObjects.begin();
for(overObjIterator;overObjIterator!=overObjects.end();++overObjIterator)
{
eventArray.push_back(overObjIterator->m_overlapped.hEvent);
}
}
/********************************************************************************
*函數(shù)介紹:投放接受操作,即將指定套接字的Recv操作與重疊I/O對象關(guān)聯(lián)起來。
*********************************************************************************/
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;
}
/********************************************************************************
*函數(shù)介紹:投放發(fā)送操作,即將指定套接字的Send操作與重疊I/O對象關(guān)聯(lián)起來。
*********************************************************************************/
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;
}
/********************************************************************************
*函數(shù)介紹:創(chuàng)建accept函數(shù)完成事件,用于處理accepe后等待函數(shù)的等待事件句柄數(shù)組發(fā)
生變化,若此時無事件觸發(fā),等待事件句柄數(shù)組仍以原來的事件句柄數(shù)組為依
據(jù)等待。
*********************************************************************************/
voidOverlapped::CreateAcceptEvent()
{
//標(biāo)志套接字,用于觸發(fā)accept完成事件。
SOCKETmyClient=::WSASocket(AF_INET,SOCK_STREAM,IPPROTO_TCP,NULL,0,WSA_FLAG_OVERLAPPED);
list::iteratoracceptIterator=GetOverObject(myClient,512);
RebuildEventArray();
}
/********************************************************************************
*函數(shù)介紹:當(dāng)accept函數(shù)完成時,重置accept所對應(yīng)的事件,從而使得等待函數(shù)能夠在改
變后的事件句柄數(shù)組上等待。
*********************************************************************************/
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;
}
/********************************************************************************
*函數(shù)介紹:數(shù)據(jù)處理函數(shù),按照不同操作類型,處理數(shù)據(jù)的發(fā)送或接受。
*********************************************************************************/
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:/*接收數(shù)據(jù)完成*/
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;
}
}
/********************************************************************************
*函數(shù)介紹:服務(wù)線程函數(shù),平常處于等待狀態(tài),完成數(shù)據(jù)處理。
*********************************************************************************/
UINTWINAPIServerThread(PVOIDpvParam)
{
while(true)
{
intindex;
DWORDeventNum=(DWORD)(myOverlapped.eventArray.size());
index=::WSAWaitForMultipleEvents(eventNum,&(myOverlapped.eventArray[0]),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;
}
?
總結(jié)
以上是生活随笔為你收集整理的重叠IO模型-异步IO的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: HTTP 1.0 与 1.1比较
- 下一篇: 写在创业的路上:如何从无到有的打造一个产