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资料说明: