IOCP完成端口例子代码:
// IoPort.cpp : 定义控制台应用程序的入口点。 // #include "stdafx.h" #include <WinSock2.h> #pragma comment(lib,"ws2_32.lib") #include "Win32NetWork.h" #include <ws2tcpip.h> #include <mswsock.h> //微软扩展的类库 typedef BOOL (* LPFN_AcceptEx) ( SOCKET sListenSocket, SOCKET sAcceptSocket, PVOID lpOutputBuffer, DWORD dwReceiveDataLength, DWORD dwLocalAddressLength, DWORD dwRemoteAddressLength, LPDWORD lpdwBytesReceived, LPOVERLAPPED lpOverlapped ); CWin32NetWork netWork; #define LISTIEN 1 #define ACCEPT 2 #define SEND 3 #define SENDED 4 #define RECV 5 #define RECVED 6 #define CLOSE 7 #define DATA_LENGTH 1024 typedef struct PER_IO_DATA { SOCKET client; OVERLAPPED overlapped; INT OperatorType; int nUseLength; int dataLength; char dataBuffer[DATA_LENGTH]; }PER_IO_DATA,*LPPER_IO_DATA; int GetSysCoreCount(){ SYSTEM_INFO si; GetSystemInfo(&si); return si.dwNumberOfProcessors; } void FreeIoData(LPPER_IO_DATA IoData){ if(IoData != NULL){ if(IoData->client != NULL)closesocket(IoData->client); //GlobalFree(IoData); free(IoData); } } LPFN_ACCEPTEX lpfnAcceptEx = NULL; // AcceptEx函数指针 void InitAcceptEx(SOCKET sock){ if(lpfnAcceptEx == NULL){ GUID GuidAcceptEx = WSAID_ACCEPTEX; // GUID,这个是识别AcceptEx函数必须的 DWORD dwBytes = 0; WSAIoctl( sock, SIO_GET_EXTENSION_FUNCTION_POINTER, &GuidAcceptEx, sizeof(GuidAcceptEx), &lpfnAcceptEx, sizeof(lpfnAcceptEx), &dwBytes, NULL, NULL); } } DWORD WINAPI WorkerThread(LPVOID lpParam){ HANDLE ioPort = lpParam; const char *strMsgState = "Hello,how are you?"; while(true){ OVERLAPPED *pOverlapped = NULL; DWORD dwBytesTransfered = 0; void *lpContext = NULL; BOOL bRet = GetQueuedCompletionStatus(ioPort, &dwBytesTransfered, (LPDWORD)&lpContext, &pOverlapped, /*INFINITE*/ 5*1000); if(bRet == FALSE){ //IO端口异常关闭 if(GetLastError() == ERROR_INVALID_HANDLE) break; //超时 if(dwBytesTransfered == 0 && pOverlapped == NULL) continue; //对端异常关闭 dwBytesTransfered = 0; } PER_IO_DATA* pIoContext = CONTAINING_RECORD(pOverlapped, PER_IO_DATA, overlapped); if (0 == dwBytesTransfered) { printf("关闭连接:%d\n",pIoContext->client); FreeIoData(pIoContext); continue; } int nError = 0; switch(pIoContext->OperatorType){ case ACCEPT: printf("接受连接:%d\n",pIoContext->client); //将新的客户套接字与完成端口连接 pIoContext->OperatorType = RECV; //状态设置成接收 if(dwBytesTransfered != -1) pIoContext->nUseLength = dwBytesTransfered; PostQueuedCompletionStatus(ioPort,dwBytesTransfered,(ULONG_PTR)pIoContext,&(pIoContext->overlapped)); break; case RECV: { DWORD Flags = 0; DWORD RecvBytes = 0; pIoContext->OperatorType = RECVED; WSABUF DataBuf; DataBuf.len= pIoContext->dataLength - pIoContext->nUseLength; DataBuf.buf= pIoContext->dataBuffer + pIoContext->nUseLength; ZeroMemory(pIoContext->dataBuffer,pIoContext->dataLength); int nRet = WSARecv(pIoContext->client,&DataBuf,1, &RecvBytes,&Flags,&(pIoContext->overlapped),NULL); int nRecvLen = strlen(pIoContext->dataBuffer); if(nRet ==SOCKET_ERROR){ nError = WSAGetLastError(); //端口PENDING则等待 if(WSA_IO_PENDING == nError){ break; }else{ //关闭端口 pIoContext->OperatorType = CLOSE; PostQueuedCompletionStatus(ioPort,0,(ULONG_PTR)pIoContext,&(pIoContext->overlapped)); break; } } } break; case RECVED: pIoContext->OperatorType = SEND; pIoContext->nUseLength += dwBytesTransfered==-1?0:dwBytesTransfered; PostQueuedCompletionStatus(ioPort,pIoContext->nUseLength,(ULONG_PTR)pIoContext,&(pIoContext->overlapped)); break; case SEND: { DWORD SendBytes = 0; pIoContext->OperatorType = SENDED; WSABUF DataBuf; if(pIoContext->nUseLength == 0){ DataBuf.len= strlen(strMsgState); DataBuf.buf= (CHAR*)strMsgState; }else{ DataBuf.len= pIoContext->nUseLength; DataBuf.buf= pIoContext->dataBuffer; } int nRet = WSASend(pIoContext->client,&DataBuf,1,&SendBytes,0,&(pIoContext->overlapped),NULL); if(nRet ==SOCKET_ERROR){ //端口PENDING则等待 nError = WSAGetLastError(); if(WSA_IO_PENDING == nError){ if(pIoContext->nUseLength == 0){ //心跳检测失败则关闭端口 pIoContext->OperatorType = CLOSE; PostQueuedCompletionStatus(ioPort,0,(ULONG_PTR)pIoContext,&(pIoContext->overlapped)); break; } break; }else { //关闭端口 pIoContext->OperatorType = CLOSE; PostQueuedCompletionStatus(ioPort,0,(ULONG_PTR)pIoContext,&(pIoContext->overlapped)); break; } } } break; case SENDED: pIoContext->OperatorType = RECV; pIoContext->nUseLength = 0; ZeroMemory(pIoContext->dataBuffer,pIoContext->dataLength); PostQueuedCompletionStatus(ioPort,-1,(ULONG_PTR)pIoContext,&(pIoContext->overlapped)); break; case CLOSE: default: printf("关闭连接:%d\n",pIoContext->client); FreeIoData(pIoContext); } } return 0; } DWORD WINAPI ServerProcess(HANDLE ioPort,HANDLE ioClient){ while(true){ OVERLAPPED *pOverlapped = NULL; DWORD dwBytesTransfered = 0; void *lpContext = NULL; BOOL bRet = GetQueuedCompletionStatus(ioPort, &dwBytesTransfered, (LPDWORD)&lpContext, &pOverlapped, INFINITE); if(bRet == FALSE){ if(GetLastError() == ERROR_INVALID_HANDLE) break; else continue; } PER_IO_DATA* pIoContext = CONTAINING_RECORD(pOverlapped, PER_IO_DATA, overlapped); int nError = 0; if(dwBytesTransfered == 0 && (pIoContext->OperatorType == RECV || pIoContext->OperatorType == SEND) ){ FreeIoData(pIoContext); }else if(pIoContext->OperatorType == ACCEPT){ CreateIoCompletionPort((HANDLE)pIoContext->client, ioClient,(ULONG_PTR)pIoContext,0); PostQueuedCompletionStatus(ioClient,dwBytesTransfered,(ULONG_PTR)pIoContext,&(pIoContext->overlapped)); break; } } return 0; } bool ProcessAcceptEx(LPPER_IO_DATA perIoData,SOCKET sock) { perIoData->client = WSASocket(AF_INET,SOCK_STREAM,IPPROTO_TCP,0,0,WSA_FLAG_OVERLAPPED); DWORD dwBytes = 0; BOOL bRet = lpfnAcceptEx(sock,perIoData->client,perIoData->dataBuffer,perIoData->dataLength-((sizeof(SOCKADDR_IN)+16)*2), sizeof(SOCKADDR_IN)+16,sizeof(SOCKADDR_IN)+16,&dwBytes,&(perIoData->overlapped)); if(bRet == FALSE){ int nError = WSAGetLastError(); if(nError == ERROR_IO_PENDING){ //正确 }else if(nError == WSAECONNRESET){ return false; }else if(nError == WSAEINVAL){ return false; }else{ return false; } } return true; } int _tmain(int argc, _TCHAR* argv[]) { //服务器地址 struct sockaddr_in my_addr = {0}; my_addr.sin_family = AF_INET; my_addr.sin_port = htons(23000); my_addr.sin_addr.s_addr = INADDR_ANY; //开启重叠端口并监听 SOCKET sock = WSASocket(AF_INET,SOCK_STREAM,0,NULL,0,WSA_FLAG_OVERLAPPED); int on = 1; int ret = setsockopt( sock, SOL_SOCKET, SO_REUSEADDR, (const char*)&on, sizeof(on) ); int nError = 0; int nRet = bind(sock,(const sockaddr*)&my_addr,sizeof(struct sockaddr_in)); nRet!=SOCKET_ERROR?nRet = listen(sock,100):0; if(nRet == SOCKET_ERROR){ nError = WSAGetLastError(); closesocket(sock); return -1; } HANDLE ioClient = CreateIoCompletionPort(INVALID_HANDLE_VALUE,NULL, NULL,0); //开启服务线程 bool bInitWorkThread = false; if(bInitWorkThread == false){ for(int i = 0; i < GetSysCoreCount()*2;i++){ HANDLE hHandle = CreateThread(NULL,0,WorkerThread,ioClient,0,NULL); if(hHandle != NULL) CloseHandle(hHandle); } bInitWorkThread = true; } //创建完成端口 HANDLE ioPort = CreateIoCompletionPort(INVALID_HANDLE_VALUE,NULL, NULL,0); //绑定完成端口 LPPER_IO_DATA perDandleData = {0}; perDandleData = (LPPER_IO_DATA)GlobalAlloc(GPTR,sizeof(LPPER_IO_DATA)); perDandleData->OperatorType = LISTIEN; CreateIoCompletionPort((HANDLE)sock,ioPort,(ULONG_PTR)perDandleData,0); //初始化接受函数 InitAcceptEx(sock); while(true){ //准备完成端口参数 LPPER_IO_DATA pIoContext = (LPPER_IO_DATA)malloc(sizeof(PER_IO_DATA));//GlobalAlloc(GPTR,sizeof(PER_IO_DATA)); if(pIoContext == NULL){ printf("No Enoufy Memory!\n"); Sleep(100); continue; } ZeroMemory(pIoContext,sizeof(struct PER_IO_DATA)); pIoContext->OperatorType = ACCEPT; pIoContext->dataLength = DATA_LENGTH; //accept接受连接方式 pIoContext->client = accept(sock,NULL,NULL); CreateIoCompletionPort((HANDLE)pIoContext->client, ioClient,(ULONG_PTR)pIoContext,0); PostQueuedCompletionStatus(ioClient,-1,(ULONG_PTR)pIoContext,&(pIoContext->overlapped)); /* //AcceptEx接受连接方式 bool bRet = ProcessAcceptEx(pIoContext,sock); if(bRet == false) break; ServerProcess(ioPort,ioClient); */ } FreeIoData(perDandleData); CloseHandle(ioPort); CloseHandle(ioClient); closesocket(sock); return 0; }Win32NetWork.h
#ifndef WIN32NETWORK_H #define WIN32NETWORK_H //#include <WinSock.h> #pragma comment(lib,"ws2_32.lib") class CWin32NetWork{ public: CWin32NetWork(){ WSADATA wsa_data; WSAStartup(0x0202, &wsa_data); } }; #endif
IOCP相关的一些总结
IOCP编程注意事项
IOCP中在WSASend以及WSARecv的时候出现WSA_IO_PENDING情况的说明