C++完成端口组件实现

在windows平台上,处理众多的TCP连接的机制中效率最高的是完成端口模型,关于完成端口模型的介绍可参照《Win32 多线程程序设计》(侯捷翻译)和《windows网络编程》。

异步机制是完成端口的基础,完成端口模型的本质思想是将"启动异步操作的线程"和"提供服务的线程"(即工作者线程)拆伙。

先看看所有TCP连接管理组件都要实现的接口 ITcpManager

#ifndef ITCPMANAGER_H
#define ITCPMANAGER_H
#include "../../Infrastructure/IEvent.h"
#include "IMessageDispatcher.h"

/*
 ITcpManager 所有基于TCP网络管理者的基础接口 。
 
 创 作 者:sky
 时    间:2005.6 
 修订时间:2005.6
*/

enum TcpUserAction
{
 UA_Connected , UA_Disconnected , UA_FunctionAccess , //标准的功能访问  
};

enum DisconnectedCause
{
 Logoff ,
 LineOff ,
 ServerStopped , //服务器停止时关闭连接
 InvalidUser ,   //强行关闭非法用户的连接
 TimeOut         //在一段时间内没有收到check消息
};

//
interface ITcpClientsController
{
public:
 virtual ~ITcpClientsController(){}
 
 //主动给某个客户发信息 ,线程安全
 virtual void SendData(int ConnectID, byte* data ,int offset ,int size) = 0  ;
 
 //主动关闭连接
 virtual void DisposeOneConnection(int connectID ,DisconnectedCause cause) = 0 ;
};

class UserActionEventArgs
{
public:
 int ConnectID ;
 TcpUserAction action ;
};

class ServiceCommittedEventArgs
{
public:
 char* data ;
 int   length ;
 int   connectID ;
};

interface ITcpManager : public ITcpClientsController
{
 
public:
 virtual ~ITcpManager(){}
 
 //Events
 IEvent<ITcpManager* ,int>* ConnectionCountChanged ;
 IEvent<ITcpManager* ,ServiceCommittedEventArgs&>* ServiceCommitted ;
 IEvent<ITcpManager* ,UserActionEventArgs&>* UserAction ;

 //Method
 virtual void Start() = 0 ;
 virtual void Stop() = 0 ;
 
 virtual int ConnectionCount() = 0 ; 
};

#endif

接口的定义很清晰明了--这也是接口的本质,如果你对IEvent事件还不是很熟悉,请参见我前面的文章。

接下来看看实现了ITcpManager的完成端口组件的头文件:

#ifndef COMPLETINGPORT_H
#define COMPLETINGPORT_H

#include <WINSOCK2.H>
#include <afx.h>

#include "../Foundation/ITcpManager.h"
#include "../Foundation/TCPListener.h"
#include "../Foundation/IMessageDispatcher.h"
#include "../../Collection/SafeObjectList.h"
#include "WorkerThreadAssistant.h"
#include "ContextKeyManager.h"

typedef SafeObjectList<HANDLE> OverlapEventList ;

void WorkerThreadStart(void* para) ;
void ListenThreadStart(void* para) ;

class CompletingPortManager :public ITcpManager ,public IEventHandler<ContextKeyManager* ,int>
{
private:
 WorkerThreadAssistant workerThread_assistant ;
 OverlapEventList overlapEventList ;
 ContextKeyManager* context_manager ; 
 TcpListener* listener ;
 IMessageDispatcher* curDispatcher ;

 HANDLE completing_port ;
 int workThreadNum ;
 volatile bool isStop ;

 

public: //interface接口
 //Events
 IEvent<ITcpManager* ,int>*       ConnectionCountChanged ;
 IEvent<ITcpManager* ,ServiceCommittedEventArgs&>* ServiceCommitted ;
 IEvent<ITcpManager* ,UserActionEventArgs&>*   UserAction ;

 //Method
 void Start()  ;
 void Stop()  ;
 
 int ConnectionCount()  ;
 
