转载请按如下方式显示标明原创作者及出处,以示尊重!!

原创作者:elssann

联系方式:PPP elssann@hotmail.com

在Windows下进行网络服务端程序开发,毫无疑问,Winsock 完成端口模型是最高效的。Winsock的完成端口模型借助Widnows的重叠IO和完成端口来实现,完成端口模型懂了之后是比较简单的,但是要想掌握Winsock完成端口模型,需要对WINDOWS下的线程、线程同步,Winsock API以及WINDOWS IO机制有一定的了解。如果不了解,推荐几本书:《Inside Windows 2000,《WINDOWS核心编程 》,《WIN32多线程程序设计》、《WINDOWS网络编程技术》。在去年,我在C语言下用完成端口模型写了一个WebSERVER,前些天,我决定用C++重写这个WEBSERVER,给这个WEBSERVER增加了一些功能,并改进完成端口操作方法,比如采用AcceptEx来代替accept和使用LOOKASIDE LIST来管理内存,使得WEBSERVER的性能有了比较大的提高。
  
  在重写的开始,我决定把完成端口模型封装成一个比较通用的C++类,针对各种网络服务端程序的开发,只要简单地继承这个类,改写其中两个虚拟函数就能满足各种需要。到昨天为止,WEBSERVER重写完毕,我就写了这篇文章对完成端口模型做一个总结,并介绍一下我的这个类。
  
  一:完成端口模型
  
  至于完成端口和Winsock完成端口模型的详细介绍,请参见我上面介绍的那几本书,这里只是我个人对完成端口模型理解的一点心得。
  
  首先我们要抽象出一个完成端口大概的处理流程 :
  
  1:创建一个完成端口。
  
  2:创建一个线程A。
  
  3:A线程循环调用GetQueuedCompletionStatus()函数来得到IO操作结果,这个函数是个阻塞函数。
  
  4:主线程循环里调用accept等待客户端连接上来。
  
  5:主线程里accept返回新连接建立以后,把这个新的套接字句柄用CreateIoCompletionPort关联到完成端口,然后发出一个异步的WSASend或者WSARecv调用,因为是异步函数,WSASend/WSARecv会马上返回,实际的发送或者接收数据的操作由WINDOWS系统去做。
  
  6:主线程继续下一次循环,阻塞在accept这里等待客户端连接。
  
  7:WINDOWS系统完成WSASend或者WSArecv的操作,把结果发到完成端口。

这里。
  
  具体的流程请看附图,其中红线表示是Windows系统进行的处理,不需要我们程序干预。


    归根到底概括完成端口模型一句话:
  
  我们不停地发出异步的WSASend/WSARecv IO操作,具体的IO处理过程由WINDOWS系统完成,WINDOWS系统完成实际的IO处理后,把结果送到完成端口上(如果有多个IO都完成了,那么就在完成端口那里排成一个队列)。我们在另外一个线程里从完成端口不断地取出IO操作结果,然后根据需要再发出WSASend/WSARecv IO操作。

