日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

【网络编程】之十、重叠IO Overlapped IO

發(fā)布時(shí)間:2024/4/11 编程问答 36 豆豆
生活随笔 收集整理的這篇文章主要介紹了 【网络编程】之十、重叠IO Overlapped IO 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

winsock2 中引入了重疊I/O(Overlapped I/O)的概念并且要求所有的傳輸協(xié)議提供者都支持這一功能。 ?他的功能高于前面我們提過的三種,但是最強(qiáng)悍的還是我們后面要說的完成端口。

基本原理:讓應(yīng)用程序使用一個(gè)重疊的數(shù)據(jù)結(jié)構(gòu),一次投遞一個(gè)活多個(gè)winsock I/O請求,針對那些提交的清酒,在他們完成之后,應(yīng)用程序可為他們提供服務(wù)。 ?應(yīng)用程序可通過ReadFile和WriteFile兩個(gè)函數(shù)執(zhí)行I/O操作。 要注意:重疊I/O僅能在由WSASocket函數(shù)打開的套接字上使用。要想在一個(gè)套接字上使用重疊I/O,首先必須使用 WSA_FLAG_OVERLAPPED 這個(gè)標(biāo)志。

[cpp]?view plaincopy
  • SOCKET?s?=?WSASocket(AF_INET,?SOCK_STREAM,?0,?NULL,?0,?WSA_FLAG_OVERLAPPED);??
  • 當(dāng)你創(chuàng)建套接字的時(shí)候,你使用socket函數(shù),她會(huì)默認(rèn)設(shè)置WSA_FLAG_OVERLAPPED 標(biāo)志。


    ok,現(xiàn)在我們已經(jīng)成功的建立了一個(gè)套接字,同時(shí)將其他與一個(gè)本地接口綁定到一起后就可以開始進(jìn)行重疊I/O操作了。在這里,我們以前用的send,recv等函數(shù)將被改變成為WSASend,WSARecv。

    來看一下這些函數(shù):

    ?WSASend ? ? ? ? ? ? //?sends data on a connected socket. 提供一個(gè)指向已填充的數(shù)據(jù)緩沖區(qū)的指針。
    ?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. ?提供存放接收數(shù)據(jù)的緩沖區(qū)
    ?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 是最常見的返回值,這是說明我們的重疊函數(shù)調(diào)用成功了,但是I/O操作還沒有完成。



    如果和一個(gè)WSAOVERLAPPED結(jié)構(gòu)一起來調(diào)用這些函數(shù),那么函數(shù)會(huì)立即完成并返回,無論套接字是否是阻塞模式。判斷I/O請求是否成功的方法有兩個(gè),分別是:

    1、等待 ? 事件對象通知。

    2、通過 完成例程。


    我們談第一個(gè),事件通知:

    在重疊函數(shù)的參數(shù)中都有一個(gè)參數(shù)是:Overlapped,我們可以假設(shè)是把我們的WSARecv這樣的操作“綁定”到這個(gè)重疊結(jié)構(gòu)上,提交一個(gè)請求,而不是將錯(cuò)做立即完成,其他的事情交給重疊結(jié)構(gòu)去做,而其中的重疊結(jié)構(gòu)又要與windows的事件對象“綁定”到一起,這樣我們調(diào)用玩WSARecv以后就OK了,等到重疊操作完成后自然會(huì)有與之對應(yīng)的時(shí)間來通知我們操作完成了,然后我們就可以來根據(jù)重疊操作的結(jié)果取得我們想要的數(shù)據(jù)了。


    重疊I/O的事件通知方法要求將win32事件對象與WSOVERLAPPED結(jié)構(gòu)關(guān)聯(lián)在一起,當(dāng)I/O操作完成后,時(shí)間的狀態(tài)會(huì)變成“已傳信”狀態(tài),也就是激發(fā)狀態(tài)。

    來看一下WSAOVERLAPPED結(jié)構(gòu):

    [cpp]?view plaincopy
  • typedef?struct?_WSAOVERLAPPED?{??
  • ????DWORD????Internal;??
  • ????DWORD????InternalHigh;??
  • ????DWORD????Offset;??
  • ????DWORD????OffsetHigh;//上面參數(shù)都是由系統(tǒng)在內(nèi)部使用,不是由應(yīng)用程序直接進(jìn)行處理或使用。??
  • ????WSAEVENT?hEvent;//允許應(yīng)用程序?qū)⒁粋€(gè)事件對象句柄同一個(gè)套接字關(guān)聯(lián)起來。??
  • }?WSAOVERLAPPED,?FAR?*?LPWSAOVERLAPPED;??
  • ??
  • typedef?struct?_WSAOVERLAPPED?{??
  • ??ULONG_PTR?Internal;??
  • ??ULONG_PTR?InternalHigh;??
  • ??union?{??
  • ?????struct?{??
  • ???????DWORD?Offset;??
  • ???????DWORD?OffsetHigh;??
  • ?????};????PVOID?Pointer;??
  • ??};??
  • ??HANDLE?hEvent;??
  • }?WSAOVERLAPPED,??*LPWSAOVERLAPPED;??

  • 當(dāng)重疊I/O請求完成后,應(yīng)用程序要負(fù)責(zé)取回重疊I/O操作的結(jié)果,一個(gè)重疊請求操作最終完成后,在事件通知方法中,winsock會(huì)更改與一個(gè)WSAOVERLAPPED結(jié)構(gòu)對應(yīng)的一個(gè)事件對象的事件傳信狀態(tài), 將它從“未傳信”變?yōu)椤耙褌餍拧薄?? ?由于一個(gè)事件對象已分配給WSAOVERLAPPED結(jié)構(gòu),所以只需要簡單滴調(diào)用WSAWaitForMultipleEvents函數(shù),從而判斷出一個(gè)重疊I/O調(diào)用在什么時(shí)候完成。


    還有一個(gè)函數(shù)是取得重疊結(jié)構(gòu),他是WSAGetOverlappedResult函數(shù),他是在發(fā)現(xiàn)一次重疊請求完成后才可以執(zhí)行。用來判斷這個(gè)重疊調(diào)用到底是成功還是失敗。

    [cpp]?view plaincopy
  • BOOL?WSAAPI?WSAGetOverlappedResult(??
  • ??__in??????????SOCKET?s,//指定在重疊操作開始的時(shí)候,與之對應(yīng)的那個(gè)套接字??
  • ??__in??????????LPWSAOVERLAPPED?lpOverlapped,//一個(gè)指針,對應(yīng)于在重疊操作開始時(shí)。??
  • ??__out?????????LPDWORD?lpcbTransfer,//一個(gè)指針,對應(yīng)一個(gè)DWORD變量,負(fù)責(zé)接收一次重疊發(fā)送/接收操作時(shí)機(jī)傳輸?shù)淖止?jié)數(shù)。??
  • ??__in??????????BOOL?fWait,//用于決定函數(shù)是否應(yīng)該等待一次待解決的重疊操作完成。TRUE:除非操作完成,否則函數(shù)不會(huì)返回。?FALSE:并且函數(shù)處于待解決狀態(tài),那么函數(shù)返回FALSE,同時(shí)返回WSAIOINCOMPLETE錯(cuò)誤。???目前,這個(gè)參數(shù)無論設(shè)置什么都沒有任何效果。??
  • ??__out?????????LPDWORD?lpdwFlags//一個(gè)指針,指向DWORD負(fù)責(zé)接收結(jié)果標(biāo)志。??
  • );??
  • 當(dāng)函數(shù)調(diào)用成功那么就會(huì)返回TRUE, 表示重疊操作成功,并且lpcbTransfer參數(shù)指向的值已進(jìn)行了更新。 ?

    如果反回了FALSE,那么可能是由于某種原因造成了錯(cuò)誤:

    1、重疊I/O已經(jīng)完成,但是又錯(cuò)誤;

    2、重疊I/O操作仍處于待解決狀態(tài);

    3、重疊操作的完成狀態(tài)不能判斷,因?yàn)楹瘮?shù)的參數(shù)存在錯(cuò)誤。

    當(dāng)函數(shù)失敗后,lpcbTransfer參數(shù)指向的值不會(huì)進(jìn)行更新,而且我們的應(yīng)用程序應(yīng)調(diào)用WSAGerLastError函數(shù)來調(diào)查到底是什么原因造成的。


    OK,下面來看一下重疊I/O的編程步驟:

    1、創(chuàng)建套接字,開始在指定的端口上監(jiān)聽連接請求。

    [cpp]?view plaincopy
  • SOCKET        ?ListenSocket,            ?//?監(jiān)聽套接字??
  • AcceptSocket;            ?//?與客戶端通信的套接字??
  • WSAOVERLAPPED ?AcceptOverlapped;    ?//?重疊結(jié)構(gòu)一個(gè)??
  • WSABUF    ?DataBuf[DATA_BUFSIZE]?;   ?  ???
  • WSADATA?wsaData;??
  • WSAStartup(MAKEWORD(2,2),&wsaData);??
  • ??
  • ListenSocket?=?socket(AF_INET,SOCK_STREAM,IPPROTO_TCP); ?//創(chuàng)建TCP套接字??
  • ??
  • SOCKADDR_IN?ServerAddr;                          ?//分配端口及協(xié)議族并綁定??
  • 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、接受一個(gè)客戶端進(jìn)入的連接請求。

    [cpp]?view plaincopy
  • AcceptSocket?=?accept?(ListenSocket,?NULL,NULL)?;???
  • 當(dāng)然,這里是我偷懶,如果想要獲得連入客戶端的信息(記得論壇上也常有人問到),accept的后兩個(gè)參數(shù)就不要用NULL,而是這樣??
  • SOCKADDR_IN?ClientAddr;                  ?//?定義一個(gè)客戶端得地址結(jié)構(gòu)作為參數(shù)??
  • int?addr_length=sizeof(ClientAddr);??
  • AcceptSocket?=?accept(ListenSocket,(SOCKADDR*)&ClientAddr,?&addr_length);??
  • //?于是乎,我們就可以輕松得知連入客戶端的信息了??
  • LPCTSTR?lpIP?= ?inet_ntoa(ClientAddr.sin_addr);     ?//?IP??
  • UINT?nPort?=?ClientAddr.sin_port;                     ?//?Port??

  • 3、為接受的套接字創(chuàng)建一個(gè)WSAOVERLAPPED結(jié)構(gòu),并給這個(gè)結(jié)構(gòu)分配一個(gè)事件對象句柄,同時(shí)將該事件對象句柄分配給一個(gè)事件數(shù)組,以便稍后WSAWaitForMultipleEvents函數(shù)使用。

    [cpp]?view plaincopy
  • WSAEVENT ?EventArray[WSA_MAXIMUM_WAIT_EVENTS]; ???
  • DWORD    ?dwEventTotal?=?0,           ?//?程序中事件的總數(shù)??
  • dwRecvBytes?=?0,           ?//?接收到的字符長度??
  • Flags?=?0;                   ?//?WSARecv的參數(shù)??
  • ??
  • #define?DATA_BUFSIZE    ?4096         ?//?接收緩沖區(qū)大小??
  • [cpp]?view plaincopy
  • //?創(chuàng)建一個(gè)事件??
  • //?dwEventTotal可以暫時(shí)先作為Event數(shù)組的索引??
  • EventArray[dwEventTotal]?=?WSACreateEvent();     ???
  • ??
  • ZeroMemory(&AcceptOverlapped,?sizeof(WSAOVERLAPPED));     ?//?置零??
  • AcceptOverlapped.hEvent?=?EventArray[dwEventTotal];           ?//?關(guān)聯(lián)事件??
  • ??
  • char?buffer[DATA_BUFSIZE];??
  • ZeroMemory(buffer,?DATA_BUFSIZE);??
  • DataBuf.len?=?DATA_BUFSIZE;??
  • DataBuf.buf?=?buffer;                         ?//?初始化一個(gè)WSABUF結(jié)構(gòu)??
  • dwEventTotal?++;                             ?//?總數(shù)加一??
  • 4、在套接字上投遞一個(gè)異步WSARecv請求,指定參數(shù)為WSAOVERLAPPED結(jié)構(gòu),注意函數(shù)通常會(huì)以失敗告終,返回SOCKET_ERROR錯(cuò)誤狀態(tài)WSA_IO_PENDING(I/O操作沒有完成)。

    [cpp]?view plaincopy
  • if(WSARecv(AcceptSocket?,&DataBuf,1,&dwRecvBytes,&Flags,??
  • &?AcceptOverlapped,?NULL)?==?SOCKET_ERROR)??
  • {???
  • //?返回WSA_IO_PENDING是正常情況,表示IO操作正在進(jìn)行,不能立即完成??
  • //?如果不是WSA_IO_PENDING錯(cuò)誤,就大事不好了~~~~~~!!!??
  • if(WSAGetLastError()?!=?WSA_IO_PENDING)   ???
  • {??
  • //?那就只能關(guān)閉大吉了??
  • closesocket(AcceptSocket);??
  • WSACloseEvent(EventArray[dwEventTotal]);??
  • }??
  • }??

  • 5、使用步驟3的事件數(shù)組,調(diào)用WSAWaitForMultipleEvents函數(shù),并等待與重疊調(diào)用關(guān)聯(lián)在一起的事件進(jìn)入“已傳信”狀態(tài)。

    6、WSAWaitForMultipleEvents函數(shù)返回后,針對“已傳信”狀態(tài)的事件,調(diào)用WSAResultEvent函數(shù)來重置事件,從而重設(shè)事件對象,并對完成的重疊請求進(jìn)行處理。

    [cpp]?view plaincopy
  • WSAResetEvent(EventArray[dwIndex]);??

  • [cpp]?view plaincopy
  • DWORD?dwIndex;??
  • //?等候重疊I/O調(diào)用結(jié)束??
  • //?因?yàn)槲覀儼咽录蚈verlapped綁定在一起,重疊操作完成后我們會(huì)接到事件通知??
  • dwIndex?=?WSAWaitForMultipleEvents(dwEventTotal,???
  • EventArray?,FALSE?,WSA_INFINITE,FALSE);??
  • //?注意這里返回的Index并非是事件在數(shù)組里的Index,而是需要減去WSA_WAIT_EVENT_0??
  • dwIndex?=?dwIndex?–?WSA_WAIT_EVENT_0;??


  • 7、使用WSAGetOverlappedResult函數(shù)來判斷重疊調(diào)用的返回狀態(tài)是什么。

    [cpp]?view plaincopy
  • DWORD?dwBytesTransferred;??
  • WSAGetOverlappedResult(?AcceptSocket,?AcceptOverlapped?,??
  • &dwBytesTransferred,?FALSE,?&Flags);??
  • //?先檢查通信對方是否已經(jīng)關(guān)閉連接??
  • //?如果==0則表示連接已經(jīng),則關(guān)閉套接字??
  • if(dwBytesTransferred?==?0)??
  • {??
  • closesocket(AcceptSocket);??
  • WSACloseEvent(EventArray[dwIndex]);   ?//?關(guān)閉事件??
  • return;??
  • }??


  • 8、在套接字上投遞另一個(gè)重疊WSARecv請求。

    9、重復(fù)5到8。


    在上文中我們提到一個(gè)AcceptEx函數(shù),這個(gè)函數(shù)是在重疊I/O模型中允許以一種重疊方式,事件對客戶端連接。他位于Mswsock.h頭文件以及Mswsock.lib庫文件中。

    這個(gè)函數(shù)和accept的區(qū)別是:我們必須提供接受的套接字,而不是讓函數(shù)自動(dòng)為我們創(chuàng)建。

    [cpp]?view plaincopy
  • BOOL?AcceptEx(??
  • ??__in??????????SOCKET?sListenSocket,//一個(gè)監(jiān)聽套接字??
  • ??__in??????????SOCKET?sAcceptSocket,//指定另一個(gè)套接字,負(fù)責(zé)對進(jìn)入連接請求的“接受”??
  • ??__in??????????PVOID?lpOutputBuffer,//指定一個(gè)特殊的緩沖區(qū),因?yàn)橐?fù)責(zé)三種數(shù)據(jù)的接收,服務(wù)器的本地地址,客戶端的遠(yuǎn)程地址和新建連接上發(fā)送的第一個(gè)數(shù)據(jù)塊。??
  • ??__in??????????DWORD?dwReceiveDataLength,//以字節(jié)為單位,指定在lpOutputBuffer緩沖區(qū)中,保留多大的空間來接收數(shù)據(jù)。??如果傳0,那么就不會(huì)再接收任何數(shù)據(jù)了。??
  • ??__in??????????DWORD?dwLocalAddressLength,//??
  • ??__in??????????DWORD?dwRemoteAddressLength,//和上一個(gè)參數(shù)以字節(jié)為單位指定在lpOutputBuffer緩沖區(qū)中,保留多大空間,在一個(gè)套接字被接受的時(shí)候,用于本地和遠(yuǎn)程地址信息的保存??
  • ??__out?????????LPDWORD?lpdwBytesReceived,//用于返回接收到的實(shí)際數(shù)據(jù)量,以字節(jié)為單位。只有在以同步方式完成的前提下,才會(huì)設(shè)置這個(gè)參數(shù)。??
  • ??__in??????????LPOVERLAPPED?lpOverlapped//對應(yīng)的是一個(gè)OVERLAPPED結(jié)構(gòu),允許AcceptEx以一種異步方式工作。?前面說過,只有在一個(gè)重疊I/O應(yīng)用中,這個(gè)函數(shù)才需要使用事件對象通知機(jī)制。??
  • );??
  • 要知道AcceptEx函數(shù)只能由這里給大家說的“事件通知”方式獲取異步I/O請求的結(jié)果,在"完成例程”中是無法使用的。


    下面給出win32重疊io,來讀取文件:

    [cpp]?view plaincopy
  • char?buf[512*10000];??
  • int?readdata(void)??
  • {??
  • ????BOOL?bRet;???//返回值??
  • ????HANDLE?hFile;???//文件指針??
  • ????DWORD?numRead;???//讀取數(shù)據(jù)長度??
  • ????OVERLAPPED?overlapped;???//I/O結(jié)構(gòu)??
  • ??????
  • ????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);???//初始化緩沖區(qū)??
  • ????memset(&overlapped,?0,?sizeof(overlapped));???//初始化overlapped??
  • ????overlapped.Offset?=?0;????//讀文件的位置??
  • ??????
  • ????bRet?=?ReadFile(hFile,?buf,?512*10000,?&numRead,?&overlapped);??
  • ??????
  • ????if(TRUE?==?bRet)??//讀取數(shù)據(jù)成功??
  • ????{??
  • ????????//讀取數(shù)據(jù)完畢??
  • ????}??
  • ????else??//重疊io操作??
  • ????{??
  • ????????if?(ERROR_IO_PENDING?==?GetLastError())??//讀取操作等待中??
  • ????????{??
  • ????????????WaitForSingleObject(hFile,?INFINITE);???//等待文件句柄被激活??
  • ????????????//讀取結(jié)果??
  • ????????????bRet?=?GetOverlappedResult(hFile,?&overlapped,?&numRead,?TRUE);??
  • ????????????if(TRUE?==?bRet)??
  • ????????????{??
  • ???????????????//讀取數(shù)據(jù)完畢??
  • ???????????????//處理數(shù)據(jù)??
  • ????????????}??
  • ????????????else??
  • ????????????{??
  • ????????????//處理錯(cuò)誤??
  • ????????????}??
  • ????????}??
  • ????????else??
  • ????????{??
  • ????????//處理錯(cuò)誤??
  • ????????}??
  • ????}??
  • ????CloseHandle(hFile);??
  • }??
  • ??
  • }??



  • 下面將講述完成端口,那個(gè)更加高效,但是也更加困難;

    2012/9/2

    jofranks 于南昌

    總結(jié)

    以上是生活随笔為你收集整理的【网络编程】之十、重叠IO Overlapped IO的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。