 //主动给某个客户发信息 
 void SendData(int ConnectID, byte* data ,int offset ,int size)   ; 
 //主动关闭连接
 void DisposeOneConnection(int connectID ,DisconnectedCause cause) ;
 
public:
 friend void WorkerThreadStart(void* para) ;
 friend void ListenThreadStart(void* para) ;
 
 CompletingPortManager(IMessageDispatcher* i_dispatcher ,int port ,int workThread_Num )
 {
  this->isStop = true ; 
  this->workThreadNum = workThread_Num ;
  this->context_manager = new ContextKeyManager() ;
  this->listener = new TcpListener(port) ;
  this->curDispatcher = i_dispatcher ; 

  //subscribe Event
  this->context_manager->ConnectionCountChanged->Register(this) ;
  
  //event 
  this->ConnectionCountChanged = new EventPublisher<ITcpManager* ,int> ;
  this->ServiceCommitted       = new EventPublisher<ITcpManager* ,ServiceCommittedEventArgs&> ;
  this->UserAction    = new EventPublisher<ITcpManager* ,UserActionEventArgs&> ;
  
  this->completing_port = CreateIoCompletionPort(INVALID_HANDLE_VALUE ,NULL ,0 ,0 ) ; 
 }

 ~CompletingPortManager()
 {
  CloseHandle(this->completing_port) ;
  delete this->context_manager ;
  delete this->listener ;

  //event
  delete this->ConnectionCountChanged ;
  delete this->ServiceCommitted ;
  delete this->UserAction ;
 }

 //ContextKeyManager::ConnectionCountChanged事件
 void HandleEvent(ContextKeyManager* sender ,int para)
 {
  ((EventPublisher<ITcpManager* ,int>*)(this->ConnectionCountChanged))->Invoke(this ,para) ;
  
 } 
 
private:
 void ListenThread() ;
 void CreateWorkerThreads() ;
 void ServeCmpltdOverlap() ;
 void BindConnectionToCompPort(SOCKET connection ,ContextKey** out_pky);
 //进行异步接收数据
 void RecieveData(ContextKey* pkey);

 void CloseAllWorkerThread() ;
    void CloseAllOverlapEvent() ;
 void ClearRespondStreamList(RespondStreamList* rs_list) ;
 
};

#endif

完成端口组件的.CPP文件如下:

#include "CompletingPortManager.h"
#include "../../Threading/Thread.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

//global
void WorkerThreadStart(void* para)
{
    CompletingPortManager* cpm = (CompletingPortManager*)para ;
    cpm->ServeCmpltdOverlap() ;
}

void ListenThreadStart(void* para)
{
    CompletingPortManager* cpm = (CompletingPortManager*)para ;
    cpm->ListenThread() ;
}

//public
void CompletingPortManager::Start()
{
 if(this->isStop)
 {
  this->isStop = FALSE ;
  this->CreateWorkerThreads() ;

  Thread thread ;
  thread.Start(ListenThreadStart ,this) ;
  //unsigned int  dwThreadId;  
  //HANDLE hThread = (HANDLE)_beginthreadex(NULL, 0 , (unsigned int (_stdcall*)(void*))&ListenThreadStart , this, 0, &dwThreadId);   
 }
}

//public
void CompletingPortManager::Stop()
{
 if(this->isStop)
 {  
  return ;
 } 
 
 this->isStop = true ;
 Sleep(500) ; //等待监听线程结束 
    this->listener->Close() ;
     
 this->CloseAllWorkerThread() ;//等所有线程退出后才返回  
 this->context_manager->ClearAllKeys() ; //关闭所有连接
 this->CloseAllOverlapEvent() ;  
}

//public
int CompletingPortManager::ConnectionCount()
{
 return this->context_manager->GetConnectionCount() ;
}

//public
void CompletingPortManager::SendData(int ConnectID, byte* data ,int offset ,int size)
{
 ContextKey* key = this->context_manager->GetContextKey(ConnectID) ;
 if(key != NULL)
 {
  key->netStream->Write((char*)(data +offset) ,size) ;
 }
}

