了解Windows下的几种Socket I/O网络模型

本文主要讲述的是windows下的Socket I/O管理模型以及相关程序例子。主要的模型有以下几个:

一:select模型


二:WSAAsyncSelect模型


三:WSAEventSelect模型


四:OverLapped I/O 时间通知模型


五:OverLapped I/O 完成例程模型


六:完成端口 IOCP模型


为了让内容能够生动易懂,本文采用一个生活的例子来比喻各种模型的场景。

       <1>首先我们把要管理的Socket看做是 几个码头, 

      <2>Socket上面接收到的数据信息就是 船上的货物,

      <3>你是码头的负责人,负责将货品的处理。


公用的数据结构和函数:

#ifndef _DATABASE_

#define  DATA_BUFFER_SIZE 8192

/*重叠数据结构封装*/
typedef struct
{
	OVERLAPPED Overlapped;
	WSABUF DataBuff;
	CHAR Buff[DATA_BUFFER_SIZE];
	SOCKET Socket;
}PER_IO_OPERATION_DATA,* LPPER_IO_OPERATION_DATA;

/*IOCP标识封装*/
typedef struct
{
	SOCKET Socket;
}PER_HANDLE_DATA,*LPPER_HANDLE_DATA;


SOCKET ServerSockInit()
{
	/*初始化相关网络环境*/
	WORD wVersionRequired=MAKEWORD(2, 2);
	WSADATA wsData;

	if(WSAStartup(wVersionRequired,&wsData)!=0)
	{
		return SOCKET_ERROR;
	}

	if(HIBYTE(wsData.wVersion) !=2 || LOBYTE(wsData.wVersion) != 2)
	{
		WSACleanup();
		return SOCKET_ERROR;
	}

	/*创建服务器监听端口*/
	SOCKET sockSrv=socket(AF_INET,SOCK_STREAM,0);
	SOCKADDR_IN sockAdd;
	sockAdd.sin_family=AF_INET;
	sockAdd.sin_addr.S_un.S_addr=htonl(ADDR_ANY);
	sockAdd.sin_port=htons(6000);

	/*绑定监听端口和设置监听队列长度*/
	if (SOCKET_ERROR == bind(sockSrv,(sockaddr *)&sockAdd,sizeof(sockaddr)))
	{
		printf("Bind绑定sock到对应端口出错, 错误码: %d !!!",  WSAGetLastError());
	}

	if(SOCKET_ERROR == listen(sockSrv,5))
	{
		printf("Listen设置监听端口队列长度出错, 错误码: %d !!!",  WSAGetLastError());
	}

	return sockSrv;
}



#define _DATABASE_
#endif




一:select模型


      在这种模型下,负责人只能轮流步行去各个码头询问有没有货要上岸,好吧,没有的话就继续下一个码头。若有得话就开始负责叫人处理货物,货品上岸,装集装箱,后面还有一大堆事情。好吧,处理完了,继续去下一个码头询问。好吧,一两个码头还行,hode得住,要是成千上万个码头可要命。那效率真低。


#ifndef _SELECT_
#define _SELECT_ 
#include "StdAfx.h"

