了解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>你是码头的负责人,负责将货品的处理。
公用的数据结构和函数:
一:select模型
在这种模型下,负责人只能轮流步行去各个码头询问有没有货要上岸,好吧,没有的话就继续下一个码头。若有得话就开始负责叫人处理货物,货品上岸,装集装箱,后面还有一大堆事情。好吧,处理完了,继续去下一个码头询问。好吧,一两个码头还行,hode得住,要是成千上万个码头可要命。那效率真低。
二:WSAAsyncSelect模型
这种模型下,负责人首先召集各个码头的调度人员,告诉他们:如果有货品要上岸,打电话给我啊,我电话是888 。好吧,于是有货的话,大家都忙着给负责人打电话,然后负责人接到电话后立刻赶往相应码头 处理货物,货品上岸,装集装箱,后面还有一大堆事情。好吧,要是忙碌的时候,可能负责人的电话各种占线,好吧,效率还是低,不过总比逐个码头跑好点。
注 : 由于WSAAsyncSelect采用与MFC相同的句柄--消息机制实现,原理是跟点击按钮触发消息,处理消息一样,由于要用到Mfc程序,这里不举例子!!
三:WSAEventSelect模型
好吧,负责人疲于奔命,所有他决定升级码头的系统,在信息中心为每个码头安装一个信号灯,负责人只要在信息中心看到哪些等亮起就知道有货要上岸了, 不过资金有限,只能安装64个信号灯,好吧,现在他可以雇多几个手下(线程),告诉他们灯亮起的时候应该怎么做。好吧,效率终于提高点了。
四:OverLapped I/O 模型
负责人厌恶了货物上岸,装集装箱的操作,它决定将这些事情交给码头调度员。 每个码头上放一个空的集装箱(重叠),有货物要上岸的时候,码头调度员直接给其上岸,然后将其装好集装箱。然后再通过信号灯通知负责人,负责人再安排手下去干剩下的工作,干完了再把空集装箱放码头上,通过充分利用码头调度员的资源,现在效率更高了。
五:OverLapped I/O 完成例程模型
负责人发现,只有人家干得多点,自己才能轻松快活点。所以他在刚才的基础上,写了个操作指引,让手下看到信号灯亮的时候能够按照指引去干活,自己就更省事了,不过遇到突发事件,可能操作指引不太适用哦。
六:完成端口 IOCP模型
好吧,效率提高了,码头的规模也准备扩大了,由之前的64个码头,扩充到几百个,好吧,信号灯不适用了。再说,码头多的话就会很乱。于是负责人决定实施规模化管理,采购了N台(N个线程)大型的自动机器,这几天自动机器的本领可大了,它负责几百个码头的活,无论哪个码头有货来,它就负责货品上岸,装集装箱,然后将货品拉到对应的地点,所以说,负责人只要到对应的地点收货,处理剩下的就行了,其他的都不用忧,好吧,这机器人真牛X.
总结:
那么多网络模型,看得头都有点晕了,究竟哪个才是最好的,其实我觉得适合自己的才是最好的。
<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
一: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
相关文章推荐
- windows 网络编程 几种套接字模型
- windows环境下Socket编程的几种模型
- .NET平台下几种SOCKET模型的简要性能供参考(转)
- Socket网络通信方式/模型
- windows环境下Socket编程的几种模式
- .NET平台下几种SOCKET模型的简要性能供参考
- Socket 网络模型之Select篇
- windows下socket网络编程小节
- 几种经典的网络服务器架构模型的分析与比较
- windows上的5种网络通信模型示例代码
- 几种网络I/O模型
- 几种经典的网络服务器架构模型的分析与比较
- Windows Socket IO 模型
- 幽默讲解Windows支持的五种Socket I/O模型【转】
- OSI七层网络模型与TCP/IP四层网络模型及在java中对应API(Socket、HttpServlet)实现
- 几种服务器网络编程模型
- .NET平台下几种SOCKET模型的简要性能供参考
- ub 网络框架的几种线程模型
- 几种经典的网络服务器架构模型的分析与比较
- Windows Socket IO 模型(转)