基于Accept的IOCP简单模型


IOCP自学实例,看的再多不如自己一点一点的完成一次。

转载要注明出处啊:http://blog.csdn.net/wsgxiaomianao


服务器代码:

// iocp.cpp : 定义控制台应用程序的入口点。
//

#include "stdafx.h"
#include "iocp.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#endif

#include <WinSock.h>
#pragma  comment(lib,"Ws2_32.lib")
#include <vector>

CWinApp theApp;

using namespace std;


enum {
	ACCEPT_POSTED = 0,
	RECV_POSTED,
	SEND_POSTER
};

#define  BUF_LEN  1024
#define  EXIT_CODE  0xFFFFFFFF

typedef struct _PRE_IO_DATA 
{
	OVERLAPPED Overlapped;
	SOCKET sClient;
	SOCKADDR_IN sClientAddr;
	WSABUF wsaBuffer;
	char buf[BUF_LEN];
	DWORD NumberOfBytesRecvd;
	int postType;
	
}_preIoData,*_pPreIoData;


HANDLE g_completionHandle = INVALID_HANDLE_VALUE;
SOCKET g_ListenSock = INVALID_SOCKET;
vector<_preIoData *>g_VecPerIoData;

/* 基于Accept的IOCP简单模型创建步骤:
(1)初始化WinSock环境
(2)创建完成端口
(3)创建完成端口的工作者线程
(4)创建监听socket并进行绑定和监听
(5)创建接收客户端连入的线程
(6)退出服务时,资源回收等
*/

void showError(TCHAR *wsBuf)
{
	CString csLog;
	csLog.Format(L"%s:%d",wsBuf,GetLastError());
	AfxMessageBox(csLog);
}

//init sock environment
void initSock()
{
	WSADATA wsaData;
	WSAStartup(MAKEWORD(2,2),&wsaData);
}

//create completion port
bool createCompletionPort()
{
	g_completionHandle = CreateIoCompletionPort(INVALID_HANDLE_VALUE,NULL,0,0);
	if (g_completionHandle == INVALID_HANDLE_VALUE)
	{
		return false;
	}

	return true;
}
//create worker thread
	/*
		in worker thread...
		{
			get queue completion port...in while
			judge exit
			judge type == recv_type
			post wsaRecv
			
		}
	*/
DWORD WINAPI workerThread( LPVOID lpParameter )
{
	UNREFERENCED_PARAMETER(lpParameter);

	_preIoData *pPerIoData= NULL;
	DWORD dwRecvLen=0;
	SOCKET sClient = INVALID_SOCKET;

	
	while(TRUE)
	{
		

		BOOL bRet = GetQueuedCompletionStatus(g_completionHandle,
									&dwRecvLen,
									(PULONG_PTR)&pPerIoData,//此参数收到完成端口的队列中的结构,也就是在绑定客户端socket时穿进去的lpCompletionKey
									(LPOVERLAPPED*)&pPerIoData,//此处使用的是结构体内的Overlapped,不用CONTAINING_RECORD了
									INFINITE);
		if (!bRet)
		{
			showError(L"GetQueuedCompletionStatus Error");
			break;
		}else
		{
			if (dwRecvLen == EXIT_CODE)
			{
				cout<<"dwRecvLen == EXIT_CODE.....\n";
				break;
			}

			switch (pPerIoData->postType)
			{
			case RECV_POSTED:
				{
					if (dwRecvLen == 0) //client has been closed the socket
					{
						cout<<"客户端断开 IP:"<<inet_ntoa(pPerIoData->sClientAddr.sin_addr)<<" PORT: "<<ntohs(pPerIoData->sClientAddr.sin_port)<<endl;
						//
						vector<_preIoData*>::iterator it;
						for (it = g_VecPerIoData.begin();it < g_VecPerIoData.end();it++)
						{
							_preIoData *ppid = (*it);

							if (ppid->sClient == pPerIoData->sClient)
							{
								closesocket(pPerIoData->sClient);

								delete ppid;
								g_VecPerIoData.erase(it);
								break;
							}
						}


					}else
					{

						cout<<"Client:"<<inet_ntoa(pPerIoData->sClientAddr.sin_addr)<<" data: "<<pPerIoData->buf<<endl;
						DWORD dwFlag = 0;
						ZeroMemory(pPerIoData->buf,sizeof(char)*BUF_LEN);

						WSARecv(pPerIoData->sClient,&(pPerIoData->wsaBuffer),1,&(pPerIoData->NumberOfBytesRecvd),&dwFlag,&(pPerIoData->Overlapped),NULL);
					}
				}
				break;
			default:
			
				break;
			}//switch
		}
		

	}

	cout<<"workerThread exit....\n";
	return 0;
}