DWORD WINAPI SelectProcessIO(LPVOID lpParam)  
{  
    fd_set* pfdSocketSet = (fd_set*)lpParam;	/*指针转换*/

	fd_set fdReadSocketSet;						/*可读socket数组,select将已经准备好Recv,
												  Accept等操作的Socket投放进去*/
	
	int nOperateCount = 0;						/*select返回值,返回可操作个数或者错误标识*/
	
	int  nRecvResult =0;
	
	char szDataBuff[DATA_BUFFER_SIZE];			
	

	while(TRUE)
	{
		/*内存重置*/
		memset(&fdReadSocketSet, 0, sizeof(fdReadSocketSet));
		
		/*将要检测读操作的所有socket复制到Read数组里面*/
		fdReadSocketSet.fd_count = pfdSocketSet->fd_count;
		for (int nIndex = 0; nIndex < pfdSocketSet->fd_count; ++nIndex)
		{
			fdReadSocketSet.fd_array[nIndex] = pfdSocketSet->fd_array[nIndex];
		}

		if (pfdSocketSet->fd_count > 0)
		{
		  /*select操作会将可以进行接收数据操作的socket从fdReadSocketSet里面筛选出来*/
		  nOperateCount = select(NULL, &fdReadSocketSet, NULL, NULL, NULL);
		  
		  if (nOperateCount == SOCKET_ERROR)
		  {
			 printf("select error,error code:%d !!!\n", WSAGetLastError());
			 continue;
		  }
		  
		  /*将符合条件的Socket逐个取出进行recv操作*/
		  for (int i = 0; i < nOperateCount; ++i)
		  {
			 memset(szDataBuff, 0, sizeof(szDataBuff));
			 nRecvResult = recv(fdReadSocketSet.fd_array[i], szDataBuff, sizeof(szDataBuff), 0);

			 if (nRecvResult == 0)	 /*返回值为0默认是断开*/
			 {
				printf("socket %d disconnect!!!\n", fdReadSocketSet.fd_array[i]);
				FD_CLR(fdReadSocketSet.fd_array[i], pfdSocketSet);
			 }
			 else if(nRecvResult == SOCKET_ERROR)
			 {
				 printf("socket %d error code:%d !!!\n", fdReadSocketSet.fd_array[i], WSAGetLastError());
				 FD_CLR(fdReadSocketSet.fd_array[i], pfdSocketSet);
			 }
			 else
			 {
				printf("Sock %d :Recv Data is:%s !!!\n", fdReadSocketSet.fd_array[i], szDataBuff);
			 }
		  }
		}
	}

	return 0;  
}  


int SelectTest()
{
	SOCKET sockSrv = ServerSockInit();

	if (sockSrv == SOCKET_ERROR)
	{
		printf("Create Server Socket Error!!!\n");
		
		return -1;
	}

	/*保存sock的数组*/
	fd_set	fdSocketSet;					
	FD_ZERO(&fdSocketSet);

	/*创建辅助线程处理网络事件 */
	HANDLE hProcessIO = CreateThread(NULL, 0, SelectProcessIO, &fdSocketSet, 0, NULL);  

	if(hProcessIO)  
	{  
		CloseHandle(hProcessIO);  
	}  
	
	SOCKET sockClient;
	SOCKADDR_IN SockAddress;
	in_addr inaddr;
	INT nAddressLength = sizeof(SockAddress);

	while (TRUE)
	{
		sockClient=accept(sockSrv, (sockaddr*)&SockAddress, &nAddressLength);

		inaddr.s_addr=SockAddress.sin_addr.s_addr;
		
		printf("Socket Register, IP:%s, Port:%d \n", 
				inet_ntoa(inaddr), 
				SockAddress.sin_port);
		
		FD_SET(sockClient, &fdSocketSet);			/*将sockClient添加到数组里*/
	}
}

#endif


二:WSAAsyncSelect模型


        这种模型下,负责人首先召集各个码头的调度人员,告诉他们:如果有货品要上岸,打电话给我啊,我电话是888 。好吧,于是有货的话,大家都忙着给负责人打电话,然后负责人接到电话后立刻赶往相应码头 处理货物,货品上岸,装集装箱,后面还有一大堆事情。好吧,要是忙碌的时候,可能负责人的电话各种占线,好吧,效率还是低,不过总比逐个码头跑好点。

注 : 由于WSAAsyncSelect采用与MFC相同的句柄--消息机制实现,原理是跟点击按钮触发消息,处理消息一样,由于要用到Mfc程序,这里不举例子!!



三:WSAEventSelect模型


        好吧,负责人疲于奔命,所有他决定升级码头的系统,在信息中心为每个码头安装一个信号灯,负责人只要在信息中心看到哪些等亮起就知道有货要上岸了, 不过资金有限,只能安装64个信号灯,好吧,现在他可以雇多几个手下(线程),告诉他们灯亮起的时候应该怎么做。好吧,效率终于提高点了。


#ifndef _WSAEVENTSELECT_
#define _WSAEVENTSELECT_ 

#include "StdAfx.h"

/*事件模式相关结构,保存socket以及对应的信号对象数组*/
typedef struct
{
	int nEventCount;
	WSAEVENT hEvents[WSA_MAXIMUM_WAIT_EVENTS];
	SOCKET	 sockSet[WSA_MAXIMUM_WAIT_EVENTS];
}EventSet;