二:提高完成端口效率的几种有效方法
  
  1:使用AcceptEx代替accept。AcceptEx函数是微软的Winsosk 扩展函数,这个函数和accept的区别就是:accept是阻塞的,一直要到有客户端连接上来后accept才返回,而AcceptEx是异步的,直接就返回了,所以我们利用AcceptEx可以发出多个AcceptEx调用
  
  等待客户端连接。另外,如果我们可以预见到客户端一连接上来后就会发送数据(比如WebSERVER的客户端浏览器),那么可以随着AcceptEx投递一个BUFFER进去,这样连接一建立成功,就可以接收客户端发出的数据到BUFFER里,这样使用的话,一次AcceptEx调用相当于accpet和recv的一次连续调用。同时,微软的几个扩展函数针对操作系统优化过,效率优于WINSOCK 的标准API函数。
  
  2:在套接字上使用SO_RCVBUF和SO_SNDBUF选项来关闭系统缓冲区。这个方法见仁见智,详细的介绍可以参考《Windows核心编程》第9章。这里不做详细介绍,我封装的类中也没有使用这个方法。
  
  3:内存分配方法。因为每次为一个新建立的套接字都要动态分配一个“单IO数据”和“单句柄数据”的数据结构,然后在套接字关闭的时候释放,这样如果有成千上万个客户频繁连接时候,会使得程序很多开销花费在内存分配和释放上。这里我们可以使用lookaside list。开始在微软的platform sdk里的SAMPLE里看到lookaside list,我一点不明白,MSDN里有没有。后来还是在DDK的文档中找到了,,
  
  lookaside list
  
  A system-managed queue from which entries of a fixed size can be allocated and into which entries can be deallocated dynamically. Callers of the Ex(ecutive) Support lookaside list routines can use a lookaside list to manage any dynamically sized set of fixed-size buffers or structures with caller-determined contents.
  
  For example, the I/O Manager uses a lookaside for fast allocation and deallocation of IRPs and MDLs. As another example, some of the system-supplied SCSI class drivers use lookaside lists to allocate and release memory for SRBs.
  lookaside list名字比较古怪(也许是我孤陋寡闻,第一次看到),其实就是一种内存管理方法,和内存池使用方法类似。我个人的理解:就是一个单链表。每次要分配内存前,先查看这个链表是否为空,如果不为空,就从这个链表中解下一个结点,则不需要新分配。如果为空,再动态分配。使用完成后,把这个数据结构不释放,而是把它插入到链表中去,以便下一次使用。这样相比效率就高了很多。在我的程序中,我就使用了这种单链表来管理。
  
  在我们使用AcceptEx并随着AcceptEx投递一个BUFFER后会带来一个副作用:比如某个客户端只执行一个connect操作,并不执行send操作,那么AcceptEx这个请求不会完成,相应的,我们用GetQueuedCompletionStatus在完成端口中得不到操作结果,这样,如果有很多个这样的连接,对程序性能会造成巨大的影响,我们需要用一种方法来定时检测,当某个连接已经建立并且连接时间超过我们规定的时间而且没有收发过数据,那么我们就把它关闭。检测连接时间可以用SO_CONNECT_TIME来调用getsockopt得到。
  
  还有一个值得注意的地方:就是我们不能一下子发出很多AcceptEx调用等待客户连接,这样对程序的性能有影响,同时,在我们发出的AcceptEx调用耗尽的时候需要新增加AcceptEx调用,我们可以把FD_ACCEPT事件和一个EVENT关联起来,然后用WaitForSingleObject等待这个Event,当已经发出AccpetEx调用数目耗尽而又有新的客户端需要连接上来,FD_ACCEPT事件将被触发,EVENT变为已传信状态,

  WaitForSingleObject返回,我们就重新发出足够的AcceptEx调用。
  
  关于完成端口模型就介绍到这里。下面介绍我封装的类,这个类写完后,我用这个类做了个ECHOSERVER。

void main()

{

CompletionPortModel p;

p.Init();

p.AllocEventMessage();

if (FALSE == p.PostAcceptEx())

{

return;

}

p.ThreadLoop();

return;

}

我在我自己的机器上测试,

客户端的代码是

for (int i=0; i<10000; i++)

{

SOCKET s = socket(….);

connect(….);

send(…);

recv(…..)

cout << buffer << endl;

}

  结果客户端程序在循环到3000多次的时候死掉,但是服务端程序运行良好,重新启动客户端程序,发送接收数据正常。
  
  使用的时候,只需要从这个类派生一个子类,并改写HandleData和DataAction这两个虚函数,对于那些需要连续发送相关联的数据应用(比如传送文件),使用者需要自己扩展这两个函数,比如创建一个全局队列,每次从完成端口里得到数据后插入队列,然后用另外一个线程专门处理这个队列。。。
  
  从结果来看,这个类还有不少需要改进的地方,比如没考虑多处理器上运行的情况。没有考虑完成端口线程阻塞情况,如果考虑完成端口阻塞情况,那么应该创建CPU数据*2个完成端口线程等等,,因为我同时正在做的毕业设计NDIS驱动防火墙开发正在一个比较难的地方卡住了,时间和精力有限,就没有对这个类进行进一步完善,程序中也许有不合理和错误的地方,请高手多多指教。对于高性能的服务端程序开发是比较难的,记得有次和腾讯一个技术人员聊天,他说,像腾讯QQ的开发,难点不在客户端,而在服务端各个服务器之间的通信和同步。服务端程程序的集群和负载平衡是一个很复杂的问题,我在这方面刚接触,希望能有更多的高手出来共享自己的经验。
  
  封装这个类的时候,我把最新的PLATFORM SDK里的例子看了一遍,借鉴了其中很多思路和方法,在此对写这个例子的微软程序员表示感谢:)
    
源代码说明:
在WINDOWS下进行网络服务端程序开发,毫无疑问,Winsock 完成端口模型是最高效的。Winsock的完成端口模型借助Widnows的重叠IO和完成端口来实现,完成端口模型懂了之后是比较简单的,但是要想掌握Winsock完成端口模型,需要对WINDOWS下的线程、线程同步,Winsock API以及WINDOWS IO机制有一定的了解。如果不了解,推荐几本书:《Inside Windows 2000,《WINDOWS核心编程》,《WIN32多线程程序设计》、《WINDOWS网络编程技术》。在去年,我在C语言下用完成端口模型写了一个WEBSERVER,前些天,我决定用C++重写这个WEBSERVER,给这个WEBSERVER增加了一些功能,并改进完成端口操作方法,比如采用AcceptEx来代替accept和使用LOOKASIDE LIST来管理内存,使得WEBSERVER的性能有了比较大的提高。