//create socket  & bind & listen
bool createSock()
{

	g_ListenSock =  WSASocket(
		AF_INET,
		SOCK_STREAM,
		IPPROTO_TCP,
		NULL,
		0,
		WSA_FLAG_OVERLAPPED);
	if (g_ListenSock == INVALID_SOCKET)
	{
		showError(L"WSASocket Error");
		return false;
	}


	//将监听socket绑定到完成端口,此处不绑定貌似也不出问题
	if( NULL== CreateIoCompletionPort( (HANDLE)g_ListenSock, g_completionHandle,(DWORD)NULL, 0))  
	{  
		return false;
	}



	SOCKADDR_IN sockSer;
	ZeroMemory(&sockSer,sizeof(SOCKADDR_IN));
	//sockSer.sin_addr.s_addr = inet_addr("192.168.1.194");
	sockSer.sin_addr.S_un.S_addr = ADDR_ANY;
	sockSer.sin_family = AF_INET;
	sockSer.sin_port = htons(12345);
	

	if (SOCKET_ERROR == bind(g_ListenSock,(struct sockaddr*)&sockSer,sizeof(SOCKADDR_IN)))
	{
		showError(L"bind error");
		return false;
	}

	if (SOCKET_ERROR == listen(g_ListenSock,SOMAXCONN))
	{
		showError(L"listen error");
		return false;

	}
	
	return true;
}

// create accept thread & in while to accept client
	/*
	{
		//accept

		//bind completion port

		//new pre_io and wsaRecv
	}
	*/


bool acceptInWhile()
{
	while(TRUE)
	{
		if (g_ListenSock == INVALID_SOCKET)
		{
			AfxMessageBox(L"g_ListenSock is INVALID_SOCKET");
			break;
		}
		SOCKADDR_IN clientSockAddr ;//= new SOCKADDR_IN;
		ZeroMemory(&clientSockAddr,sizeof(SOCKADDR_IN));
		int nLen = sizeof(SOCKADDR_IN);
		SOCKET clientSock = accept(g_ListenSock,(struct sockaddr*)&clientSockAddr,&nLen);
		if (clientSock == INVALID_SOCKET)
		{
			//showError(L"accept client socket error");
			break;
		}

		cout<<"客户端IP:"<<inet_ntoa(clientSockAddr.sin_addr)<<" PORT: "<<ntohs(clientSockAddr.sin_port)<<endl;

		_preIoData *perData = new _preIoData;
		g_VecPerIoData.push_back(perData);
		ZeroMemory(perData,sizeof(_preIoData));

		perData->sClient = clientSock;
		perData->sClientAddr = clientSockAddr;

		/*
		将连入的客户端的SOCKET绑定到完成端口上,这里面最注意的是第三个参数,
		此参数传递进去以后,当此请求返回时会在消息队列中取出,因此可以传递所需要的参数(例如客户端的IP以及端口等)
		*/
		if (NULL == CreateIoCompletionPort((HANDLE)clientSock,g_completionHandle,(DWORD)perData,0))
		{
			showError(L"bind completion port error");
			return false;
		}

		perData->wsaBuffer.buf = perData->buf;
		perData->wsaBuffer.len = BUF_LEN;
		perData->NumberOfBytesRecvd = BUF_LEN;
		perData->postType = RECV_POSTED;

		
		/*
		这里投递了一个WSARecv的请求,当有此类型的请求返回时,便会在完成端口的工作者线程中去处理
		*/
		DWORD dwFlag = 0;
		if ( SOCKET_ERROR  == WSARecv(clientSock,&(perData->wsaBuffer),1,&(perData->NumberOfBytesRecvd),&dwFlag,&(perData->Overlapped),NULL))
		{
			if (GetLastError() != WSA_IO_PENDING)
			{
				showError(L"SOCKET_ERROR  == WSARecv");
				break;

			}
			
		}
	}
	return true;
}