void EVENT_CLR(EventSet* pEventSet, WSAEVENT hEvent)
{
	for (int i = 0; i < pEventSet->nEventCount; ++i)
	{
		if (pEventSet->hEvents[i] == hEvent)
		{
			while (i < pEventSet->nEventCount - 1)
			{
				pEventSet->hEvents[i] = pEventSet->hEvents[i+1];
				pEventSet->sockSet[i] = pEventSet->sockSet[i+1];
				++i;
			}
			pEventSet->nEventCount -- ;
			break;
		}
	}
}

void EVENT_ADD(EventSet* pEventSet, WSAEVENT hEvent, SOCKET sock)
{
	if (pEventSet->nEventCount == WSA_MAXIMUM_WAIT_EVENTS)
	{
		return;
	}
	
	pEventSet->hEvents[pEventSet->nEventCount] = hEvent;

	pEventSet->sockSet[pEventSet->nEventCount] = sock;

	pEventSet->nEventCount ++ ;
}


DWORD WINAPI WSAEventSelectProcessIO(LPVOID lpParam)  
{  
   EventSet* pEventSet = (EventSet*)lpParam;

   int dwWaitResult = 0;
   int nEventIndex = 0;
   WSANETWORKEVENTS WSANetworkEvents;
   char szDataBuff[DATA_BUFFER_SIZE];
   int nErrorCode = 0;

   while(true)
   {
	  dwWaitResult = WSAWaitForMultipleEvents(pEventSet->nEventCount, pEventSet->hEvents, FALSE, WSA_INFINITE, FALSE);

	 	if (dwWaitResult == WSA_WAIT_FAILED)
		{
			/*信号量数组为null*/
			nErrorCode = WSAGetLastError();

			if (nErrorCode == WSA_INVALID_PARAMETER)
			{
				continue;
			}

			else
			{
				printf("WSAWaitForMultipleEvent error,error code :%d !!!", WSAGetLastError());
			}
			
			break;;
		}

	  /*获取有信号的信号对象在数组中的下标未知*/
	  nEventIndex = dwWaitResult - WSA_WAIT_EVENT_0;			 
	  
	  /*根据信号量和Socket获取网络事件*/
	  WSAEnumNetworkEvents(pEventSet->sockSet[nEventIndex],  
						   pEventSet->hEvents[nEventIndex], 
						   &WSANetworkEvents);
	  
	  /*根据网络事件执行对应的操作*/
	  if (FD_READ == WSANetworkEvents.lNetworkEvents)		 
	  {
		  memset(szDataBuff, 0, sizeof(szDataBuff));
		  recv(pEventSet->sockSet[nEventIndex], szDataBuff, sizeof(szDataBuff), 0);
		  printf("Sock %d Recv Data:%s !!!\n", pEventSet->sockSet[nEventIndex], szDataBuff);
	  }
	  else
	  {
		  printf("lNetWorkEventCode is %d", WSANetworkEvents.lNetworkEvents);
	  }

	  WSAResetEvent(pEventSet->hEvents[nEventIndex]);	/*信号量重置*/
   }

   return 0;
}  

INT WSAEventSelectTest()
{
	SOCKET sockSrv = ServerSockInit();

	if (sockSrv == SOCKET_ERROR)
	{
		printf("创建服务器端口出错!!!\n");

		return -1;
	}
	
	/*信号数组*/
	EventSet eventSet;
	memset(&eventSet, 0, sizeof(eventSet));

	/*创建完成端口处理完成消息线程 理论创建线程数 = cpu核心数+1 */
	HANDLE hProcessIO = CreateThread(NULL, 0, WSAEventSelectProcessIO, &eventSet, 0, NULL);  
	if(hProcessIO)  
	{  
		CloseHandle(hProcessIO);  
	}  

	SOCKET sockClient;
	SOCKADDR_IN SockAddress;
	in_addr inaddr;
	INT nAddressLength = sizeof(SockAddress);

	while (TRUE)
	{
		sockClient=accept(sockSrv, (sockaddr*)&SockAddress, &nAddressLength);
		inaddr.s_addr=SockAddress.sin_addr.s_addr;

		WSAEVENT hTempEvent = WSACreateEvent();						/*创建新信号量*/
		WSAEventSelect(sockClient, hTempEvent, FD_READ|FD_CLOSE);	/*绑定信号量与相关的网络事件*/
		EVENT_ADD(&eventSet, hTempEvent, sockClient);				/*加到信号量数组里面*/

		printf("收到SOCK注册, IP:%s, 端口:%d \n", inet_ntoa(inaddr), SockAddress.sin_port);
	}
	
	return 0;
}