/*++ Copyright (c) 2004 模块名: iomodel.cpp 模块描述: Winsock 完成端口类实现文件 作者: PPP elssann@hotmail.com 开发环境: Visual C++ 6.0, Windows 2000. 修订记录: 创建于: 2004.1.16 最后修改日期: 2004.1.23 --*/ #include <iostream.h> #include <winsock2.h> #include <mswsock.h> #include "iomodel.h" CompletionPortModel::CompletionPortModel() /*++ 函数描述: 构造函数,初始化线程句柄数组,初始化AcceptEx()调用的计数。初始化临界段代码变量。 Arguments: 无。 Return Value: 无。 --*/ { for (int i=0; i< MAXTHREAD_COUNT; i++) { m_hThreadArray[i] = INVALID_HANDLE_VALUE; } m_lAcceptExCounter = 0; InitializeCriticalSection(&m_ListCriSection); InitializeCriticalSection(&m_HandleCriSection); InitializeCriticalSection(&m_IoCriSection); m_lpHandleLOOKasideLists = NULL; m_lpIoLookasideLists = NULL; #ifndef _DEBUG GetAddressAndPort(); #endif }//end of CompletionPortModel() CompletionPortModel::~CompletionPortModel() /*++ 函数描述: 析构函数,释放链表所有结点。 Arguments: 无。 Return Value: --*/ { PPER_IO_CONTEXT lpIoNode; while (m_lpConnectionListHead->pNext) { lpIoNode = m_lpConnectionListHead->pNext; m_lpConnectionListHead->pNext = lpIoNode->pNext; closesocket(lpIoNode->sClient); HeapFree(GetProcessHeap(), 0, lpIoNode); } while(NULL != m_lpIoLookasideLists) { lpIoNode = m_lpIoLookasideLists; m_lpIoLookasideLists = m_lpIoLookasideLists->pNext; HeapFree(GetProcessHeap(), 0, lpIoNode); } PPER_HANDLE_CONTEXT lpHandleNode; while(NULL != m_lpHandleLOOKasideLists) { lpHandleNode = m_lpHandleLOOKasideLists; m_lpHandleLOOKasideLists = m_lpHandleLOOKasideLists->pNext; HeapFree(GetProcessHeap(), 0, lpHandleNode); } DeleteCriticalSection(&m_ListCriSection); DeleteCriticalSection(&m_HandleCriSection); DeleteCriticalSection(&m_IoCriSection); }//end of ~CompletionPortModel() BOOL CompletionPortModel::Init() /*++ 函数描述: 初始化,创建完成端口、创建完成端口线程,并调用类成员函数InitWinsock初始化Winsock、 建立一个监听套接字m_ListenSocket,并将此套接字同完成端口关联起来,获取AcceptEx指针。 Arguments: 无。 Return Value: 函数调用成功返回TRUE,失败返回FALSE。 --*/ { BOOL bSuccess = InitLinkListHead(); if (FALSE == bSuccess) { return FALSE; } m_hCOP = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, NULL, 0); if (NULL == m_hCOP) { cout << "CreateIoCompletionPort() failed: " << GetLastError() << endl; return FALSE; } // //取得系统中CPU的数目,创建和CPU数目相等的线程,如果事先估计到完成端口处理线程会堵塞, //可以考虑创建 SysInfo.dwNumberOfProcessors*2个线程。一般在单处理器上创建和CPU数目相等 //的线程就可以了 // SYSTEM_INFO SysInfo; GetSystemInfo(&SysInfo); if (MAXTHREAD_COUNT < SysInfo.dwNumberOfProcessors) { SysInfo.dwNumberOfProcessors = MAXTHREAD_COUNT; } for (int i=0; i<(int)SysInfo.dwNumberOfProcessors; i++) { m_hThreadArray[i] = CreateThread(NULL, 0, CompletionRoutine, (LPVOID)this, 0, NULL); if (NULL == m_hThreadArray[i]) { while (i>0) { CloseHandle(m_hThreadArray[i-1]); m_hThreadArray[i-1] = INVALID_HANDLE_VALUE; i--; }//end of while cout << "CreateThread() failed: " << GetLastError() << endl; CloseHandle(m_hCOP); HeapFree(GetProcessHeap(), 0, m_lpConnectionListHead); return FALSE; } }//end of for // //调用InitWinsock函数初始化Winsock、建立一个监听套接字m_ListenSocket, //并将此套接字同完成端口关联起来,获取AcceptEx指针。 // bSuccess = InitWinsock(); if (!bSuccess) { // //给完成端口线程发送消息,指示线程退出。 // PostQueuedCompletionStatus(m_hCOP, 0, NULL, NULL); CloseThreadHandle(); CloseHandle(m_hCOP); HeapFree(GetProcessHeap(), 0, m_lpConnectionListHead); return FALSE; } // //调用BindAndListenSocket()绑定套接字并将套接字置于监听状态 // bSuccess = BindAndListenSocket(); if (!bSuccess) { PostQueuedCompletionStatus(m_hCOP, 0, NULL, NULL); CloseThreadHandle(); CloseHandle(m_hCOP); HeapFree(GetProcessHeap(), 0, m_lpConnectionListHead); return FALSE; } return TRUE; }//end of Init() void CompletionPortModel::CloseThreadHandle() /*++ 函数描述: 对每一个创建的线程调用CloseHandle()。 Arguments: 无。 Return Value: 无。 --*/ { for (int i=0; i< MAXTHREAD_COUNT; i++) { if (INVALID_HANDLE_VALUE != m_hThreadArray[i]) { CloseHandle(m_hThreadArray[i]); m_hThreadArray[i] = INVALID_HANDLE_VALUE; } }//end of for return; }//end of CloseThreadHandle() BOOL CompletionPortModel::InitWinsock() /*++ 函数描述: 初始化Winsock,创建一个监听套接字,获取AcceptEx函数指针,为监听套接字分配一个单句柄 数据,并将监听套接字与完成端口hCOP关联。 Arguments: 无。 Return Value: 函数调用成功返回TRUE,失败返回FALSE。 --*/ { WSADATA wsd; int nResult = WSAStartup(MAKEWORD(2,2), &wsd); if (0 != nResult) { cout << "WSAStartup() failed" << endl; return FALSE; } m_ListenSocket = WSASocket(AF_INET, SOCK_STREAM, IPPROTO_IP, NULL, 0, WSA_FLAG_OVERLAPPED); if (INVALID_SOCKET == m_ListenSocket) { cout << "WSASocket() failed: " << WSAGetLastError() << endl; WSACleanup(); return FALSE; } DWORD dwResult; // //获取微软SOCKET扩展函数指针 // nResult = WSAIoctl( m_ListenSocket, SIO_GET_EXTENSION_FUNCTION_POINTER, &g_GUIDAcceptEx, sizeof(g_GUIDAcceptEx), &lpAcceptEx, sizeof(lpAcceptEx), &dwResult, NULL, NULL ); if (SOCKET_ERROR == nResult) { cout << "Get AcceptEx failed: " << WSAGetLastError() << endl; closesocket(m_ListenSocket); WSACleanup(); return FALSE; } nResult = WSAIoctl( m_ListenSocket, SIO_GET_EXTENSION_FUNCTION_POINTER, &g_GUIDTransmitFile, sizeof(g_GUIDTransmitFile), &lpTransmitFile, sizeof(lpTransmitFile), &dwResult, NULL, NULL ); if (SOCKET_ERROR == nResult) { cout << "Get TransmitFile failed: " << WSAGetLastError() << endl; closesocket(m_ListenSocket); WSACleanup(); return FALSE; } // //为监听套接字分配一个单句柄数据 // PPER_HANDLE_CONTEXT lpListenHandleContext = (PPER_HANDLE_CONTEXT)HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(PER_HANDLE_CONTEXT) ); if (NULL == lpListenHandleContext) { closesocket(m_ListenSocket); WSACleanup(); cout << "HeapAlloc() failed " << endl; return FALSE; } lpListenHandleContext->IoSocket = m_ListenSocket; lpListenHandleContext->pNext = NULL; // //将监听套接字m_ListenSocket和已经建立的完成端口关联起来 // HANDLE hrc = CreateIoCompletionPort( (HANDLE)m_ListenSocket, m_hCOP, (ULONG_PTR)lpListenHandleContext, 0 ); if (NULL == hrc) { closesocket(m_ListenSocket); HeapFree(GetProcessHeap(), 0, lpListenHandleContext); WSACleanup(); cout << "CreateIoCompletionPort failed: " << GetLastError() << endl; return FALSE; } return TRUE; }//end of InitWinsock() BOOL CompletionPortModel::BindAndListenSocket() /*++ 函数描述: private函数,供Init调用。 将监听套接字m_ListenSocket绑定到本地IP地址,并置于监听模式。 Arguments: 无。 Return Value: 函数调用成功返回TRUE,失败返回FALSE。 --*/ { SOCKADDR_IN InternetAddr; InternetAddr.sin_family = AF_INET; #ifdef _DEBUG InternetAddr.sin_addr.s_addr = inet_addr(LOCALADDRESS); InternetAddr.sin_port = htons(PORT); #else InternetAddr.sin_addr.s_addr = inet_addr(szAddress); InternetAddr.sin_port = htons(uPort); #endif int nResult = bind(m_ListenSocket, (PSOCKADDR)&InternetAddr, sizeof(InternetAddr)); if (SOCKET_ERROR == nResult) { WSACleanup(); closesocket(m_ListenSocket); cout << "bind() failed: " << WSAGetLastError() << endl; return FALSE; } nResult = listen(m_ListenSocket, 20); if (SOCKET_ERROR == nResult) { WSACleanup(); closesocket(m_ListenSocket); cout << "listen() failed: " << WSAGetLastError() << endl; return FALSE; } return TRUE; }//end of BindAndListenSocket() DWORD __stdcall CompletionRoutine(LPVOID Param) /*++ 函数描述: 完成端口处理线程,循环调用GetQueuedCompletionStatus来获取IO操作结果。 Arguments: Return Value: 线程退出代码。 --*/ { CompletionPortModel* pThis = (CompletionPortModel*)Param; DWORD dwNumberBytes; PPER_HANDLE_CONTEXT lpHandleContext = NULL; LPWSAOVERLAPPED lpOverlapped = NULL; int nResult; BOOL bSuccess; while (TRUE) { bSuccess = GetQueuedCompletionStatus( pThis->m_hCOP, &dwNumberBytes, (PULONG_PTR )&lpHandleContext, &lpOverlapped, INFINITE ); if (FALSE == bSuccess) { #ifndef _DEBUG cout << "GetQueuedCompletionStatus() failed: " << GetLastError() << endl; #endif continue; } if (NULL == lpHandleContext) { // //PostQueuedCompletionStatus发过来一个空的单句柄数据,表示线程要退出了。 // return 0; } PPER_IO_CONTEXT lpPerIoContext = (PPER_IO_CONTEXT)lpOverlapped; #ifdef _DEBUG cout << "recv buffer data: " << lpPerIoContext->szBuffer << endl; #endif if(IoAccept != lpPerIoContext->IoOperation) { if((!bSuccess) || (bSuccess && (0 == dwNumberBytes))) { closesocket(lpPerIoContext->sClient); lpPerIoContext->pNext = NULL; pThis->InsertToLookaside(lpPerIoContext, NULL); lpHandleContext->pNext = NULL; pThis->InsertToLookaside(NULL, lpHandleContext); continue; } } HANDLE hResult; PPER_HANDLE_CONTEXT lpNewperHandleContext; switch(lpPerIoContext->IoOperation) { case IoAccept : if (dwNumberBytes) { // //第一次连接成功并且收到了数据,将这个结点从链表中解除 // EnterCriticalSection(&pThis->m_ListCriSection); pThis->ReleaseNode(lpPerIoContext); LeaveCriticalSection(&pThis->m_ListCriSection); } nResult = setsockopt( lpPerIoContext->sClient, SOL_SOCKET, SO_UPDATE_ACCEPT_CONTEXT, (char *)&pThis->m_ListenSocket, sizeof(pThis->m_ListenSocket) ); if(SOCKET_ERROR == nResult) { cout << "SO_UPDATE_ACCEPT_CONTEXT failed to update accept socket" << endl; closesocket(lpPerIoContext->sClient); lpPerIoContext->pNext = NULL; pThis->InsertToLookaside(lpPerIoContext, NULL); continue; } lpNewperHandleContext = pThis->GetHandleFromLookaside(); if (NULL == lpNewperHandleContext) { lpNewperHandleContext = (PPER_HANDLE_CONTEXT)HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(PER_HANDLE_CONTEXT) ); if (NULL == lpNewperHandleContext) { cout << "HeapAlloc() for lpNewperHandlecontext failed" << endl; closesocket(lpPerIoContext->sClient); lpPerIoContext->pNext = NULL; pThis->InsertToLookaside(lpPerIoContext, NULL); continue; } } lpNewperHandleContext->IoSocket = lpPerIoContext->sClient; lpNewperHandleContext->pNext = NULL; // //将新建立的套接字关联到完成端口 // hResult = CreateIoCompletionPort( (HANDLE)lpPerIoContext->sClient,/ pThis->m_hCOP, (DWORD_PTR)lpNewperHandleContext, 0 ); if (NULL == hResult) { cout << "CreateIoCompletionPort() failed: " << GetLastError(); closesocket(lpPerIoContext->sClient); lpPerIoContext->pNext = NULL; lpNewperHandleContext->pNext = NULL; pThis->InsertToLookaside(lpPerIoContext, NULL); pThis->InsertToLookaside(NULL, lpNewperHandleContext); continue; } if (dwNumberBytes) { // //分析处理数据。 // pThis->HandleData(lpPerIoContext, IO_READ_COMPLETION); bSuccess = pThis->DataAction(lpPerIoContext, lpNewperHandleContext); if (FALSE == bSuccess) { continue; } } // //如果连接成功但是没有收到数据 // else { pThis->HandleData(lpPerIoContext, IO_ACCEPT_COMPLETION); bSuccess = pThis->DataAction(lpPerIoContext, lpNewperHandleContext); if (FALSE == bSuccess) { continue; } } break;//end of case IoAccept case IoRead: pThis->HandleData(lpPerIoContext, IO_READ_COMPLETION); bSuccess = pThis->DataAction(lpPerIoContext, lpNewperHandleContext); if (FALSE == bSuccess) { continue; } break;//end of case IoRead case IoWrite: pThis->HandleData(lpPerIoContext, IO_WRITE_COMPLETION); bSuccess = pThis->DataAction(lpPerIoContext, lpNewperHandleContext); if (FALSE == bSuccess) { continue; } break; default: continue; break; } } return 0; }//end of CompletionRoutine() BOOL CompletionPortModel::PostAcceptEx() /*++ Fucntion Description: 连续发出10个AcceptEx调用。 Arguments: Return Value: 函数调用成功返回TRUE,失败返回FALSE。 --*/ { while (m_lAcceptExCounter < 10) { SOCKET AcceptSocket = WSASocket(AF_INET, SOCK_STREAM, IPPROTO_IP, / NULL, 0, WSA_FLAG_OVERLAPPED); if (INVALID_SOCKET == AcceptSocket) { cout << "WSASocket failed " << endl; return FALSE; } PPER_IO_CONTEXT lpAcceptExIoContext = GetIoFromLookaside(); if (NULL == lpAcceptExIoContext) { lpAcceptExIoContext = (PPER_IO_CONTEXT)HeapAlloc(/ GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(PER_IO_CONTEXT)); if (NULL == lpAcceptExIoContext) { cout << "HeapAlloc() failed " << endl; closesocket(AcceptSocket); return FALSE; } } ZeroMemory(&(lpAcceptExIoContext->ol), sizeof(lpAcceptExIoContext->ol)); lpAcceptExIoContext->sClient = AcceptSocket; lpAcceptExIoContext->IoOperation = IoAccept; lpAcceptExIoContext->pNext = NULL; ZeroMemory(lpAcceptExIoContext->szBuffer, BUFFER_SIZE); lpAcceptExIoContext->wsaBuffer.buf = lpAcceptExIoContext->szBuffer; lpAcceptExIoContext->wsaBuffer.len = BUFFER_SIZE; lpAcceptExIoContext->unId = lpAcceptExIoContext->sClient; DWORD dwBytes; BOOL bSuccess = lpAcceptEx( m_ListenSocket, lpAcceptExIoContext->sClient, lpAcceptExIoContext->szBuffer, lpAcceptExIoContext->wsaBuffer.len - ((sizeof(SOCKADDR_IN) + 16) * 2), sizeof(SOCKADDR_IN) + 16, sizeof(SOCKADDR_IN) + 16, &dwBytes, &(lpAcceptExIoContext->ol)); if (FALSE == bSuccess) { int nResult = WSAGetLastError(); if (nResult != ERROR_IO_PENDING) { cout << "AcceptEx() failed :" << nResult << endl; closesocket(AcceptSocket); HeapFree(GetProcessHeap(), 0 , lpAcceptExIoContext); return FALSE; } InsertNode(lpAcceptExIoContext, NULL); InterlockedExchangeAdd(&m_lAcceptExCounter, 1); } } InterlockedExchangeAdd(&m_lAcceptExCounter, -10); return TRUE; }//end of PostAccetExRoutine() void CompletionPortModel::InsertNode(PPER_IO_CONTEXT pNode, PPER_HANDLE_CONTEXT pHandleNode) /*++ Fucntion Description: 根据参数类型将传递进来结点插入到相应的链表头。 Arguments: pNode - 要插入链表中的结点 pHandleNode - 要插入链表中的结点 Return Value: 无. --*/ { if (NULL != pNode) { EnterCriticalSection(&m_ListCriSection); pNode->pNext = m_lpConnectionListHead->pNext; m_lpConnectionListHead->pNext = pNode; LeaveCriticalSection(&m_ListCriSection); } return; }//end of InsertNode

