您的位置:首页 > 理论基础 > 计算机网络

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

2013-07-05 11:21 477 查看
本文主要讲述的是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
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息