#endif



四:OverLapped I/O 模型


        负责人厌恶了货物上岸,装集装箱的操作,它决定将这些事情交给码头调度员。 每个码头上放一个空的集装箱(重叠),有货物要上岸的时候,码头调度员直接给其上岸,然后将其装好集装箱。然后再通过信号灯通知负责人,负责人再安排手下去干剩下的工作,干完了再把空集装箱放码头上,通过充分利用码头调度员的资源,现在效率更高了。


#ifndef _OVERLAPPEDIO_
#define _OVERLAPPEDIO_

#include "DataBase.h"

typedef struct
{
	int							nSetCount;
	WSAEVENT					hEvents[WSA_MAXIMUM_WAIT_EVENTS];
	LPPER_HANDLE_DATA			pHandleDatas[WSA_MAXIMUM_WAIT_EVENTS];
	LPPER_IO_OPERATION_DATA		pOperationsDatas[WSA_MAXIMUM_WAIT_EVENTS];
}OverLappedIoSet;


void OVERLAPPEDIOSET_CLR(OverLappedIoSet* pOverLappedIoSet, WSAEVENT hEvent)
{
	for (int i = 0; i < pOverLappedIoSet->nSetCount; ++i)
	{
		if (pOverLappedIoSet->hEvents[i] == hEvent)
		{
			while (i < pOverLappedIoSet->nSetCount - 1)
			{
				pOverLappedIoSet->hEvents[i] = pOverLappedIoSet->hEvents[i+1];
				pOverLappedIoSet->pOperationsDatas[i] = pOverLappedIoSet->pOperationsDatas[i+1];
				pOverLappedIoSet->pHandleDatas[i] = pOverLappedIoSet->pHandleDatas[i+1];
				++i;
			}
			pOverLappedIoSet->nSetCount -- ;
			break;
		}
	}
}

void OVERLAPPEDIOSET_ADD(OverLappedIoSet* pOverLappedIoSet, 
						 WSAEVENT hEvent, 
						 LPPER_IO_OPERATION_DATA pOperationsData,
						 LPPER_HANDLE_DATA	pHandleData
						 )
{
	if (pOverLappedIoSet->nSetCount == WSA_MAXIMUM_WAIT_EVENTS)
	{
		return;
	}

	pOverLappedIoSet->hEvents[pOverLappedIoSet->nSetCount] = hEvent;
	pOverLappedIoSet->pOperationsDatas[pOverLappedIoSet->nSetCount] = pOperationsData;
	pOverLappedIoSet->pHandleDatas[pOverLappedIoSet->nSetCount] = pHandleData;
	pOverLappedIoSet->nSetCount ++ ;
}


DWORD WINAPI OverLapperdIoProcessIO(LPVOID lpParam)  
{  
	OverLappedIoSet* pOverLappedIoSet = (OverLappedIoSet*)lpParam;

	LPPER_IO_OPERATION_DATA pIoData = NULL;
	LPPER_HANDLE_DATA pHandle = NULL;
	
	DWORD dwWaitResult = 0;
	int nEventIndex = 0;
	int nBufferTransfered = 0;
	int nFlag = 0;
	int nErrorCode = 0;

	while(true)
	{
		dwWaitResult = WSAWaitForMultipleEvents(pOverLappedIoSet->nSetCount, pOverLappedIoSet->hEvents, FALSE, WSA_INFINITE, FALSE);

		if (dwWaitResult == WSA_WAIT_FAILED)
		{
			/*信号量数组为null*/
			nErrorCode = WSAGetLastError();

			if (nErrorCode == WSA_INVALID_PARAMETER)
			{
				continue;
			}

			else
			{
				printf("WSAWaitForMultipleEvent error,error code %d !!!", WSAGetLastError());
			}

			break;;
		}
		
		/*获取有信号的信号对象在数组中的下标未知*/
		nEventIndex = dwWaitResult - WSA_WAIT_EVENT_0;
		pIoData = pOverLappedIoSet->pOperationsDatas[nEventIndex];
		pHandle = pOverLappedIoSet->pHandleDatas[nEventIndex];
		printf("Sock %d :接收到数据:%s !!!\n", pHandle->Socket, pIoData->Buff);

		memset(pIoData->Buff, 0, sizeof(pIoData->Buff));
	
		int nRet = WSARecv(pHandle->Socket, 
							&(pIoData->DataBuff), 
							1, 
							(LPDWORD)&nBufferTransfered, 
							(LPDWORD)&nFlag, 
							(LPWSAOVERLAPPED)&pIoData->Overlapped, 
							NULL);
		
		if (nRet != 0)
		{
			if ( (nErrorCode =WSAGetLastError()) != WSA_IO_PENDING )
			{
				printf("WSARecv error, error code: %d!!!", nErrorCode);
				break;
			}
		}

		WSAResetEvent(pOverLappedIoSet->hEvents[nEventIndex]);	/*信号量重置*/
	}

	return 0;
}  



