完成端口的Win32实例及ACE实现对比

9 篇文章 1 订阅
2 篇文章 0 订阅

 

完成端口(IOC)是用来实现高性能服务的一种常用方法,主要是通过操作系统提供的异步调用功能实现IO操作,可以通过很少的线程数实现高性能的并发服务。 ACE的前摄器(Proactor)模式在Windows下就是通过完成端口实现的
 
1. 下面是网上一个 利用完成端口技术实现的高性能文件下载服务程序的源码:
(用Visual C++ 6.0 SP5+2003 Platform SDK 编译通过)

 

/********************************************************************
     created:     2005/12/24
     created:     24:12:2005   20:25
     modified:     2005/12/24
     filename:     d:/vcwork/iocomp/iocomp.cpp
     file path:     d:/vcwork/iocomp
     file base:     iocomp
     file ext:     cpp
     author:         kruglinski(kruglinski_at_gmail_dot_com)
    
     purpose:     利用完成端口技术实现的高性能文件下载服务程序
*********************************************************************/
 
#define _WIN32_WINNT     0x0500
 
#include <cstdlib>
#include <clocale>
#include < ctime >
#include < iostream > // 一使用输入输出流程序顿时增大 70K
#include < vector >
#include <algorithm>
#include <winsock2. h >
#include <mswsock. h >
 
using namespace std;
 
#pragma comment (lib, "ws2_32.lib" )
#pragma comment (lib, "mswsock.lib" )
 
const int MAX_BUFFER_SIZE =1024;
const int PRE_SEND_SIZE =1024;
const int QUIT_TIME_OUT =3000;
const int PRE_DOT_TIMER = QUIT_TIME_OUT /80;
 
typedef enum{IoTransFile,IoSend,IoRecv,IoQuit} IO_TYPE;
 
typedef struct
{
      SOCKET hSocket ;
     SOCKADDR_IN ClientAddr ;
} PRE_SOCKET_DATA ,* PPRE_SOCKET_DATA ;
 
typedef struct
{
     OVERLAPPED     oa ;
     WSABUF         DataBuf ;
     char          Buffer [ MAX_BUFFER_SIZE ];
     IO_TYPE         IoType ;
} PRE_IO_DATA ,* PPRE_IO_DATA ;
 
typedef vector < PPRE_SOCKET_DATA >     SocketDataVector ;
typedef vector < PPRE_IO_DATA >         IoDataVector ;
 
SocketDataVector      gSockDataVec ;
IoDataVector          gIoDataVec ;
 
CRITICAL_SECTION      csProtection ;
 
char * TimeNow(void)
{
     time_t t = time ( NULL );
     tm * localtm = localtime (& t );
     static char timemsg [512]={0};
    
     strftime ( timemsg ,512, "%Z: %B %d %X,%Y" , localtm );
     return timemsg ;
}
 
BOOL TransFile( PPRE_IO_DATA pIoData , PPRE_SOCKET_DATA pSocketData , DWORD dwNameLen )
{
     // 这一句是为 nc 做的 , 你可以修改它
     pIoData -> Buffer [ dwNameLen -1]= '/0' ;
    
     HANDLE hFile = CreateFile ( pIoData -> Buffer , GENERIC_READ ,0, NULL , OPEN_EXISTING ,0, NULL );
     BOOL bRet = FALSE ;
 
     if( hFile != INVALID_HANDLE_VALUE )
     {
         cout << "Transmit File " << pIoData -> Buffer << " to client" << endl ;
         pIoData -> IoType =IoTransFile;
         memset (& pIoData -> oa ,0,sizeof(OVERLAPPED));
         *reinterpret_cast< HANDLE *>( pIoData -> Buffer )= hFile ;
         TransmitFile ( pSocketData -> hSocket , hFile , GetFileSize ( hFile , NULL ), PRE_SEND_SIZE ,reinterpret_cast< LPOVERLAPPED >( pIoData ), NULL ,TF_USE_SYSTEM_THREAD);
         bRet = WSAGetLastError ()== WSA_IO_PENDING ;
     }
     else
         cout << "Transmit File " << "Error:" << GetLastError ()<< endl ;
 
     return bRet ;
}
 
