生活随笔
收集整理的這篇文章主要介紹了
【网络编程】之十、重叠IO Overlapped IO
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
winsock2 中引入了重疊I/O(Overlapped I/O)的概念并且要求所有的傳輸協議提供者都支持這一功能。 ?他的功能高于前面我們提過的三種,但是最強悍的還是我們后面要說的完成端口。
基本原理:讓應用程序使用一個重疊的數據結構,一次投遞一個活多個winsock I/O請求,針對那些提交的清酒,在他們完成之后,應用程序可為他們提供服務。 ?應用程序可通過ReadFile和WriteFile兩個函數執行I/O操作。 要注意:重疊I/O僅能在由WSASocket函數打開的套接字上使用。要想在一個套接字上使用重疊I/O,首先必須使用 WSA_FLAG_OVERLAPPED 這個標志。
[cpp] ?view plaincopy
SOCKET?s?=?WSASocket(AF_INET,?SOCK_STREAM,?0,?NULL,?0,?WSA_FLAG_OVERLAPPED);??
當你創建套接字的時候,你使用socket函數,她會默認設置WSA_FLAG_OVERLAPPED 標志。
ok,現在我們已經成功的建立了一個套接字,同時將其他與一個本地接口綁定到一起后就可以開始進行重疊I/O操作了。在這里,我們以前用的send,recv等函數將被改變成為WSASend,WSARecv。
來看一下這些函數:
?WSASend ? ? ? ? ? ? //?sends data on a connected socket. 提供一個指向已填充的數據緩沖區的指針。 ?WSASendTo ? ? ? ?//?sends data to a specific destination, using overlapped I/O where applicable. ?WSARecv ? ? ? ?//?receives data from a connected socket. ?WSARecvFrom ? ? ?//receives data on a socket and stores the source address. ?提供存放接收數據的緩沖區 ?WSAIoctl ? ? ?//allows for miscellaneous control of a socket. ? 還可以使用重疊I/O操作的延遲完成特性。 ?AcceptEx ? //accepts a new connection, returns the local and remote address, and receives the first block of data sent by the client application. ?TrnasmitFile ? //transmits file data over a connected socket handle.?uses the operating system's cache manager to retrieve the file data, and provides high-performance file data transfer over sockets.
這里面WS_IO_PENDING 是最常見的返回值,這是說明我們的重疊函數調用成功了,但是I/O操作還沒有完成。
如果和一個WSAOVERLAPPED結構一起來調用這些函數,那么函數會立即完成并返回,無論套接字是否是阻塞模式。判斷I/O請求是否成功的方法有兩個,分別是:
1、等待 ? 事件對象通知。
2、通過 完成例程。
我們談第一個,事件通知:
在重疊函數的參數中都有一個參數是:Overlapped,我們可以假設是把我們的WSARecv這樣的操作“綁定”到這個重疊結構上,提交一個請求,而不是將錯做立即完成,其他的事情交給重疊結構去做,而其中的重疊結構又要與windows的事件對象“綁定”到一起,這樣我們調用玩WSARecv以后就OK了,等到重疊操作完成后自然會有與之對應的時間來通知我們操作完成了,然后我們就可以來根據重疊操作的結果取得我們想要的數據了。
重疊I/O的事件通知方法要求將win32事件對象與WSOVERLAPPED結構關聯在一起,當I/O操作完成后,時間的狀態會變成“已傳信”狀態,也就是激發狀態。
來看一下WSAOVERLAPPED結構:
[cpp] ?view plaincopy
typedef ? struct ?_WSAOVERLAPPED?{?? ????DWORD ????Internal;?? ????DWORD ????InternalHigh;?? ????DWORD ????Offset;?? ????DWORD ????OffsetHigh; ?? ????WSAEVENT?hEvent;?? }?WSAOVERLAPPED,?FAR?*?LPWSAOVERLAPPED;?? ?? typedef ? struct ?_WSAOVERLAPPED?{?? ??ULONG_PTR ?Internal;?? ??ULONG_PTR ?InternalHigh;?? ??union ?{?? ?????struct ?{?? ???????DWORD ?Offset;?? ???????DWORD ?OffsetHigh;?? ?????};????PVOID ?Pointer;?? ??};?? ??HANDLE ?hEvent;?? }?WSAOVERLAPPED,??*LPWSAOVERLAPPED;??
當重疊I/O請求完成后,應用程序要負責取回重疊I/O操作的結果,一個重疊請求操作最終完成后,在事件通知方法中,winsock會更改與一個WSAOVERLAPPED結構對應的一個事件對象的事件傳信狀態, 將它從“未傳信”變為“已傳信”。 ? ?由于一個事件對象已分配給WSAOVERLAPPED結構,所以只需要簡單滴調用WSAWaitForMultipleEvents函數,從而判斷出一個重疊I/O調用在什么時候完成。
還有一個函數是取得重疊結構,他是WSAGetOverlappedResult函數,他是在發現一次重疊請求完成后才可以執行。用來判斷這個重疊調用到底是成功還是失敗。
[cpp] ?view plaincopy
BOOL ?WSAAPI?WSAGetOverlappedResult(?? ??__in??????????SOCKET?s,?? ??__in??????????LPWSAOVERLAPPED?lpOverlapped,?? ??__out?????????LPDWORD ?lpcbTransfer, ?? ??__in??????????BOOL ?fWait, ?? ??__out?????????LPDWORD ?lpdwFlags ?? );??
當函數調用成功那么就會返回TRUE, 表示重疊操作成功,并且lpcbTransfer參數指向的值已進行了更新。 ?
如果反回了FALSE,那么可能是由于某種原因造成了錯誤:
1、重疊I/O已經完成,但是又錯誤;
2、重疊I/O操作仍處于待解決狀態;
3、重疊操作的完成狀態不能判斷,因為函數的參數存在錯誤。
當函數失敗后,lpcbTransfer參數指向的值不會進行更新,而且我們的應用程序應調用WSAGerLastError函數來調查到底是什么原因造成的。
OK,下面來看一下重疊I/O的編程步驟:
1、創建套接字,開始在指定的端口上監聽連接請求。
[cpp] ?view plaincopy
SOCKET ?ListenSocket, ? ?? AcceptSocket; ??? WSAOVERLAPPED ?AcceptOverlapped; ??? WSABUF ?DataBuf[DATA_BUFSIZE]?; ? ??? WSADATA?wsaData;?? WSAStartup(MAKEWORD(2,2),&wsaData);?? ?? ListenSocket?=?socket(AF_INET,SOCK_STREAM,IPPROTO_TCP); ??? ?? SOCKADDR_IN?ServerAddr; ??? ServerAddr.sin_family=AF_INET; ??? ServerAddr.sin_addr.S_un.S_addr ?=htonl(INADDR_ANY); ??? ServerAddr.sin_port=htons(8888);?? ?? bind(ListenSocket,(LPSOCKADDR)&ServerAddr,?sizeof (ServerAddr));? ?? ?? listen(ListenSocket,?5); ??
2、接受一個客戶端進入的連接請求。
[cpp] ?view plaincopy
AcceptSocket?=?accept?(ListenSocket,?NULL,NULL)?;??? 當然,這里是我偷懶,如果想要獲得連入客戶端的信息(記得論壇上也常有人問到),accept的后兩個參數就不要用NULL,而是這樣?? SOCKADDR_IN?ClientAddr; ??? int ?addr_length= sizeof (ClientAddr);?? AcceptSocket?=?accept(ListenSocket,(SOCKADDR*)&ClientAddr,?&addr_length);?? ?? LPCTSTR ?lpIP?= ?inet_ntoa(ClientAddr.sin_addr); ? ?? UINT ?nPort?=?ClientAddr.sin_port; ? ??
3、為接受的套接字創建一個WSAOVERLAPPED結構,并給這個結構分配一個事件對象句柄,同時將該事件對象句柄分配給一個事件數組,以便稍后WSAWaitForMultipleEvents函數使用。
[cpp] ?view plaincopy
WSAEVENT ?EventArray[WSA_MAXIMUM_WAIT_EVENTS]; ??? DWORD ?dwEventTotal?=?0, ? ?? dwRecvBytes?=?0, ??? Flags?=?0; ??? ?? #define?DATA_BUFSIZE ?4096 ?//?接收緩沖區大小 ??
[cpp] ?view plaincopy
?? ?? EventArray[dwEventTotal]?=?WSACreateEvent(); ??? ?? ZeroMemory(&AcceptOverlapped,?sizeof (WSAOVERLAPPED)); ? ?? AcceptOverlapped.hEvent?=?EventArray[dwEventTotal]; ??? ?? char ?buffer[DATA_BUFSIZE];?? ZeroMemory(buffer,?DATA_BUFSIZE);?? DataBuf.len?=?DATA_BUFSIZE;?? DataBuf.buf?=?buffer; ??? dwEventTotal?++; ???
4、在套接字上投遞一個異步WSARecv請求,指定參數為WSAOVERLAPPED結構,注意函數通常會以失敗告終,返回SOCKET_ERROR錯誤狀態WSA_IO_PENDING(I/O操作沒有完成)。
[cpp] ?view plaincopy
if (WSARecv(AcceptSocket?,&DataBuf,1,&dwRecvBytes,&Flags,?? &?AcceptOverlapped,?NULL)?==?SOCKET_ERROR)?? {??? ?? ?? if (WSAGetLastError()?!=?WSA_IO_PENDING) ??? {?? ?? closesocket(AcceptSocket);?? WSACloseEvent(EventArray[dwEventTotal]);?? }?? }??
5、使用步驟3的事件數組,調用WSAWaitForMultipleEvents函數,并等待與重疊調用關聯在一起的事件進入“已傳信”狀態。
6、WSAWaitForMultipleEvents函數返回后,針對“已傳信”狀態的事件,調用WSAResultEvent函數來重置事件,從而重設事件對象,并對完成的重疊請求進行處理。
[cpp] ?view plaincopy
WSAResetEvent(EventArray[dwIndex]);??
[cpp] ?view plaincopy
DWORD ?dwIndex;?? ?? ?? dwIndex?=?WSAWaitForMultipleEvents(dwEventTotal,??? EventArray?,FALSE?,WSA_INFINITE,FALSE);?? ?? dwIndex?=?dwIndex?–?WSA_WAIT_EVENT_0;??
7、使用WSAGetOverlappedResult函數來判斷重疊調用的返回狀態是什么。
[cpp] ?view plaincopy
DWORD ?dwBytesTransferred;?? WSAGetOverlappedResult(?AcceptSocket,?AcceptOverlapped?,?? &dwBytesTransferred,?FALSE,?&Flags);?? ?? ?? if (dwBytesTransferred?==?0)?? {?? closesocket(AcceptSocket);?? WSACloseEvent(EventArray[dwIndex]); ??? return ;?? }??
8、在套接字上投遞另一個重疊WSARecv請求。
9、重復5到8。
在上文中我們提到一個AcceptEx函數,這個函數是在重疊I/O模型中允許以一種重疊方式,事件對客戶端連接。他位于Mswsock.h頭文件以及Mswsock.lib庫文件中。
這個函數和accept的區別是:我們必須提供接受的套接字,而不是讓函數自動為我們創建。
[cpp] ?view plaincopy
BOOL ?AcceptEx(?? ??__in??????????SOCKET?sListenSocket,?? ??__in??????????SOCKET?sAcceptSocket,?? ??__in??????????PVOID ?lpOutputBuffer, ?? ??__in??????????DWORD ?dwReceiveDataLength, ?? ??__in??????????DWORD ?dwLocalAddressLength, ?? ??__in??????????DWORD ?dwRemoteAddressLength, ?? ??__out?????????LPDWORD ?lpdwBytesReceived, ?? ??__in??????????LPOVERLAPPED?lpOverlapped?? );??
要知道AcceptEx函數只能由這里給大家說的“事件通知”方式獲取異步I/O請求的結果,在"完成例程”中是無法使用的。
下面給出win32重疊io,來讀取文件:
[cpp] ?view plaincopy
char ?buf[512*10000];?? int ?readdata( void )?? {?? ????BOOL ?bRet;??? ?? ????HANDLE ?hFile;??? ?? ????DWORD ?numRead;??? ?? ????OVERLAPPED?overlapped;????? ?????? ????hFile?=?CreateFile("..\\se.zip" ,?GENERIC_READ,?FILE_SHARE_READ|FILE_SHARE_WRITE,?NULL,?OPEN_EXISTING,??? ???????????????????????FILE_FLAG_OVERLAPPEN,?NULL);?? ?????? ????if ?(INVALID_HANDLE_VALUE?==?hFile)?? ????{?? ????????return ?-1;?? ????}?? ?????? ????memset(buf,?0,?sizeof ( char )?*?512*10000);??? ?? ????memset(&overlapped,?0,?sizeof (overlapped));??? ?? ????overlapped.Offset?=?0;?????? ?????? ????bRet?=?ReadFile(hFile,?buf,?512*10000,?&numRead,?&overlapped);?? ?????? ????if (TRUE?==?bRet)?? ?? ????{?? ?????????? ????}?? ????else ?? ?? ????{?? ????????if ?(ERROR_IO_PENDING?==?GetLastError())?? ?? ????????{?? ????????????WaitForSingleObject(hFile,?INFINITE);????? ?????????????? ????????????bRet?=?GetOverlappedResult(hFile,?&overlapped,?&numRead,?TRUE);?? ????????????if (TRUE?==?bRet)?? ????????????{?? ????????????????? ????????????????? ????????????}?? ????????????else ?? ????????????{?? ?????????????? ????????????}?? ????????}?? ????????else ?? ????????{?? ?????????? ????????}?? ????}?? ????CloseHandle(hFile);?? }?? ?? }??
下面將講述完成端口,那個更加高效,但是也更加困難;
2012/9/2
jofranks 于南昌
總結
以上是生活随笔 為你收集整理的【网络编程】之十、重叠IO Overlapped IO 的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔 網站內容還不錯,歡迎將生活随笔 推薦給好友。