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

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

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

發布時間:2024/4/11 编程问答 33 豆豆
生活随笔 收集整理的這篇文章主要介紹了 【网络编程】之十、重叠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,//一個指針,對應一個DWORD變量,負責接收一次重疊發送/接收操作時機傳輸的字節數。??
  • ??__in??????????BOOL?fWait,//用于決定函數是否應該等待一次待解決的重疊操作完成。TRUE:除非操作完成,否則函數不會返回。?FALSE:并且函數處于待解決狀態,那么函數返回FALSE,同時返回WSAIOINCOMPLETE錯誤。???目前,這個參數無論設置什么都沒有任何效果。??
  • ??__out?????????LPDWORD?lpdwFlags//一個指針,指向DWORD負責接收結果標志。??
  • );??
  • 當函數調用成功那么就會返回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); ?//創建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);     ?//?IP??
  • UINT?nPort?=?ClientAddr.sin_port;                     ?//?Port??

  • 3、為接受的套接字創建一個WSAOVERLAPPED結構,并給這個結構分配一個事件對象句柄,同時將該事件對象句柄分配給一個事件數組,以便稍后WSAWaitForMultipleEvents函數使用。

    [cpp]?view plaincopy
  • WSAEVENT ?EventArray[WSA_MAXIMUM_WAIT_EVENTS]; ???
  • DWORD    ?dwEventTotal?=?0,           ?//?程序中事件的總數??
  • dwRecvBytes?=?0,           ?//?接收到的字符長度??
  • Flags?=?0;                   ?//?WSARecv的參數??
  • ??
  • #define?DATA_BUFSIZE    ?4096         ?//?接收緩沖區大小??
  • [cpp]?view plaincopy
  • //?創建一個事件??
  • //?dwEventTotal可以暫時先作為Event數組的索引??
  • 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;                         ?//?初始化一個WSABUF結構??
  • 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)??
  • {???
  • //?返回WSA_IO_PENDING是正常情況,表示IO操作正在進行,不能立即完成??
  • //?如果不是WSA_IO_PENDING錯誤,就大事不好了~~~~~~!!!??
  • 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;??
  • //?等候重疊I/O調用結束??
  • //?因為我們把事件和Overlapped綁定在一起,重疊操作完成后我們會接到事件通知??
  • dwIndex?=?WSAWaitForMultipleEvents(dwEventTotal,???
  • EventArray?,FALSE?,WSA_INFINITE,FALSE);??
  • //?注意這里返回的Index并非是事件在數組里的Index,而是需要減去WSA_WAIT_EVENT_0??
  • dwIndex?=?dwIndex?–?WSA_WAIT_EVENT_0;??


  • 7、使用WSAGetOverlappedResult函數來判斷重疊調用的返回狀態是什么。

    [cpp]?view plaincopy
  • DWORD?dwBytesTransferred;??
  • WSAGetOverlappedResult(?AcceptSocket,?AcceptOverlapped?,??
  • &dwBytesTransferred,?FALSE,?&Flags);??
  • //?先檢查通信對方是否已經關閉連接??
  • //?如果==0則表示連接已經,則關閉套接字??
  • 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,//以字節為單位,指定在lpOutputBuffer緩沖區中,保留多大的空間來接收數據。??如果傳0,那么就不會再接收任何數據了。??
  • ??__in??????????DWORD?dwLocalAddressLength,//??
  • ??__in??????????DWORD?dwRemoteAddressLength,//和上一個參數以字節為單位指定在lpOutputBuffer緩沖區中,保留多大空間,在一個套接字被接受的時候,用于本地和遠程地址信息的保存??
  • ??__out?????????LPDWORD?lpdwBytesReceived,//用于返回接收到的實際數據量,以字節為單位。只有在以同步方式完成的前提下,才會設置這個參數。??
  • ??__in??????????LPOVERLAPPED?lpOverlapped//對應的是一個OVERLAPPED結構,允許AcceptEx以一種異步方式工作。?前面說過,只有在一個重疊I/O應用中,這個函數才需要使用事件對象通知機制。??
  • );??
  • 要知道AcceptEx函數只能由這里給大家說的“事件通知”方式獲取異步I/O請求的結果,在"完成例程”中是無法使用的。


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

    [cpp]?view plaincopy
  • char?buf[512*10000];??
  • int?readdata(void)??
  • {??
  • ????BOOL?bRet;???//返回值??
  • ????HANDLE?hFile;???//文件指針??
  • ????DWORD?numRead;???//讀取數據長度??
  • ????OVERLAPPED?overlapped;???//I/O結構??
  • ??????
  • ????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??
  • ????overlapped.Offset?=?0;????//讀文件的位置??
  • ??????
  • ????bRet?=?ReadFile(hFile,?buf,?512*10000,?&numRead,?&overlapped);??
  • ??????
  • ????if(TRUE?==?bRet)??//讀取數據成功??
  • ????{??
  • ????????//讀取數據完畢??
  • ????}??
  • ????else??//重疊io操作??
  • ????{??
  • ????????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的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。