WINDOWS重叠IO模型

来源:互联网 发布:sql判断语句case 编辑:程序博客网 时间:2024/04/29 15:12
    

一. 重叠IO模型简介

重叠IO的核心实际上就是一个重叠的数据结构。应用程序在单个套接字上投递一个或者多个IO操作,当IO操作完成时对应的重叠数据结构中的事件对象会受信,相应的应用程序通过查事件对象可以得到通知。就这样,通过重叠的数据结构将异步的IO和程序连接起来了。

   重叠数据结构:

 

typedef struct _OVERLAPPED{

      DWORDInternal;

      DWORDInternalHigh;

      DWORDOffset;

      DWORDOffsetHigh;

      HANDLEhEvent;//核心的参数,这个就是连接异步IO和应用程序的桥梁

}OVERLAPPED, *LPOVERLAPPED;

二.重叠IO模型的优点

    1.可以运行在支持winsock2的所有windows平台上(IOCP只能运行在NT平台上)

    2.比起阻塞,select,WSAAsyncSelect以及WSAEventSelect等模型提供了更好的系统级性能。

    3.使用重叠模型的应用程序通知缓冲区收发系统直接使用数据,也就是说,如果应用程序投递了一个10KB大小的缓冲区来接收数据,且数据已经到达套接字,则该数据直接    被拷贝到投递的缓冲区。(这一点和以上四种模型是截然不同的)

三。重叠IO机制的基本步骤

     1.要使用重叠数据结构我们就要使用新的IO函数(也就是send,recv,sendto,recvfrom要被WSASend(),WSARecv(),WSASendto(),WSARecvfrom(),WSAAccept()代替)。

     2.将WINDOWS事件对象与重叠数据结构相关联

     3.使用1中说的函数在套接字上投递IO请求

     4.不断的查询与重叠数据结构关联在一起的事件对象

    5.获得IO结果,处理结果。

四。废话少说直接上代码

     注意:这里是一份单线程的代码,所以它做多同时支持64个socket连接,如需更多的连接请采用线程池。

 

 

#include "initsock.h"#include <Mswsock.h>#include "stdio.h"#include <windows.h>CInitSock theSock;  //主要用来初始化socket库#define BUFFER_SIZE 1024//这里是与套接字相关的信息typedef struct _SOCKET_OBJ{SOCKET s;int nOutstandingOps;  //此套接字上重叠IO的数量LPFN_ACCEPTEX lpfnAcceptEx;  //AcceptEx()函数的指针(仅对于监听套接字而言)}SOCKET_OBJ,*PSOCKET_OBJ;typedef struct _BUFFER_OBJ{OVERLAPPED ol;  //重叠的数据结构char* buff;   //投递IO请求时使用的缓冲区结构int nLen;    //缓冲区结构的长度PSOCKET_OBJ   pSocket;  //SOCKET结构int nOperation;  //提交的操作类型#define OP_ACCEPT  1#define OP_READ    2#define OP_WRITE   3SOCKET   sAccept;   //客户端套接字,仅对于监听套接字而言_BUFFER_OBJ *pNext;}BUFFER_OBJ,*PBUFFER_OBJ;HANDLE g_events[WSA_MAXIMUM_WAIT_EVENTS];int g_nBufferCount;PBUFFER_OBJ g_pBufferHead,g_pBufferTail;//申请套接字结构PSOCKET_OBJ GetSocketObj(SOCKET s){PSOCKET_OBJ pSocket=(PSOCKET_OBJ)::GlobalAlloc(GPTR,sizeof(SOCKET_OBJ));if(pSocket!=NULL)pSocket->s=s;return pSocket;}void FreeSocketObj(PSOCKET_OBJ pSocket){if(pSocket->s!=INVALID_SOCKET)::closesocket(pSocket->s);::GlobalFree(pSocket);}PBUFFER_OBJ GetBufferObj(PSOCKET_OBJ pSocket,ULONG nLen){if(g_nBufferCount>WSA_MAXIMUM_WAIT_EVENTS-1)return NULL;PBUFFER_OBJ pBuffer=(PBUFFER_OBJ)::GlobalAlloc(GPTR,sizeof(BUFFER_OBJ));if(pBuffer!=NULL){pBuffer->buff=(char*)::GlobalAlloc(GPTR,nLen);pBuffer->ol.hEvent=WSACreateEvent();pBuffer->pSocket=pSocket;pBuffer->sAccept=INVALID_SOCKET;if(g_pBufferHead==NULL){g_pBufferHead=g_pBufferTail=pBuffer;}else{g_pBufferTail->pNext=pBuffer;g_pBufferTail=pBuffer;}g_events[++g_nBufferCount]=pBuffer->ol.hEvent;}return pBuffer;}void FreeBufferObj(PBUFFER_OBJ pBuffer){PBUFFER_OBJ pTest=g_pBufferHead;BOOL bFind=FALSE;if(pTest==pBuffer)  //这边难道不会造成内存泄露吗?{g_pBufferHead=g_pBufferTail=NULL;bFind=TRUE;}else{while(pTest!=NULL&&pTest->pNext!=pBuffer){pTest=pTest->pNext;}if(pTest!=NULL){pTest->pNext=pBuffer->pNext;if(pTest->pNext==NULL)g_pBufferTail=pTest;bFind=TRUE;}}if(bFind){g_nBufferCount--;::CloseHandle(pBuffer->ol.hEvent);::GlobalFree(pBuffer->buff);::GlobalFree(pBuffer);}}void RebuildArray(){PBUFFER_OBJ pBuffer = g_pBufferHead;int i =  1;while(pBuffer != NULL){g_events[i++] = pBuffer->ol.hEvent;pBuffer = pBuffer->pNext;}}PBUFFER_OBJ FindBufferObj(HANDLE hEvent){PBUFFER_OBJ pBuffer=g_pBufferHead;while(pBuffer!=NULL){if(pBuffer->ol.hEvent==hEvent)break;pBuffer=pBuffer->pNext;}return pBuffer;}
/*
后面有对AcceptEx这个函数进行说明
*/BOOL PostAccept(PBUFFER_OBJ pBuffer){PSOCKET_OBJ pSocket=pBuffer->pSocket;if(pSocket->lpfnAcceptEx!=NULL){pBuffer->nOperation=OP_ACCEPT;pSocket->nOutstandingOps++;//投递此IO请求DWORD dwBytes;pBuffer->sAccept=::WSASocket(AF_INET,SOCK_STREAM,0,NULL,0,WSA_FLAG_OVERLAPPED);
                //注意:AcceptEx()如果提供了第四个参数,则这个函数会一直等到得到第一块数据才会返回。BOOL b=pSocket->lpfnAcceptEx(pSocket->s,pBuffer->sAccept,pBuffer->buff,BUFFER_SIZE-((sizeof(sockaddr_in)+16)*2),sizeof(sockaddr_in)+16,sizeof(sockaddr_in)+16,&dwBytes,&pBuffer->ol);if(!b){if(::WSAGetLastError()!=WSA_IO_PENDING)return false;}return true;}return false;}BOOL PostRecv(PBUFFER_OBJ pBuffer){pBuffer->nOperation=OP_READ;pBuffer->pSocket->nOutstandingOps++;DWORD dwBytes;DWORD dwFlags=0;WSABUF buf;buf.buf=pBuffer->buff;buf.len=pBuffer->nLen;if(::WSARecv(pBuffer->pSocket->s,&buf,1,&dwBytes,&dwFlags,&pBuffer->ol,NULL)!=NO_ERROR){if(::WSAGetLastError()!=WSA_IO_PENDING)return false;}return true;}BOOL PostSend(PBUFFER_OBJ pBuffer){pBuffer->nOperation=OP_WRITE;pBuffer->pSocket->nOutstandingOps++;DWORD dwBytes;DWORD dwFlags=0;WSABUF buf;buf.buf=pBuffer->buff;buf.len=pBuffer->nLen;if(::WSASend(pBuffer->pSocket->s,&buf,1,&dwBytes,dwFlags,&pBuffer->ol,NULL)!=NO_ERROR){if(::WSAGetLastError()!=WSA_IO_PENDING)return false;}return true;}//最重要的函数:IO处理函数BOOL HandleIO(PBUFFER_OBJ pBuffer){PSOCKET_OBJ pSocket=pBuffer->pSocket;pSocket->nOutstandingOps--;DWORD dwTrans;DWORD dwFlags;BOOL Ret=::WSAGetOverlappedResult(pBuffer->pSocket->s,&pBuffer->ol,&dwTrans,FALSE,&dwFlags);if(!Ret){if(pSocket->s!=INVALID_SOCKET){::closesocket(pSocket->s);pSocket->s=INVALID_SOCKET;}if(pSocket->nOutstandingOps==0)FreeSocketObj(pSocket);FreeBufferObj(pBuffer);return FALSE;}switch(pBuffer->nOperation){case OP_ACCEPT:{PSOCKET_OBJ pClient=GetSocketObj(pBuffer->sAccept);PBUFFER_OBJ pSend=GetBufferObj(pClient,BUFFER_SIZE);if(pSend==NULL){printf("Too much  connections!");FreeSocketObj(pClient);return FALSE;}RebuildArray();pSend->nLen=dwTrans;memcpy(pSend->buff,pBuffer->buff,dwTrans);if(!PostSend(pSend)){FreeSocketObj(pClient);FreeBufferObj(pSend);return FALSE;}PostAccept(pBuffer);}break;case OP_READ:if(dwTrans>0){PBUFFER_OBJ pSend=pBuffer;pBuffer->nLen=dwTrans;PostSend(pSend);}else{if(pSocket->s!=INVALID_SOCKET){::closesocket(pSocket->s);pSocket->s=INVALID_SOCKET;}if(pSocket->nOutstandingOps==0)FreeSocketObj(pSocket);FreeBufferObj(pBuffer);return FALSE;}break;case OP_WRITE:if(dwTrans>0){pBuffer->nLen=BUFFER_SIZE;PostRecv(pBuffer);}else{if(pSocket->s!=INVALID_SOCKET){::closesocket(pSocket->s);pSocket->s=INVALID_SOCKET;}if(pSocket->nOutstandingOps==0)FreeSocketObj(pSocket);FreeBufferObj(pBuffer);return FALSE;}break;}return TRUE;}void main(){// 创建监听套节字,绑定到本地端口,进入监听模式int nPort = 4567;SOCKET sListen = ::WSASocket(AF_INET, SOCK_STREAM, IPPROTO_TCP, NULL, 0, WSA_FLAG_OVERLAPPED);SOCKADDR_IN si;si.sin_family = AF_INET;si.sin_port = ::ntohs(nPort);si.sin_addr.S_un.S_addr = INADDR_ANY;::bind(sListen, (sockaddr*)&si, sizeof(si));::listen(sListen, 200);// 为监听套节字创建一个SOCKET_OBJ对象PSOCKET_OBJ pListen = GetSocketObj(sListen);// 加载扩展函数AcceptExGUID GuidAcceptEx = WSAID_ACCEPTEX;DWORD dwBytes;WSAIoctl(pListen->s, SIO_GET_EXTENSION_FUNCTION_POINTER, &GuidAcceptEx, sizeof(GuidAcceptEx),&pListen->lpfnAcceptEx, sizeof(pListen->lpfnAcceptEx), &dwBytes, NULL, NULL);// 创建用来重新建立g_events数组的事件对象g_events[0] = ::WSACreateEvent();// 在此可以投递多个接受I/O请求for(int i=0; i<5; i++){PostAccept(GetBufferObj(pListen, BUFFER_SIZE));}::WSASetEvent(g_events[0]);while(TRUE){int nIndex = ::WSAWaitForMultipleEvents(g_nBufferCount + 1, g_events, FALSE, WSA_INFINITE, FALSE);if(nIndex == WSA_WAIT_FAILED){printf("WSAWaitForMultipleEvents() failed \n");break;}nIndex = nIndex - WSA_WAIT_EVENT_0;for(int i=0; i<=nIndex; i++){int nRet = ::WSAWaitForMultipleEvents(1, &g_events[i], TRUE, 0, FALSE);if(nRet == WSA_WAIT_TIMEOUT)continue;else{::WSAResetEvent(g_events[i]);// 重新建立g_events数组if(i == 0){RebuildArray();continue;}// 处理这个I/OPBUFFER_OBJ pBuffer = FindBufferObj(g_events[i]);if(pBuffer != NULL){if(!HandleIO(pBuffer))RebuildArray();}}}}}