/*++ Copyright (c) 2004 模块名: iomodel.h 模块描述: Winsock 完成端口类头文件 作者: PPP elssann@hotmail.com 开发环境: Visual C++ 6.0, Windows 2000. 修订记录: 创建于: 2004.1.16 最后修改日期: 2004.1.23 --*/ #ifndef _IOMODEL_H #define _IOMODEL_H // //Head files // #include <winsock2.h> #include <mswsock.h> / / / #ifdef __cplusplus extern "C" { #endif #define BUFFER_SIZE 4096 #define MAXTHREAD_COUNT 8 #define PORT 8080 #define LOCALADDRESS "172.29.90.96" #define IO_READ_COMPLETION 100 #define IO_WRITE_COMPLETION 200 #define IO_ACCEPT_COMPLETION 300 // //自定义枚举数据类型,用来标识套接字IO动作类型 // typedef enum _IO_OPERATION { IoAccept, //AcceptEx/accept IoRead, //WSARecv/recv/ReadFile IoWrite, //WSASend/send/WriteFile IoEnd }IO_OPERATION, *PIO_OPERATION; // //自定义结构,即“完成键”(单句柄数据) // typedef struct _PER_HANDLE_CONTEXT { SOCKET IoSocket; _PER_HANDLE_CONTEXT* pNext; }PER_HANDLE_CONTEXT, *PPER_HANDLE_CONTEXT; // //单IO数据,扩展的WSAOVERLAPPED // typedef struct _PER_IO_CONTEXT { WSAOVERLAPPED ol; char szBuffer[BUFFER_SIZE]; WSABUF wsaBuffer; SOCKET sClient; unsigned int unId; IO_OPERATION IoOperation; _PER_IO_CONTEXT* pNext; }PER_IO_CONTEXT, *PPER_IO_CONTEXT; // // global var // static GUID g_GUIDAcceptEx = WSAID_ACCEPTEX; static GUID g_GUIDTransmitFile = WSAID_TRANSMITFILE; DWORD __stdcall CompletionRoutine(LPVOID); // //完成端口模型类 // class CompletionPortModel { public: CompletionPortModel(); ~CompletionPortModel(); BOOL Init(); BOOL ThreadLoop(); BOOL AllocEventMessage(); BOOL PostAcceptEx(); virtual BOOL HandleData( PPER_IO_CONTEXT lpPerIoContext, int nFlags ); virtual BOOL DataAction( PPER_IO_CONTEXT lpPerIoContext, PPER_HANDLE_CONTEXT lpNewperHandletext ); void InsertNode(PPER_IO_CONTEXT pNode, PPER_HANDLE_CONTEXT pHandleNode); void ReleaseNode(PPER_IO_CONTEXT pNode); void InsertToLookaside(PPER_IO_CONTEXT lpIoNode, PPER_HANDLE_CONTEXT lpHandleNode); PPER_IO_CONTEXT GetIoFromLookaside(); PPER_HANDLE_CONTEXT GetHandleFromLookaside(); HANDLE m_hCOP; SOCKET m_ListenSocket; CRITICAL_SECTION m_ListCriSection; CRITICAL_SECTION m_HandleCriSection; CRITICAL_SECTION m_IoCriSection; LPFN_TRANSMITFILE lpTransmitFile; volatile PPER_IO_CONTEXT m_lpIoLookasideLists; volatile PPER_HANDLE_CONTEXT m_lpHandleLOOKasideLists; protected: BOOL InitWinsock(); BOOL BindAndListenSocket(); BOOL InitLinkListHead(); void CloseThreadHandle(); void GetAddressAndPort(); UINT uPort; char szAddress[20]; HANDLE m_hThreadArray[MAXTHREAD_COUNT]; HANDLE m_hEvent; volatile LONG m_lAcceptExCounter; volatile PPER_IO_CONTEXT m_lpConnectionListHead; LPFN_ACCEPTEX lpAcceptEx; private: }; #ifdef __cplusplus } #endif #endif //_IOMODEL_H