int OverLapperdIoTest()
{
	SOCKET sockSrv = ServerSockInit();
	
	if (sockSrv == SOCKET_ERROR)
	{
		printf("Create Server Socket Error!!!\n");
		
		return -1;
	}
	
	/*信号数组*/
	OverLappedIoSet overLappedIoSet;
	memset(&overLappedIoSet, 0, sizeof(overLappedIoSet));
	overLappedIoSet.nSetCount = 0;
	
	/*创建辅助线程检测事件处理消息 */
	HANDLE hProcessIO = CreateThread(NULL, 0, OverLapperdIoProcessIO, &overLappedIoSet, 0, NULL);  
	if(hProcessIO)  
	{  
		CloseHandle(hProcessIO);  
	}  
	
	SOCKET sockClient;
	SOCKADDR_IN SockAddress;
	in_addr inaddr;
	INT nAddressLength = sizeof(SockAddress);
	
	while (TRUE)
	{
		sockClient=accept(sockSrv, (sockaddr*)&SockAddress, &nAddressLength);
		inaddr.s_addr=SockAddress.sin_addr.s_addr;

		printf("Socket Register, IP:%s, Port:%d \n", inet_ntoa(inaddr), SockAddress.sin_port);

		/*建立重叠数据*/
		LPPER_IO_OPERATION_DATA pIoData = new PER_IO_OPERATION_DATA;
		LPPER_HANDLE_DATA pHandle = new PER_HANDLE_DATA;

		WSAEVENT hTempEvent = WSACreateEvent();	/*创建新信号量*/

		memset(pIoData, 0, sizeof(PER_IO_OPERATION_DATA));
		pIoData->DataBuff.len = sizeof(pIoData->Buff);
		pIoData->DataBuff.buf = pIoData->Buff;
		pIoData->Overlapped.hEvent = hTempEvent;
		pHandle->Socket = sockClient;

		int nBufferTransfered;
		int nFlag = 0;
	   OVERLAPPEDIOSET_ADD(&overLappedIoSet, hTempEvent, pIoData, pHandle);		/*加到信号量数组里面*/

	   int nRet = WSARecv(sockClient, &(pIoData->DataBuff), 1, (LPDWORD)&nBufferTransfered, 
						  (LPDWORD)&nFlag, (LPWSAOVERLAPPED)&pIoData->Overlapped, NULL);

	   if (nRet != 0)
	   {
		   int nErrorCode = WSAGetLastError();
		   
		   if (nErrorCode != WSA_IO_PENDING )
		   {
				printf("WSARecv error, error code: %d!!!", nErrorCode);
				break;
		   }
	   }
	}
	
	return 0;
}


#endif

五:OverLapped I/O 完成例程模型


        负责人发现,只有人家干得多点,自己才能轻松快活点。所以他在刚才的基础上,写了个操作指引,让手下看到信号灯亮的时候能够按照指引去干活,自己就更省事了,不过遇到突发事件,可能操作指引不太适用哦。


#ifndef _IOCPRoutine_
#define _IOCPRoutine_ 