DWORD WINAPI AcceptThreadProc( LPVOID lpParameter )
{

	UNREFERENCED_PARAMETER(lpParameter);
	acceptInWhile();

	return 0;

}



int _tmain(void)
{
	int nRetCode = 0;

	if (!AfxWinInit(::GetModuleHandle(NULL), NULL, ::GetCommandLine(), 0))
	{
		_tprintf(_T("错误: MFC 初始化失败\n"));
		nRetCode = 1;
		return nRetCode;
	}
	

	initSock();

	if (!createCompletionPort())
	{
		AfxMessageBox(L"create completion port error!");
		return -1;
	}
	
	SYSTEM_INFO sysInfo;
	ZeroMemory(&sysInfo,sizeof(SYSTEM_INFO));
	GetSystemInfo(&sysInfo);
	int nProcessNum = sysInfo.dwNumberOfProcessors;

	HANDLE *hWorker = new HANDLE [nProcessNum];
	for (int i=0;i<nProcessNum;i++)
	{
		DWORD dwThreadExit;
		hWorker[i] = CreateThread(NULL,0,workerThread,NULL,0,&dwThreadExit);
		if (!hWorker)
		{
			
			return -1;
		}
	}

	if (!createSock())
	{
		showError(L"createSock error");
	}
	

	DWORD dwAcceptExit;
	HANDLE hAcceptHdl = CreateThread(NULL,0,AcceptThreadProc,NULL,0,&dwAcceptExit);
	CloseHandle(hAcceptHdl);

EXIT:
	cout<<"IOCP服务已经打开,按'x'键将关闭IOCP服务"<<endl;
	if ('x' != getchar())
	{
		goto EXIT;
	}

	//以下是关闭服务的清理工作
	

	for (int i=0;i<nProcessNum;i++)
	{
		PostQueuedCompletionStatus(g_completionHandle,EXIT_CODE,(DWORD)NULL,NULL);
	}

	WaitForMultipleObjects(nProcessNum,hWorker,TRUE,INFINITE);
	cout<<"both of workerThreads have been closed..."<<endl;


	
	for (int i=0;i<nProcessNum;i++)
	{
		CloseHandle(hWorker[i]);
	}

	 
	CloseHandle(g_completionHandle);
	closesocket(g_ListenSock);
	WSACleanup();



	delete[] hWorker;

	for (int i=0;i<g_VecPerIoData.size();i++)
	{
		_preIoData *pPid = g_VecPerIoData.at(i);
		delete pPid;
		pPid = NULL;
	}
	g_VecPerIoData.clear();

	system("PAUSE");
	return nRetCode;
}

下面是客户端的测试代码:开启N个线程同时向服务器发送数据

// Client.cpp : 定义控制台应用程序的入口点。
//

#include "stdafx.h"
#include "Client.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#endif


// 唯一的应用程序对象
#include <iostream>
#include <string>
CWinApp theApp;
using namespace std;

#include <WinSock.h>
#pragma comment(lib,"Ws2_32.lib")


#define  MAX_SOCK_CONN		100
#define  SERVER_IP			"192.168.1.128"
#define  SERVER_PORT		12345

UINT g_Fail = 0;