//public
void CompletingPortManager::DisposeOneConnection(int connectID ,DisconnectedCause cause)
{
 this->context_manager->ClearPkey(connectID) ;
 
 UserActionEventArgs args ;
 args.action    = UA_Disconnected ;
 args.ConnectID = connectID ;
 ((EventPublisher<ITcpManager* ,UserActionEventArgs&>*)(this->UserAction))->Invoke(this ,args) ;
 
}

//private
void CompletingPortManager::CloseAllWorkerThread()
{
 while(! this->workerThread_assistant.IsSafeToExit())//等待到达安全点
 {
  Sleep(200) ;  
 } 
}

//private
void CompletingPortManager::CloseAllOverlapEvent()
{
    int count = this->overlapEventList.Count() ;
 for(int i=0 ;i<count ;i++)
 {
  HANDLE handle ;
  this->overlapEventList.GetElement(i ,handle) ;
  CloseHandle(handle) ;
 }
 
 this->overlapEventList.Clear() ;
}

//private
void CompletingPortManager::CreateWorkerThreads()
{
    SYSTEM_INFO  sysinfo;  
    DWORD        dwThreads;
    DWORD        i;
   
    GetSystemInfo(&sysinfo);
    dwThreads = this->workThreadNum ; //sysinfo.dwNumberOfProcessors * 32 + 2;
    for (i=0; i<dwThreads; i++)
    {
  Thread thread ;
  thread.Start(WorkerThreadStart ,this) ;
       // HANDLE hThread;
       // hThread = (HANDLE)_beginthreadex(NULL, 0, (unsigned int (_stdcall*)(void*))&WorkerThreadStart , this, 0, &dwThreadId);
        workerThread_assistant.IncreaseWorkingThreadNum() ;
    }
}

//private
void CompletingPortManager::BindConnectionToCompPort(SOCKET connection ,ContextKey** out_pky)
{
    ContextKey* pkey = new ContextKey(connection) ;  
    *out_pky = pkey ;
   
    CreateIoCompletionPort((HANDLE)connection ,this->completing_port ,(DWORD)pkey ,0);   
}

//public
void CompletingPortManager::ListenThread()
{
 if(! this->isStop)
 {
  return ;
 }
 
 SOCKET connection ;
 
 BOOL succeed = this->listener->Start() ;
 if(!succeed)
 { 
  MessageBox(NULL ,"无法启动监听线程 !" ,"Tip" ,0) ;  
  return ;
 } 
 
 ContextKey* pkey  ;    
 
 while((! this->isStop) && (this->listener->Pending()))
 { 
  connection = this->listener->AcceptSocket() ; //当关闭监听socket时,该函数也会返回
  if(this->isStop)
  {   
   closesocket(connection) ;  
   break ;
  }

  this->BindConnectionToCompPort(connection ,&pkey) ;
  this->context_manager->RegisterContextKey(pkey) ;

  UserActionEventArgs args ;
  args.action    = UA_Connected ;
  args.ConnectID = connection ;
  ((EventPublisher<ITcpManager* ,UserActionEventArgs&>*)(this->UserAction))->Invoke(this ,args) ;
  
  this->RecieveData(pkey) ;  
 }
}

//private 进行异步接收数据
void CompletingPortManager::RecieveData(ContextKey* pkey)
{
 if(this->context_manager->ContainsPkey(pkey))
 {
  DWORD Flags = 0;  
  pkey->wsa_buf.len = pkey->requestData.BufferSize() - pkey->requestData.LeftDataLength ;
  pkey->wsa_buf.buf = (char*)(pkey->requestData.Buff + pkey->requestData.LeftDataLength) ;
  ZeroMemory(&(pkey->ovlapped) ,sizeof(OVERLAPPED)) ;
  
  unsigned long numRead = 0 ;
  pkey->netStream->AsynRead(pkey->wsa_buf ,&numRead ,&(pkey->ovlapped)) ;
 }
}

