IOCP 学习例子

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编程注意事项

IOCP中在WSASend以及WSARecv的时候出现WSA_IO_PENDING情况的说明

你可能感兴趣的:(IOCP 学习例子)