DWORD WINAPI ThreadProc( LPVOID IocpHandle )
{
     DWORD dwRecv =0;
     DWORD dwFlags =0;
    
     HANDLE hIocp =reinterpret_cast< HANDLE >( IocpHandle );
     DWORD dwTransCount =0;
     PPRE_IO_DATA pPreIoData = NULL ;
     PPRE_SOCKET_DATA pPreHandleData = NULL ;
 
     while( TRUE )
     {
         if( GetQueuedCompletionStatus ( hIocp ,& dwTransCount ,
             reinterpret_cast< LPDWORD >(& pPreHandleData ),
             reinterpret_cast< LPOVERLAPPED *>(& pPreIoData ), INFINITE ))
        {
             if(0== dwTransCount &&IoQuit!= pPreIoData -> IoType )
             {
                 cout << "Client:"
                     << inet_ntoa ( pPreHandleData -> ClientAddr .sin_addr)
                     << ":" << ntohs ( pPreHandleData -> ClientAddr . sin_port )
                     << " is closed" << endl ;
 
                 closesocket ( pPreHandleData -> hSocket );
 
                 EnterCriticalSection (& csProtection );
                     IoDataVector :: iterator itrIoDelete = find ( gIoDataVec . begin (), gIoDataVec . end (), pPreIoData );
                     SocketDataVector :: iterator itrSockDelete = find ( gSockDataVec . begin (), gSockDataVec . end (), pPreHandleData );
                   delete * itrIoDelete ;
                   delete * itrSockDelete ;
                   gIoDataVec . erase ( itrIoDelete );
                   gSockDataVec . erase ( itrSockDelete );
                 LeaveCriticalSection (& csProtection );
 
                       
                 continue;
             }
            
             switch( pPreIoData -> IoType ){
             case IoTransFile:
                 cout << "Client:"
                     << inet_ntoa ( pPreHandleData -> ClientAddr .sin_addr)
                     << ":" << ntohs ( pPreHandleData -> ClientAddr . sin_port )
                     << " Transmit finished" << endl ;
                 CloseHandle (*reinterpret_cast< HANDLE *>( pPreIoData -> Buffer ));
                 goto LRERECV ;
                
             case IoSend:
                 cout << "Client:"
                     << inet_ntoa ( pPreHandleData -> ClientAddr .sin_addr)
                     << ":" << ntohs ( pPreHandleData -> ClientAddr . sin_port )
                     << " Send finished" << endl ;
 
LRERECV :
                 pPreIoData -> IoType =IoRecv;
                 pPreIoData -> DataBuf . len = MAX_BUFFER_SIZE ;
                 memset (& pPreIoData -> oa ,0,sizeof(OVERLAPPED));
 
                 WSARecv ( pPreHandleData -> hSocket ,& pPreIoData -> DataBuf ,1,
                     & dwRecv ,& dwFlags ,
                     reinterpret_cast< LPWSAOVERLAPPED >( pPreIoData ), NULL );
 
                 break;
 
             case IoRecv:
                 cout << "Client:"
                     << inet_ntoa ( pPreHandleData -> ClientAddr .sin_addr)
                     << ":" << ntohs ( pPreHandleData -> ClientAddr . sin_port )
                     << " recv finished" << endl ;
                 pPreIoData -> IoType =IoSend;
                
                 if(!TransFile( pPreIoData , pPreHandleData , dwTransCount ))
                 {
                     memset (& pPreIoData -> oa ,0,sizeof(OVERLAPPED));
                     strcpy ( pPreIoData -> DataBuf . buf , "File transmit error!/r/n" );
                     pPreIoData -> DataBuf . len = strlen ( pPreIoData -> DataBuf . buf );
                    
                     WSASend ( pPreHandleData -> hSocket ,& pPreIoData -> DataBuf ,1,
                         & dwRecv , dwFlags ,
                         reinterpret_cast< LPWSAOVERLAPPED >( pPreIoData ), NULL );
                 }
                 break;
                
             case IoQuit:
                 goto LQUIT ;
                
             default:
                 ;
             }
         }    
     }
    
LQUIT :
     return 0;
}
 
HANDLE hIocp = NULL ;
SOCKET hListen = NULL ;
 