//private 工作者线程
void CompletingPortManager::ServeCmpltdOverlap()
{
 BOOL    bResult;
    int   dwNumRead;
    ContextKey *pkey;
    LPOVERLAPPED lpOverlapped;
    DWORD temp ;
   
    //回复客户
    RespondStreamList* respond_list = new RespondStreamList() ;
 RespondStream* respond_stream = NULL ;
 BOOL socket_closed = FALSE ;

    BOOL succeed = FALSE ;
   // DWORD bytes_send ;
   // WSABUF wsabuff_send ;
    OVERLAPPED ovlapped_send ;
    memset(&ovlapped_send ,0 ,sizeof(OVERLAPPED)) ;
    ovlapped_send.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL); 
    this->overlapEventList.Add(ovlapped_send.hEvent) ;
    ovlapped_send.hEvent = (HANDLE)((DWORD)ovlapped_send.hEvent | 0x1);//表示不关心异步结果,避免Completion Packets
   
    while(! this->isStop)
    {
        bResult = GetQueuedCompletionStatus(  //在底层由系统提供高效的同步机制
            this->completing_port,
            (DWORD*)&dwNumRead,
            &temp,
            &lpOverlapped,
            10
            );  

   if(bResult == 0)
  {
   DWORD rc = GetLastError() ;
   
   if((this->isStop) &&(rc == WAIT_TIMEOUT))
   {
    break ;
   }
   else
   {
    continue ;
   }
  }        
       
        pkey = (ContextKey *)temp ;
  socket_closed = FALSE  ;
       
        if ((bResult == FALSE) || (dwNumRead == 0)) //当客户端正常关闭socket或掉线时,异步WSARecv调用会向队列投递一个特殊的Completion Packets-并且读到的字节长度为0
        {   
   socket_closed = TRUE ;
     }      
        else// Got a valid data block! 处理数据
        {
   try
   {

    pkey->requestData.ValidCount = dwNumRead + pkey->requestData.LeftDataLength ;  
    
    succeed = this->curDispatcher->DealRequestMessage(pkey->requestData ,respond_list) ;   
  
    if(succeed)
    {
     int count = respond_list->Count() ;
     for(int i=0 ;i<count ;i++)
     {
      respond_stream = respond_list->GetElement(i) ;          
      
      //同步发送
      BOOL sendSucceed = pkey->netStream->Write(respond_stream->data ,respond_stream->length) ;
      if(! sendSucceed)
      {
       socket_closed = TRUE ;
       break ;
      }
      else
      {
       ServiceCommittedEventArgs arg ;
       arg.connectID = pkey->netStream->SocketID() ;
       arg.data   = respond_stream->data ;
       arg.length   = respond_stream->length ;

       ((EventPublisher<ITcpManager* ,ServiceCommittedEventArgs&>*)this->ServiceCommitted)->Invoke(this,arg) ;
       
       UserActionEventArgs actArgs ;
       actArgs.action    = UA_Connected ;
       actArgs.ConnectID = pkey->netStream->SocketID() ;
       ((EventPublisher<ITcpManager* ,UserActionEventArgs&>*)this->UserAction)->Invoke(this ,actArgs);
      }
      
      respond_stream = NULL ;     
     }       
    } 
    else
    {
     socket_closed = TRUE ;
    }
   }
   catch(...)//(ContextKey* )
   {
    socket_closed = TRUE ;    
   }

   if(socket_closed)
   {
    this->DisposeOneConnection(pkey->netStream->SocketID() ,LineOff) ;
   }

   this->ClearRespondStreamList(respond_list) ; //清空并删除所有的RespondStream

   if((! this->isStop) && (!socket_closed))
   {  
    if(pkey->requestData.IsFirstMsg)
    {
     pkey->requestData.IsFirstMsg = false ;
    }
    
    this->RecieveData(pkey) ;//继续从socket接收消息
   }      
        }
    }

 delete respond_list ;

    this->workerThread_assistant.DecreaseWorkingThreadNum() ;    //工作线程正常退出 
}


