一个对Winsock 完成端口模型封装的类

源代码说明: 
在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重写完毕,我就写了这篇文章对完成端口模型做一个总结,并介绍一下我的这个类。

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

 

/* ++

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

================================================================================


 

 

/*++

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)this0, 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


BOOL CompletionPortModel::ThreadLoop()
/* ++

Fucntion Description:
  
   主线程循环,用WaitForSigleObject等待m_hEvent,已经发出的AcceptEx()调用耗尽,FD_ACCEPT
事件将被触发,WaitForSigleObject成功返回,然后调用PostAcceptEx()来新发出10个AcceptEx()调用。
WaitForSigleObject每次等待10秒,超时返回后,对系统中已经建立成功了的并且还没有收发过数据的
SOCKET连接进行检测,如果某个连接已经建立了30秒,并且还没收发过数据,则强制关闭。

Arguments:
     
   无。

Return Value:
      
   函数调用成功返回TRUE,调用失败返回FALSE;
     
--
*/
{
 
int  nOptval;
 
int  nOptlen;
 
int  nResult;
 DWORD dwResult;
 
int  nCounter  =   0 ;

#ifdef _DEBUG
 
int  nTimeOut  =   0 ;
#endif

 cout 
<<   " Server is running.......... "   <<  nCounter  <<   "  times "   <<  endl; 
 
 
while  (TRUE)
 {  
  dwResult 
=  WaitForSingleObject(m_hEvent,  10000 );
 
  
if  (WAIT_FAILED  ==  dwResult)
  {
   PostQueuedCompletionStatus(m_hCOP, 
0 , NULL, NULL);
   cout 
<<   " WSAWaitForMultipleEvents() failed:  "   <<  WSAGetLastError()  <<  endl;

   
return  FALSE;
  }
  
  
if  (WAIT_TIMEOUT  ==  dwResult)
  { 
   nCounter
++ ;
   cout 
<<   " Server is running.......... "   <<  nCounter  <<   "  times "   <<  endl;
   
#ifdef _DEBUG
   nTimeOut
++ ;
   cout 
<<  nTimeOut  <<   " *******TIME_OUT******** "   <<  nTimeOut  <<  endl;
#endif
   
   PPER_IO_CONTEXT lpCurentNode 
=  m_lpConnectionListHead -> pNext;
   PPER_IO_CONTEXT lpPreNode 
=  m_lpConnectionListHead;
   PPER_IO_CONTEXT lpTempNode; 
   
   
while  (NULL  !=  lpCurentNode)
   { 
    EnterCriticalSection(
& m_ListCriSection);
    nOptlen 
=   sizeof (nOptval);
    nResult 
=  getsockopt(
     lpCurentNode
-> sClient,
     SOL_SOCKET,
     SO_CONNECT_TIME,
     (
char * ) & nOptval,
     
& nOptlen
     );
#ifdef _DEBUG
    cout 
<<   " nOptval =  "   <<  nOptval  <<  endl;
#endif  _DEBUG
    
    
if  (SOCKET_ERROR  ==  nResult)
    {
     cout 
<<   " SO_CONNECT_TIME failed:  "   <<  WSAGetLastError()  <<  endl;
     lpPreNode 
=  lpCurentNode;
     lpCurentNode 
=  lpCurentNode -> pNext;
     LeaveCriticalSection(
& m_ListCriSection);
     
     
continue ;
    }
    
if  ((nOptval != 0xFFFFFFFF &&  (nOptval > 30 ))
    {
     lpPreNode
-> pNext  =  lpCurentNode -> pNext;
     lpTempNode 
=  lpCurentNode;
     lpCurentNode 
=  lpCurentNode -> pNext;
     closesocket(lpTempNode
-> sClient);
     lpTempNode
-> pNext  =  NULL;
     InsertToLookaside(lpTempNode, NULL);
    }
    
else
    {
     lpPreNode 
=  lpCurentNode;
     lpCurentNode 
=  lpCurentNode -> pNext;
    }
    LeaveCriticalSection(
& m_ListCriSection);
   }
  }
  
else
  {
   
if  (WAIT_TIMEOUT  !=  dwResult)
   {
    
if  (FALSE  ==  PostAcceptEx())
    {
     PostQueuedCompletionStatus(m_hCOP, 
0 , NULL, NULL);
     
     
return  FALSE;
    }
   }
  }
 }

 
return  TRUE;
}
// end of CheckConnectTime


void  CompletionPortModel::ReleaseNode(PPER_IO_CONTEXT pNode)
/* ++

Fucntion Description:

     将参数中传递的结点从链表中解除,但不释放结点。以便不让ThreadLoop函数对其进行超时检测。
此函数在完成端口线程里收发数据成功后调用。

Arguments:
     
  要从链表中释放的结点。

Return Value:

     无。
     
--
*/
{
 PPER_IO_CONTEXT pTempNode 
=  m_lpConnectionListHead -> pNext;
 PPER_IO_CONTEXT pPreNode 
= m_lpConnectionListHead;
 PPER_IO_CONTEXT pDeleteNode;
 
 
while  (NULL  !=  pTempNode)
 {
  
if  (pNode -> unId  ==  pTempNode -> unId)
  {  
   pPreNode
-> pNext  =  pTempNode -> pNext;
   pDeleteNode 
=  pTempNode;
   pTempNode 
=  pTempNode -> pNext;
   
   
return ;
  }
  
else
  { 
   pPreNode 
=  pTempNode;
   pTempNode 
=  pTempNode -> pNext;
  }
 }

 
return ;
}
// end of RealeseNode


BOOL CompletionPortModel::HandleData(PPER_IO_CONTEXT lpPerIoContext, 
int  nFlags)
/* ++

Fucntion Description:

      根据传进来的nFlags参数对lpPerIoContext进行设置,并指示下一步IO操作。

Arguments:
     
      lpPerIoContext - 调用GetQueueCompletionStatus函数得到的上一次IO操作的结果(扩展的
                      WSAOVERLAPPED结构)。
      nFlags         - 指明已经完成上一次IO的操作类型。

Return Value:

      函数调用成功返回TRUE,失败返回FALSE。
     
--
*/
{
 
//
 
// nFlags == IO_READ_COMPLETION表示完成的上一次IO操作是WSARecv。
 
//
  if  (IO_READ_COMPLETION  ==  nFlags)
 {
  
//
  
// 完成了WSARecv,接下来需要调用WSASend把刚接收到的数据发送回去,把
  
// lpPerIoContext->ContinueAction = ContinueWrite;
  
//
  lpPerIoContext -> IoOperation  =  IoWrite;
  ZeroMemory(
& (lpPerIoContext -> ol),  sizeof (WSAOVERLAPPED));
  
  
//
  
// 接收到的数据在lpPerIoContext->wsaBuffer.buf里,可以调用
  
// 自定义函数对数据自行处理,本例中,简单的将数据再发送回去
  
//
  strcpy(lpPerIoContext -> szBuffer, lpPerIoContext -> wsaBuffer.buf);
  lpPerIoContext
-> wsaBuffer.buf  =  lpPerIoContext -> szBuffer;
  lpPerIoContext
-> wsaBuffer.len  =  BUFFER_SIZE;
  
  
return  TRUE;
 }

 
if  (IO_WRITE_COMPLETION  ==  nFlags)
 {
  
//
  
// 上一次IO操作WSASend数据发送完成,将后续操作标志设置为关闭
  
// 如果不需要关闭而是要继续发送,将lpPerIoContext->IoOperation设置为
  
// IoWrite,如果要继续接收,将lpPerIoContext->IoOperation设置为
  
// IoRead,并初始化好BUFFER,本例中,设置关闭
  
//
  lpPerIoContext -> IoOperation  =  IoEnd;

  
return  TRUE;
 }
 
if  (IO_ACCEPT_COMPLETION  ==  nFlags)
 {
  
//
  
// 刚建立了一个连接,并且没有收发数据,,,,
  
//
  lpPerIoContext -> IoOperation  =  IoRead;
  ZeroMemory(
& (lpPerIoContext -> ol),  sizeof (WSAOVERLAPPED));
  ZeroMemory(lpPerIoContext
-> szBuffer, BUFFER_SIZE);
  lpPerIoContext
-> wsaBuffer.len  =  BUFFER_SIZE;
  lpPerIoContext
-> wsaBuffer.buf  =  lpPerIoContext -> szBuffer;

  
return  TRUE;
 }
 
 
 
return  FALSE;
}
//  end of HandleData()


BOOL CompletionPortModel::InitLinkListHead()
/* ++

Fucntion Description:

     初始化链表头指针。

Arguments:
     无。

Return Value:
     函数调用成功返回TRUE,失败返回FALSE。
     
--
*/
{
 m_lpConnectionListHead 
=  (PPER_IO_CONTEXT)HeapAlloc(GetProcessHeap(), 
  HEAP_ZERO_MEMORY, 
sizeof (PER_IO_CONTEXT));
 
 
if  (NULL  ==  m_lpConnectionListHead)
 {
  cout 
<<   " HeapAlloc() failed  "   <<  endl;
  
  
return  FALSE;
 }
 m_lpConnectionListHead
-> pNext  =  NULL;
 
 
return  TRUE;
}
//  end of InitLinkListHead()


BOOL CompletionPortModel::AllocEventMessage()
/* ++

Fucntion Description:

     将FD_ACCEPT事件注册到m_hEvent,这样当可用AcceptEx调用被耗尽的时候,就会触发FD_ACCEPT
  事件,然后ThreadLoop里的WaitForSingleObject就会成功返回,导致PostAcceptEx被调用。

Arguments:
     无。

Return Value:
     函数调用成功返回TRUE,失败返回FALSE。
     
--
*/
{
 m_hEvent 
=  CreateEvent(NULL, FALSE, FALSE, NULL);
 
if  (NULL  ==  m_hEvent)
 {
  PostQueuedCompletionStatus(m_hCOP, 
0 , NULL, NULL);
  cout 
<<   " CreateEvent() failed:  "   <<  GetLastError()  <<  endl;

  
return  FALSE;
 }
 
 
int  nResult  =  WSAEventSelect(m_ListenSocket, m_hEvent, FD_ACCEPT);
 
if  (SOCKET_ERROR  ==  nResult)
 {
  PostQueuedCompletionStatus(m_hCOP, 
0 , NULL, NULL);
  CloseHandle(m_hEvent);

  cout 
<<   " WSAEventSeclet() failed:  "   <<  WSAGetLastError()  <<  endl;
  
  
return  FALSE;
 }

 
return  TRUE;
}
// end of AllocEventMessage()


BOOL CompletionPortModel::DataAction(PPER_IO_CONTEXT lpPerIoContext, PPER_HANDLE_CONTEXT lpNewperHandleContext)
/* ++

Fucntion Description:

     根据参数lpPerIoContext的成员IoOperation的值来进行下一步IO操作。

Arguments:
     
  lpPerIoContext        - 将随WSASend或者WSARecv一起投递的扩展WSAOVERLAPPED结构。
  lpNewperHandleContext - AcceptEx调用成功后给新套接字分配的“单句柄数据”。

Return Value:
     函数调用成功返回TRUE,失败返回FALSE。
     
--
*/
{
 
int  nResult;
 DWORD dwIosize 
=   0 ;
 DWORD dwFlags 
= 0 ;
 
if  (IoWrite  ==  lpPerIoContext -> IoOperation)
 {
  nResult 
=  WSASend(lpPerIoContext -> sClient,
     
& (lpPerIoContext -> wsaBuffer),
     
1 ,
     
& dwIosize,
     
0 ,
     
& (lpPerIoContext -> ol),
     NULL
     );
  
if ((SOCKET_ERROR == nResult)  &&  (ERROR_IO_PENDING  !=  WSAGetLastError()))
  {
   cout 
<<   " WSASend() failed:  "   <<  WSAGetLastError()  <<  endl;
   
   closesocket(lpPerIoContext
-> sClient);
   lpPerIoContext
-> pNext  =  NULL;
   lpNewperHandleContext
-> pNext  =  NULL;
   InsertToLookaside(lpPerIoContext, NULL);
   InsertToLookaside(NULL, lpNewperHandleContext);
   
   
return  FALSE;
  }
 }
 
 
if  (IoRead  ==  lpPerIoContext -> IoOperation)
 {
  nResult 
=  WSARecv(lpPerIoContext -> sClient,
     
& (lpPerIoContext -> wsaBuffer),
     
1 ,
     
& dwIosize,
     
& dwFlags,
     
& (lpPerIoContext -> ol),
     NULL
     );
     
  
if ((SOCKET_ERROR == nResult)  &&  (ERROR_IO_PENDING  !=  WSAGetLastError()))
  {
   cout 
<<   " WSARecv() failed:  "   <<  WSAGetLastError()  <<  endl;
    
   closesocket(lpPerIoContext
-> sClient);
   lpNewperHandleContext
-> pNext  =  NULL;
   lpPerIoContext
-> pNext  =  NULL;
   InsertToLookaside(lpPerIoContext, NULL);
   InsertToLookaside(NULL, lpNewperHandleContext);
     
   
return  FALSE;
  }
   
 }
    
 
if  (IoEnd  ==  lpPerIoContext -> IoOperation)
 {
  closesocket(lpPerIoContext
-> sClient);
  lpNewperHandleContext
-> pNext  =  NULL;
  InsertToLookaside(NULL, lpNewperHandleContext);
  lpPerIoContext
-> pNext  =  NULL;
  InsertToLookaside(lpPerIoContext, NULL);
 }

 
return  TRUE;
}
//  end of DataAction()


void  CompletionPortModel::GetAddressAndPort()
/* ++

Fucntion Description:

     由类构造函数调用的函数,用来输入服务器要绑定的本地IP地址和端口。

Arguments:
     
  无。

Return Value:
     
 无。   
     
--
*/
{
 cout 
<<   " Please input a port:  " ;
 cin 
>>  uPort;
 cout 
<<   " Please input localaddress: " ;
 cin 
>>  szAddress;

 system(
" cls " );

 
return ;
}
//  end of GetAddressAdnPort



void  CompletionPortModel::InsertToLookaside(PPER_IO_CONTEXT lpIoNode, PPER_HANDLE_CONTEXT lpHandleNode)
/* ++

Fucntion Description:

     给旁视列表的链表中插入一个空闲的结点。

Arguments:
     
  lpIoNode     - 要插入的结点,类型为PPER_IO_CONTEXT。
  lpHandleNode - 要插入的结点,类型为PPER_HANDLE_CONTEXT。

Return Value:
     
 无。   
     
--
*/
{
 
if  (NULL  !=  lpIoNode)
 {
  
if  (NULL  ==  m_lpIoLookasideLists)
  {
   m_lpIoLookasideLists 
=  lpIoNode;
   
   
return ;
  }
  lpIoNode
-> pNext  =  m_lpIoLookasideLists -> pNext;
  m_lpIoLookasideLists
-> pNext  =  lpIoNode;
  
  
return ;
 }

 
if  (NULL  !=  lpHandleNode)
 {
  
if  (NULL  ==  m_lpHandleLOOKasideLists)
  {
   m_lpHandleLOOKasideLists 
=  lpHandleNode;
   
   
return ;
  }
  lpHandleNode
-> pNext  =  m_lpHandleLOOKasideLists -> pNext;
  m_lpHandleLOOKasideLists
-> pNext  =  lpHandleNode;
  
  
return ;
 }

 
return ;
}


PPER_IO_CONTEXT CompletionPortModel::GetIoFromLookaside()
/* ++

Fucntion Description:

     从旁视列表中解除一个结点并将其返回。

Arguments:
     
  无。

Return Value:
     
 返回一个PPER_IO_CONTEXT类型的结点。   
     
--
*/
{
 
if  (NULL  ==  m_lpIoLookasideLists)
 {
  
return  NULL;
 }
 EnterCriticalSection(
& m_IoCriSection);
 PPER_IO_CONTEXT lpReturnNode 
=  m_lpIoLookasideLists;
 m_lpIoLookasideLists 
=  m_lpIoLookasideLists -> pNext;
 LeaveCriticalSection(
& m_IoCriSection);

 
return  lpReturnNode;
}


PPER_HANDLE_CONTEXT CompletionPortModel::GetHandleFromLookaside()
/* ++

Fucntion Description:

     从旁视列表中解除一个结点并将其返回。

Arguments:
     
  无。

Return Value:
     
 返回一个PPER_HANDLE_CONTEXT类型的结点。   
     
--
*/
{
 
if  (NULL  ==  m_lpHandleLOOKasideLists)
 {
  
return  NULL;
 }
 EnterCriticalSection(
& m_HandleCriSection);
 PPER_HANDLE_CONTEXT lpReturnNode 
=  m_lpHandleLOOKasideLists;
 m_lpHandleLOOKasideLists 
=  m_lpHandleLOOKasideLists -> pNext;
 LeaveCriticalSection(
& m_HandleCriSection);
 
 
return  lpReturnNode;
}

================================================================================
/* ++

Copyright (c) 2004 

模块名: 

     main.cpp

模块描述:

     主线程文件

作者:

     PPP elssann@hotmail.com

开发环境:
    
     Visual C++ 6.0, Windows 2000.
     
修订记录:

     创建于: 2004.1.16

最后修改日期:
      2004.1.23


--
*/

#include 
" iomodel.h "
#include 
< winsock2.h >
#include 
< windows.h >
#include 
< iostream.h >

#pragma  comment(lib, "ws2_32.lib")


void  main()
{
 CompletionPortModel p;
 p.Init();
 p.AllocEventMessage();
 
if  (FALSE  ==  p.PostAcceptEx())
 {
  
return ;
 }
 p.ThreadLoop();

 
return ;
}

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值