一. 重叠IO模型简介
重叠IO的核心实际上就是一个重叠的数据结构。应用程序在单个套接字上投递一个或者多个IO操作,当IO操作完成时对应的重叠数据结构中的事件对象会受信,相应的应用程序通过查事件对象可以得到通知。就这样,通过重叠的数据结构将异步的IO和程序连接起来了。
重叠数据结构:
typedef struct _OVERLAPPED{
DWORD Internal;
DWORD InternalHigh;
DWORD Offset;
DWORD OffsetHigh;
HANDLE hEvent; //核心的参数,这个就是连接异步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 3
- SOCKET 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);
- //<span style="color:#ff6666;">注意:AcceptEx()如果提供了第四个参数,则这个函数会一直等到得到第一块数据才会返回。
- </span> 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);
- // 加载扩展函数AcceptEx
- GUID 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/O
- PBUFFER_OBJ pBuffer = FindBufferObj(g_events[i]);
- if(pBuffer != NULL)
- {
- if(!HandleIO(pBuffer))
- RebuildArray();
- }
- }
- }
- }
- }