//private
void CompletingPortManager::ClearRespondStreamList(RespondStreamList* rs_list)
{
 RespondStream* respond_stream = NULL ;
 int count = rs_list->Count() ;
 for(int i=0 ;i<count ;i++)
 {
  respond_stream = rs_list->GetElement(i) ;
  respond_stream->ClearAll() ;
  delete respond_stream ; 
  respond_stream = NULL ;
 }

 rs_list->Clear() ;
}

当然,还有很多的基本设施没有包含进来,所以如果你打算使用我的这个完成端口组件可email至sky.zhuwei@163.com索取所有相关的源码。

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
IOCP全称I/O Completion Port,中文译为I/O完成端口。IOCP是一个异步I/O的API,它可以高效地将I/O事件通知给应用程序。与使用select()或是其它异步方法不同的是,一个套接字[socket]与一个完成端口关联了起来,然后就可继续进行正常的Winsock操作了。然而,当一个事件发生的时候,此完成端口就将被操作系统加入一个队列中。然后应用程序可以对核心层进行查询以得到此完成端口。 大体上来讲,使用完成端口只用遵循如下几个步骤: (1) 调用 CreateIoCompletionPort() 函数创建一个完成端口,而且在一般情况下,我们需要且只需要建立这一个完成端口,把它的句柄保存好,我们今后会经常用到它…… (2) 根据系统中有多少个处理器,就建立多少个工作者(为了醒目起见,下面直接说Worker)线程,这几个线程是专门用来和客户端进行通信的,目前暂时没什么工作; (3) 下面就是接收连入的Socket连接了,这里有两种实现方式:一是和别的编程模型一样,还需要启动一个独立的线程,专门用来accept客户端的连接请求;二是用性能更高更好的异步AcceptEx()请求。 (4) 每当有客户端连入的时候,我们就还是得调用CreateIoCompletionPort()函数,这里却不是新建立完成端口了,而是把新连入的Socket(也就是前面所谓的设备句柄),与目前的完成端口绑定在一起。 至此,我们其实就已经完成完成端口的相关部署工作了,嗯,是的,完事了,后面的代码里我们就可以充分享受完成端口带给我们的巨大优势,坐享其成了,是不是很简单呢? (5) 例如,客户端连入之后,我们可以在这个Socket上提交一个网络请求,例如WSARecv(),然后系统就会帮咱们乖乖的去执行接收数据的操作,我们大可以放心的去干别的事情了; (6) 而此时,我们预先准备的那几个Worker线程就不能闲着了, 我们在前面建立的几个Worker就要忙活起来了,都需要分别调用GetQueuedCompletionStatus() 函数在扫描完成端口的队列里是否有网络通信的请求存在(例如读取数据,发送数据等),一旦有的话,就将这个请求从完成端口的队列中取回来,继续执行本线程中后面的处理代码,处理完毕之后,我们再继续投递下一个网络通信的请求就OK了,如此循环。
### 回答1: Boost是一个在C++标准库之上提供可移植、跨平台的增强功能的开源库。Boost.http是Boost库中的一个模块,它提供了HTTP协议的支持,使得C++程序能够更方便地实现HTTP通信。 Boost.http库提供了一组类和函数,用于处理HTTP请求和响应。通过这些类和函数,我们可以方便地发送HTTP请求、接收HTTP响应,并处理请求和响应的各种属性,例如请求头、响应头、请求参数等。 在使用Boost.http库实现HTTP功能时,我们首先需要创建一个HTTP客户端或服务器对象。客户端对象用于发送HTTP请求,而服务器对象用于接收和处理HTTP请求。 对于HTTP客户端,我们可以使用boost::beast::http::request类来构造HTTP请求并发送,例如构造一个GET请求发送到指定的URL地址。在发送请求后,我们可以通过读取boost::beast::http::response类来获取HTTP响应。 对于HTTP服务器,我们可以使用boost::beast::http::request_parser类来解析接收到的HTTP请求,然后根据请求的内容进行相应的处理。处理完毕后,可以使用boost::beast::http::response_creator类来构造HTTP响应并发送回客户端。 Boost.http库还提供了其他一些有用的功能,例如重定向、处理cookie、处理SSL等。 通过使用Boost.http库,我们可以更方便地实现基于HTTP协议的网络通信功能。而且,由于Boost库的可移植性和跨平台性,我们的代码可以在不同的操作系统和编译器上运行,极大地增强了程序的可移植性和可扩展性。 ### 回答2: C++是一种强大的编程语言,而Boost库则是C++语言中的一个重要工具。Boost库提供了大量的功能和算法,可以帮助开发者更高效地进行C++编程。其中,Boost库中的Boost.Asio模块提供了对HTTP/HTTPS和网络编程的支持。 在Boost库中使用Boost.Asio模块实现HTTP/Web功能,需要以下步骤: 1. 引入Boost库:首先,我们需要在自己的项目中引入Boost库。可以通过下载Boost库源代码并进行编译,或者直接使用已经编译好的二进制包。 2. 包含Asio头文件:在我们的代码中,需要包含Asio头文件,以便使用其中提供的HTTP/HTTPS和网络编程相关类和函数。 3. 创建HTTP请求和响应:使用Asio提供的类和函数,我们可以创建HTTP请求和响应对象,并设置相应的请求方法、URL、头部信息等。 4. 发送HTTP请求:通过Asio提供的socket对象,我们可以将HTTP请求发送到服务器,并等待服务器的响应。 5. 接收HTTP响应:当服务器返回响应时,我们可以使用Asio提供的相应函数,将响应内容保存到相应的数据结构中,以便我们进一步处理。 6. 处理响应:根据服务器返回的HTTP响应内容,我们可以进行相应的处理,如解析HTML、处理JSON数据等。 7. 关闭连接:当所有的请求和响应处理完成后,我们可以关闭与服务器的连接。 使用Boost库的Asio模块,我们可以很方便地实现HTTP/Web功能,包括发送HTTP请求、接收和处理服务器的响应等。通过充分利用Boost库的强大功能,我们可以编写高效、稳定的C++代码,实现各种应用程序中的网络功能。 ### 回答3: C++是一种高级编程语言,常用于开发高性能和跨平台的应用程序。Boost库是一个开源的C++库,提供了很多通用的功能组件和工具,用于增强C++程序的功能。而HTTP是一种基于客户端-服务器模型的协议,用于在Web上进行数据通信。 在C++中,我们可以使用Boost库中的ASIO模块来实现基于HTTP的Web应用程序。ASIO(Asynchronous Input/Output)是一种基于事件驱动的异步I/O库,能够实现高效的网络通信。 为了实现HTTP的功能,我们可以使用Boost库中的HTTP模块。这个模块提供了HTTP的解析器和生成器,可以用于解析HTTP请求和构建HTTP响应。 使用Boost库实现HTTP Web应用程序的一般步骤如下: 1. 导入Boost库:在C++项目中,首先需要将Boost库导入到项目中。可以从官方网站下载编译好的Boost库,然后将其链接到项目中。 2. 创建HTTP服务器:使用Boost库的ASIO模块创建一个HTTP服务器对象。这个服务器将监听指定的端口,接收和处理客户端的HTTP请求。 3. 处理HTTP请求:当服务器接收到客户端的HTTP请求时,使用Boost库中的HTTP模块解析该请求,并提取请求的内容以及其他相关信息。根据请求的内容和参数,执行相应的操作。 4. 响应HTTP请求:根据处理结果,使用Boost库中的HTTP模块生成HTTP响应,并将其发送给客户端。HTTP响应包括状态码、响应头和响应体等内容。 5. 清理资源:在服务器不再需要监听请求时,需要及时关闭服务器对象,并清理相关的资源。 总结起来,使用Boost库的ASIO和HTTP模块,可以方便地实现基于HTTP的Web应用程序。通过解析HTTP请求和生成HTTP响应,我们可以实现各种功能,如网页服务、文件传输、数据交互等。 Boost库提供了丰富的功能和工具,可以大大提高C++程序的开发效率和性能。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值