在重写的开始,我决定把完成端口模型封装成一个比较通用的C++类,针对各种网络服务端程序的开发,只要简单地继承这个类,改写其中两个虚拟函数就能满足各种需要。到昨天为止,WEBSERVER重写完毕,我就写了这篇文章对完成端口模型做一个总结,并介绍一下我的这个类。

DEMO就是一个ECHOSERVER,记得使用Release模式编译。

一个对Winsock完成端口模型封装的类相关推荐

  1. Winsock 完成端口模型简介

    摘自<Networking Programming for Microsoft Windows>第八章 "完成端口"模型是迄今为止最为复杂的一种I/O模型.然而,假若一 ...

  2. WinSock完成端口I/O模型

    关于重叠I/O,参考<WinSock重叠I/O模型>:关于完成端口的概念及内部机制,参考译文<深度探索I/O完成端口>. 完成端口对象取代了WSAAsyncSelect中的消息 ...

  3. Django中--自定义模型管理器类

    BookInfo.objects.all()->objects是一个什么东西呢? 答:objects是models.Manger类的一个对象,是Django帮我自动生成的管理器对象,通过这个管理 ...

  4. WinSock I/O 模型 -- IOCP 模型

    前言 IOCP 全称 Input/Ouput Completion Ports,中文中翻译一般为"完成端口",本文中我们使用 IOCP 简写. IOCP 模型是迄今为止最为复杂的一 ...

  5. WinSocket模型的探讨——完成端口模型

    众所皆知,完成端口是在WINDOWS平台下效率最高,扩展性最好的IO模型,特别针对于WINSOCK的海量连接时,更能显示出其威力.其实建立一个完成端口的服务器也很简单,只要注意几个函数,了解一下关键的 ...

  6. 一个简单的完成端口(服务端/客户端)类

    一个简单的完成端口(服务端/客户端)类 作者:spinoza 翻译:麦子芽儿, POWERCPP(后面部分内容) 下载源代码 原文网址:http://www.codeproject.com/KB/IP ...

  7. 高性能的 socket 通讯服务器(完成端口模型--IOCP)

    2019独角兽企业重金招聘Python工程师标准>>> 很多人费尽心思,都没有找到一个完美的 I/O CP 例程,甚至跟人于误解,先将本人编写的例程公布出来,希望对那些苦苦寻觅的人带 ...

  8. 【Django入门】——模型管理器对象、模型管理器类和模型类

    文章目录 一.模型管理器对象 1. 自定义模型管理器对象 2. 自定义模型管理器类 3. 自定义模型管理器类应用 3.1 重写框架的方法 3.2 封装自定义方法 4. 模型管理器对象的`model`属 ...

  9. [课程][原创]yolox检测封装成类调用几句代码完成目标检测任务

    搞定系列:yolox检测封装成类调用 课程地址:搞定系列:yolox检测封装成类调用--深度学习视频教程-人工智能-CSDN程序员研修院 你将收获 1.学会Yolox封装基本技巧和大体思路 2.学会Y ...

