一: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