BOOL WINAPI ShutdownHandler( DWORD dwCtrlType )
{
     PRE_SOCKET_DATA PreSockData ={0};
     PRE_IO_DATA PreIoData ={0};
 
     PreIoData . IoType =IoQuit;
 
     if( hIocp )
     {
         PostQueuedCompletionStatus ( hIocp ,1,
             reinterpret_cast< ULONG_PTR >(& PreSockData ),
             reinterpret_cast< LPOVERLAPPED >(& PreIoData ));
 
         cout << "Shutdown at " <<TimeNow()<< endl << "wait for a moment please" << endl ;
        
         // 让出 CPU 时间 , 让线程退出
         for( int t =0; t <80; t +=1)
         {
             Sleep ( PRE_DOT_TIMER );
             cout << "." ;
         }
        
         CloseHandle ( hIocp );
     }
    
     int i =0;
 
     for(; i < gSockDataVec . size (); i ++)
     {
         PPRE_SOCKET_DATA pSockData = gSockDataVec [ i ];
         closesocket ( pSockData -> hSocket );
         delete pSockData ;
     }
 
     for( i =0; i < gIoDataVec . size (); i ++)
     {
         PPRE_IO_DATA pIoData = gIoDataVec [ i ];
         delete pIoData ;
     }
 
     DeleteCriticalSection (& csProtection );
     if( hListen )
         closesocket ( hListen );
 
     WSACleanup ();
     exit (0);
     return TRUE ;
}
 
LONG WINAPI MyExceptionFilter(struct _EXCEPTION_POINTERS * ExceptionInfo )
{
     ShutdownHandler(0);
     return EXCEPTION_EXECUTE_HANDLER ;
}
 
u_short DefPort =8182;
 
int main ( int argc , char ** argv )
{
     if( argc ==2)
         DefPort = atoi ( argv [1]);
 
     InitializeCriticalSection (& csProtection );
     SetUnhandledExceptionFilter (MyExceptionFilter);
     SetConsoleCtrlHandler (ShutdownHandler, TRUE );
 
     hIocp = CreateIoCompletionPort ( INVALID_HANDLE_VALUE , NULL ,0,0);
 
     WSADATA data ={0};
     WSAStartup (0x0202,& data );
 
     hListen = socket ( AF_INET , SOCK_STREAM , IPPROTO_TCP );
     if( INVALID_SOCKET == hListen )
     {
         ShutdownHandler(0);
     }
    
     SOCKADDR_IN addr ={0};
     addr . sin_family = AF_INET ;
     addr . sin_port = htons ( DefPort );
    
     if( bind ( hListen ,reinterpret_cast< PSOCKADDR >(& addr ),
         sizeof( addr ))== SOCKET_ERROR )
     {
         ShutdownHandler(0);
     }
    
     if( listen ( hListen ,256)== SOCKET_ERROR )
         ShutdownHandler(0);
 
     SYSTEM_INFO si ={0};
     GetSystemInfo (& si );
     si . dwNumberOfProcessors <<=1;
 
     for( int i =0; i < si . dwNumberOfProcessors ; i ++)
     {
        
         QueueUserWorkItem(ThreadProc, hIocp ,WT_EXECUTELONGFUNCTION);
     }
    
     cout << "Startup at " <<TimeNow()<< endl
         << "work on port " << DefPort << endl
         << "press CTRL+C to shutdown" << endl << endl << endl ;
 
     while( TRUE )
     {
         int namelen =sizeof( addr );
         memset (& addr ,0,sizeof( addr ));
          SOCKET hAccept = accept ( hListen ,reinterpret_cast< PSOCKADDR >(& addr ),& namelen );
 
         if( hAccept != INVALID_SOCKET )
         {
             cout << "accept a client:" << inet_ntoa ( addr .sin_addr)<< ":" << ntohs ( addr . sin_port )<< endl ;
 
             PPRE_SOCKET_DATA pPreHandleData = new PRE_SOCKET_DATA ;
             pPreHandleData -> hSocket = hAccept ;
             memcpy (& pPreHandleData -> ClientAddr ,& addr ,sizeof( addr ));
            
             CreateIoCompletionPort (reinterpret_cast< HANDLE >( hAccept ), hIocp ,reinterpret_cast< DWORD >( pPreHandleData ),0);
            
             PPRE_IO_DATA pPreIoData = new ( nothrow ) PRE_IO_DATA
 
             if( pPreIoData )
             {
                 EnterCriticalSection (& csProtection );
                 gSockDataVec . push_back ( pPreHandleData );
                 gIoDataVec . push_back ( pPreIoData );
                 LeaveCriticalSection (& csProtection );
 
                 memset ( pPreIoData ,0,sizeof( PRE_IO_DATA ));
                 pPreIoData -> IoType =IoRecv;
                  pPreIoData -> DataBuf . len = MAX_BUFFER_SIZE ;
                 pPreIoData -> DataBuf . buf = pPreIoData -> Buffer ;
                 DWORD dwRecv =0;
                 DWORD dwFlags =0;
                 WSARecv ( hAccept ,& pPreIoData -> DataBuf ,1,& dwRecv ,& dwFlags ,reinterpret_cast<WSAOVERLAPPED*>( pPreIoData ), NULL );
             }
             else
             {
                 delete pPreHandleData ;
                 closesocket ( hAccept );
             }
         }
     }
    
     return 0;
}

 