最新文章

  1. MQTT Qos类型解释
  2. html表格判断,lua table 长度解析
  3. Java后台 自动 翻页查询
  4. 用Aspose.Words for .NET动态生成word文档中的图片或水印
  5. pytorch安装-Windows(pip install失败)
  6. opencv摄像头 vmware虚拟机出现select timeout
  7. android背景图拉伸,Android使背景图像不拉伸它指定的视图_android_开发99编程知识库...
  8. (十四)算法设计思想之“贪心算法”
  9. 论文浅尝 | 对于知识图谱嵌入表示的几何形状理解
  10. python3--多目录之间的协作的一些必备知识
  11. 计算机网络—信道划分与介质访问控制
  12. python如何实现手眼定标_手把手教你如何实现Python手势识别与控制(含代码及动图)...
  13. Ubuntu修改源镜像方法(22.04也能用)附带常用源镜像地址
  14. matlab中的方波信号图片_MATLAB| 望远镜分辨率amp;艾里斑的模拟
  15. python用cartopy包画地图_python – 使用Cartopy在地图上显示图像时的投影问题
  16. 论文查找ICCV ECCV CVPR
  17. MFC怎么封装CreateWindow
  18. 网络基础之计算机网络参考模型(OSI参考模型与TCP/IP协议簇)
  19. Windows版navicat for mysql 11.2注册码
  20. python单选按钮控件_Tkinter 单选按钮

热门文章

  1. 使用JavaWeb将文件上传到后台服务器
  2. Winform 初识记录
  3. Vue packages version mismatch:- vue@2.6.14 - vue-template-compiler@2.6.11解决方法
  4. msyql之解决mysql出现ERROR 1698 (28000): Access denied的问题
  5. git的一些简单用法
  6. wampp更新php版本,有没有办法在XAMPP中使用两个PHP版本?
  7. 安卓gridview条目点击事件_敬业签的安卓手机版设置便签按正序排序教程
  8. 工作问题总结-----付款
  9. 怎么购买企业邮箱,才不被坑?
  10. 左耳朵耗子:聊聊分布式系统架构