typedef struct _SOCK_PARAM
{
	HANDLE hThread;
	DWORD dwThreadId;
	SOCKET sock;
	UINT uIndex;

	BOOL bIsExitThread;//
	//HANDLE hEventConn;

}SockParam,*pSockParam;

DWORD WINAPI ThreadProc(LPVOID lpParameter)
{

	SockParam *PSockParam = (SockParam*)lpParameter;
	if (!PSockParam)
	{
		return -1;

	}
	

	int nRetValue = -1;
	CString csLog;

	do 
	{
		//create 
		PSockParam->sock = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
		if (PSockParam->sock == INVALID_SOCKET)
		{

			csLog.Format(L"create socket error:%d",GetLastError());
			AfxMessageBox(csLog);
			break;
		}


		SOCKADDR_IN sockAddr;
		ZeroMemory(&sockAddr,sizeof(SOCKADDR_IN));
		sockAddr.sin_addr.s_addr = inet_addr(SERVER_IP);
		sockAddr.sin_family = AF_INET;
		sockAddr.sin_port = htons(SERVER_PORT);


		//connect
		nRetValue = connect(PSockParam->sock,(sockaddr*)&sockAddr,sizeof(SOCKADDR_IN));
		if (nRetValue == SOCKET_ERROR)
		{
			int nError = GetLastError();
			
			PSockParam->sock = INVALID_SOCKET;
			g_Fail++;

			CString csLog;
			csLog.Format(L"Thread index %d connect error:%d,g_Fail:%d",PSockParam->uIndex,nError,g_Fail);
			OutputDebugString(csLog);

			break;
		}

		while(TRUE)
		{
			Sleep(300);
			if (PSockParam->bIsExitThread)
			{
				shutdown(PSockParam->sock,SD_SEND);
				break;
			}
			//Send
			nRetValue = send(PSockParam->sock,"hello world\n",strlen("hello world\n"),0);
			if (nRetValue == SOCKET_ERROR )
			{
				CString csLog;
				csLog.Format(L"send error:%d",GetLastError());
				AfxMessageBox(csLog);
				break;
			}
		}
		

	} while (FALSE);

	return 0;
}



int _tmain(int argc, TCHAR* argv[], TCHAR* envp[])
{
	int nRetCode = -1;

	if (!AfxWinInit(::GetModuleHandle(NULL), NULL, ::GetCommandLine(), 0))
	{
		_tprintf(_T("错误: MFC 初始化失败\n"));
		nRetCode = 1;
		return nRetCode;
	}

	cout<<"创建"<<MAX_SOCK_CONN<<"个网络连接一起发送data.........."<<endl;
	int nRetValue = -1;
	CString csLog;
	WSADATA wsaData;
	

	nRetValue = WSAStartup(MAKEWORD(2,2),&wsaData);
	if (nRetValue)
	{
		csLog.Format(L"WSAStartup error:%d",GetLastError());
		AfxMessageBox(csLog);
		return nRetCode;
	}


	SockParam sockParam[MAX_SOCK_CONN];
	ZeroMemory(&sockParam,sizeof(SockParam)*MAX_SOCK_CONN);


	for (int i=0;i<MAX_SOCK_CONN;i++)
	{		
		sockParam[i].uIndex = i;
		sockParam[i].bIsExitThread = FALSE;
		sockParam[i].hThread = CreateThread(NULL,0,ThreadProc,(LPVOID)&sockParam[i],/*CREATE_SUSPENDED*/0,&sockParam[i].dwThreadId);
		Sleep(100);
	}
	


	Sleep(500);
	cout<<"按任意键将关闭所有网络连接......"<<endl;
	system("PAUSE");


	for (int i=0;i<MAX_SOCK_CONN;i++)
	{
		Sleep(10);

		sockParam[i].bIsExitThread = TRUE;
	}



	for (int i=0;i<MAX_SOCK_CONN;i++)
	{
		if (sockParam[i].sock != INVALID_SOCKET)
		{
			closesocket(sockParam[i].sock);
		}
	}

	WSACleanup();
	
	return nRetCode;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值