2. 而使用ACE前摄器模式的话,IOC架构更加清晰,扩展性更强,而且是跨平台的!在ACE包里面ACE_wrappers/examples/Reactor/Proactor下面有几个例子,可以说明这个应用:
l         test_udp_proactor   基于 Proactor UDP 服务器以及客户端的例子。服务端先打开一个 udp 服务端口,然后在 ACE_Asynch_Read_Dgram 上异步读数据。运行这个应用时带 -h 参数时做服务器,不带时做客户端。
l         test_timeout 基于 Proactor 的计时器事件处理例子。首先在前摄器框架上注册计时器 ACE_Proactor :: instance ()-> schedule_timer() ,然后激活 Worker 线程池,每个工作线程的 svc() 函数中,调用 ACE_Proactor :: run_event_loop () 处理计时器到期事件 。这和反应器框架的主要不同在于,前摄器的线程池中的每个线程可以处理不同计时器的到时事件。
l         test_proactor 基于 Proactor TCP 文件服务器以及客户端的例子,功能类似于前面的“ 利用完成端口技术实现的高性能文件下载服务程序 ”。其中客户端 Sender 读取一个本地文件,然后写到服务端去( 512 字节一组),读取和发送都是异步的。服务端每来一个连接,就创建一个 Receiver ,这个 Receiver 接收客户端发来的数据,并把它存到文件(默认的名字叫 output ),接收和保存两者都是异步的。
 
Main 函数功能:
              服务端首先创建一个异步连接器:
                               ACE_Asynch_Acceptor < Receiver > acceptor ;
              然后打开异步连接器,进入服务状态:
                               acceptor . open()
              再进入前摄器主事件分派循环:
                               ACE_Proactor :: instance ()-> handle_events ()
 
连接到来时会创建一个 Receiver 并由 OS 调用 Receiver :: open() ,并且打开 < ACE_Asynch_Write_File > < ACE_Asynch_Read_Stream > ,准备进行读取 TCP 连接发来的数据和将数据写文件的工作,然后调用 initiate_read_stream() 进行第一次的数据接收工作。
如果这两项工作被 OS 完成,则回调 handle_write_file() handle_read_stream()
 
这个实例是单线程的。在多 CPU 环境下,需要改成工作者线程模式。
 
另外测试时发现test_proactor实例有个错误的地方:
1)  Receiver :: open() 里面,默认的文件打开函数打不开文件
/*Stephen :下面的直接执行会导致打不开,报 ACE_OS::open: 参数错误。
 this->dump_file_ = ACE_OS::open (dump_file,
                                   O_CREAT | O_RDWR | O_TRUNC | FILE_FLAG_OVERLAPPED,
                                   0644);
 */
 this-> dump_file_ = ACE_OS:: open ( dump_file , O_CREAT | O_RDWR | O_TRUNC | FILE_FLAG_OVERLAPPED );
 
  // Stephen :曾经试图把上面第二个参数改为 GENERIC_READ | GENERIC_WRITE | FILE_FLAG_OVERLAPPED ,结果不行,不是打不开文件,就是 input/output error
 // Stephen :失败是由于 0644 参数造成的,这个是干什么用的呢?
 
还有每次计划从文件读回来1024字节,但显示读出只有512字节,何故?
//Stephen :后来发现 stdio.h 里面 BUFSIZ 确实是 512 ,而且它在 include 路径的前面,导致每次都读 512 字节,在 ace 里面一些地方才是 1024 for WinCE
int
Sender :: initiate_read_file (void)
{
  // Create a new <Message_Block>. Note that this message block will
  // be used both to <read> data asynchronously from the file and to
  // <write> data asynchronously to the socket.
  ACE_Message_Block * mb = 0;
  ACE_NEW_RETURN ( mb ,
                  ACE_Message_Block ( BUFSIZ + 1),
                  -1);
 
  // Inititiate an asynchronous read from the file
 // 由于是异步的,所以 下面的 read() 函数很快返回,当 OS 读完后,回调 Sender::handle_read_file() 函数去处理发送,并在这个函数中激发下一次 512 字节的读
 if (this-> rf_ . read (* mb ,
                      mb -> size () - 1,
                      this-> file_offset_ ) == -1)
    ACE_ERROR_RETURN ((LM_ERROR,
                       "%p/n" ,
                       "ACE_Asynch_Read_File::read" ),
                      -1);
 return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值