void CALLBACK CompletionROUTINE(
	IN DWORD dwError, 
	IN DWORD cbTransferred, 
	IN LPWSAOVERLAPPED lpOverlapped, 
	IN DWORD dwFlags
	)
{
	LPPER_IO_OPERATION_DATA pIoData = (LPPER_IO_OPERATION_DATA)lpOverlapped;

	if (cbTransferred == 0)
	{
		printf("Sock %d disconnect !!!\n", pIoData->Socket);
		return;
	}

	printf("Sock %d: Recv Data:%s !!!\n", pIoData->Socket, pIoData->Buff);

	/*继续投递*/
	int nBufferTransfered	= 0;
	int nFlag				= 0;
	
	memset(pIoData->Buff, 0, sizeof(pIoData->Buff));

	int nRet = WSARecv(	pIoData->Socket, 
						&(pIoData->DataBuff), 
						1, 
						(LPDWORD)&nBufferTransfered, 
						(LPDWORD)&nFlag, 
						(LPWSAOVERLAPPED)&pIoData->Overlapped, 
						CompletionROUTINE
					 );
	
	if (nRet != 0)
	{
	   int nErrorCode = WSAGetLastError();
	   if (nErrorCode != WSA_IO_PENDING )
	   {
		   printf("WSARecv error, error code: %d!!!");
	   }
	}
}


int IOCPRoutine()
{
	SOCKET sockSrv = ServerSockInit();
	
	if (sockSrv == SOCKET_ERROR)
	{
		printf("Create Server Socket Error!!!\n");
		
		return -1;
	}

	SOCKET sockClient;
	SOCKADDR_IN SockAddress;
	in_addr inaddr;
	INT nAddressLength		= sizeof(SockAddress);
	int nBufferTransfered	= 0;
	int nFlag				= 0;

	printf("Current Thread ID :%d !!!\n", GetCurrentThreadId());
	
	while (TRUE)
	{
		sockClient=accept(sockSrv, (sockaddr*)&SockAddress, &nAddressLength);
		inaddr.s_addr=SockAddress.sin_addr.s_addr;

		printf("Socket Register, IP:%s, Port:%d \n", inet_ntoa(inaddr), SockAddress.sin_port);

		/*建立重叠数据*/
		LPPER_IO_OPERATION_DATA pIoData = new PER_IO_OPERATION_DATA;

		memset(pIoData, 0, sizeof(PER_IO_OPERATION_DATA));
		pIoData->DataBuff.len = sizeof(pIoData->Buff);
		pIoData->DataBuff.buf = pIoData->Buff;
		pIoData->Socket = sockClient;

	   int nRet = WSARecv(	sockClient, 
							&(pIoData->DataBuff), 
							1, 
							(LPDWORD)&nBufferTransfered, 
							(LPDWORD)&nFlag, 
							(LPWSAOVERLAPPED)&pIoData->Overlapped, 
							CompletionROUTINE
						   );

	   if (nRet != 0)
	   {
		   int nErrorCode = WSAGetLastError();
		   if (nErrorCode != WSA_IO_PENDING )
		   {
				printf("WSARecv error, error code: %d!!!", nErrorCode);
				break;
		   }
	   }
	}
	return 0;
}

#endif


六:完成端口 IOCP模型


        好吧,效率提高了,码头的规模也准备扩大了,由之前的64个码头,扩充到几百个,好吧,信号灯不适用了。再说,码头多的话就会很乱。于是负责人决定实施规模化管理,采购了N台(N个线程)大型的自动机器,这几天自动机器的本领可大了,它负责几百个码头的活,无论哪个码头有货来,它就负责货品上岸,装集装箱,然后将货品拉到对应的地点,所以说,负责人只要到对应的地点收货,处理剩下的就行了,其他的都不用忧,好吧,这机器人真牛X.


#ifndef _IOCP_
#define _IOCP_
#include "StdAfx.h"
#include "DataBase.h"

DWORD WINAPI ProcessIO(LPVOID lpParam)  
{  
    HANDLE CompletionPort = (HANDLE)lpParam;  
    DWORD BytesTransferred;  
    LPPER_HANDLE_DATA PerHandleData;  
    LPPER_IO_OPERATION_DATA PerIoData;  
	printf("entering process");
	while(true)  
	{  
		if(0 == GetQueuedCompletionStatus(CompletionPort, &BytesTransferred, (LPDWORD)&PerHandleData, (LPOVERLAPPED*)&PerIoData, INFINITE))  
		{  
			if( (GetLastError() ==WAIT_TIMEOUT) || (GetLastError() == ERROR_NETNAME_DELETED) )  
			{   
				closesocket(PerHandleData->Socket);  
				delete PerIoData;  
				delete PerHandleData;  
				continue;  
			}  
			else  
			{  
				printf("GetQueuedCompletionStatus failed!");  
			}  
			return 0;  
		}  
		
		/* 说明客户端已经退出 */
		if(BytesTransferred == 0)  
		{  
			printf("Socket %d disconnect !!!\n", PerHandleData->Socket, PerIoData->Buff);
			closesocket(PerHandleData->Socket);  
			delete PerIoData;  
			delete PerHandleData;  
			continue;  
		}  
		
		/*取得数据并处理*/
		printf("Socket %d, Recv Data :%s \n", PerHandleData->Socket, PerIoData->Buff);
		
		/*继续向 socket 投递WSARecv操作 */ 
		DWORD Flags = 0;  
		DWORD dwRecv = 0;  
		ZeroMemory(PerIoData,sizeof(PER_IO_OPERATION_DATA));  
		PerIoData->DataBuff.buf =PerIoData->Buff;  
		PerIoData->DataBuff.len = DATA_BUFFER_SIZE;  
		WSARecv(PerHandleData->Socket,&PerIoData->DataBuff, 1, &dwRecv, &Flags,&PerIoData->Overlapped, NULL);  
	}  
	return 0;  
}  



INT IOCPTest()
{
	SOCKET sockSrv = ServerSockInit();

	
	/*创建完成端口句柄*/
	HANDLE hIocp=CreateIoCompletionPort(INVALID_HANDLE_VALUE,NULL,0,0);
	
	/*创建完成端口处理完成消息线程 理论创建线程数 = cpu核心数+1 */
	HANDLE hProcessIO = CreateThread(NULL, 0,ProcessIO, hIocp, 0, NULL);  
	if(hProcessIO)  
	{  
		CloseHandle(hProcessIO);  
	}  
	
	SOCKET sockClient;
	SOCKADDR_IN SockAddress;
	in_addr inaddr;
	INT nAddressLength = sizeof(SockAddress);

	while (TRUE)
	{
		sockClient=accept(sockSrv, (sockaddr*)&SockAddress, &nAddressLength);
		inaddr.s_addr=SockAddress.sin_addr.s_addr;

		printf("SOCK register, IP:%s, Port:%d \n", inet_ntoa(inaddr), SockAddress.sin_port);
		
		/*重叠IO标识结构和数据结构*/
		LPPER_HANDLE_DATA perHandleData=new PER_HANDLE_DATA;
		LPPER_IO_OPERATION_DATA perIoData=new PER_IO_OPERATION_DATA;
		ZeroMemory(perIoData,sizeof(PER_IO_OPERATION_DATA));
		perHandleData->Socket = sockClient;
		perIoData->DataBuff.buf = perIoData->Buff;
		perIoData->DataBuff.len = DATA_BUFFER_SIZE;
		
		/*绑定到完成端口*/
		CreateIoCompletionPort((HANDLE)sockClient,hIocp,(DWORD)perHandleData,0);

		/*向完成端口投递sock接收请求操作*/
		DWORD dwFlag=0;
		DWORD dwRecSize=0;
		WSARecv(sockClient, &perIoData->DataBuff, 1, &dwRecSize, &dwFlag, &perIoData->Overlapped, NULL);
	}
	
	
	return 0;
}


#endif

总结:

那么多网络模型,看得头都有点晕了,究竟哪个才是最好的,其实我觉得适合自己的才是最好的。

<1>假如你的软件只需要一个socket来通信,相信Recv,Send等阻塞函数都可以满足你的要求。


<2>若你要管理的socket可能有几个,那select是你的选择,相信遍历那几个不会耗费你很多资源。


<3>如果你在写客户端通信软件,而且用上mfc,那WSAAsyncSelect将会让让消息映射处理发挥得淋漓尽致。


<4>另外事件select 和 重叠IO 也是好东西,只不过事件数组有64的限制,真需要多的话那就躲开几个数组检测吧。


<5>完成端口例程貌似比前两个简单一点,无需再处理繁杂的事件信号,不过可定制差点,而且应该只能在发起操作的那个线程完成操作。


<6>假如你要做windows通信服务器的话,要管理庞大的socket连接的话,强烈建议完成端口IOCP,完善的线程池管理,加上重叠IO的结合,底层的高度优化,谁用谁知道好。

例子代码:http://download.csdn.net/detail/luoti784600/5705175



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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值