q986 发表于 2017-3-1 06:28:50

基于 C++ POCO 库封装的异步多线程的 CHttpClient 类

  用惯了 Jetty 的 基于事件的 HttpClient 类,在C++平台上也没找到这样调用方式的类库,只好自己写一个了。
  目前版本 1.0,朋友们看了给点建议。(注:Kylindai原创,转载请注明出处)
  Feature: 基于C++跨平台的 POCO 库实现,支持线程池 Poco::ThreadPool, 异步 HttpClient, 支持Http事件回调处理。
  基本思路,核心方法:
  /**
  * 创建多线程支持的HttpClient,
  * nTimeout 超时
  * nMaxThreads 最大线程数
  * pLogFileName 日志文件
  */
  CHttpClient.Create(int nTimeout, int nMaxThreads, char * pLogFileName = NULL);
  /**
  * 发送请求,此方法会创建一个 CHttpTask 放到线程池里,然后由线程发起 HTTPClientSession 请求,
  * 收到响应后,回调 CHttpExchange.GetHandler().OnResponseComplate() 方法。
  * 类似 Java Jetty的 HttpClient.send(HttpExchange exchange)
  * pExchange CHttpExchange HTTP 交换类
  */
  CHttpClient.Send(CHttpExchange * pExchange);


void CHttpClient::Send(CHttpExchange * pExchange)
{
   try
   {
         CHttpTask * pHttpTask = new CHttpTask(pExchange, m_timeout, m_pLogger);
         ThreadPool::defaultPool().start(* pHttpTask);
   }
   catch (Exception& ex)
   {
         if (m_pLogger != NULL)
         {
             m_pLogger->error(ex.displayText());
         }
   }
}  /**
  * 线程池中任务 CHttpTask.run 处理Http请求及响应
  */
  CHttpTask : public Runnable


void CHttpTask::run()
{
   CHttpHandler * pHandler = m_pExchange->GetHandler();

   try
   {
         HTTPClientSession httpClientSession;
         httpClientSession.setHost(m_pExchange->GetHost());
         httpClientSession.setTimeout(m_timeout);
         httpClientSession.sendRequest(m_pExchange->GetHttpRequest());

         HTTPResponse response;
         istream& rs = httpClientSession.receiveResponse(response);

         if (pHandler != NULL)
         {
             const string& contentType = response.getContentType();
             if (contentType.find("text/") >= 0)
             {
               stringstream responseStream;
               StreamCopier::copyStream(rs, responseStream);

               string responseContent;
               responseStream >> responseContent;

               pHandler->OnResponseComplete(response.getStatus(), responseContent);
             }
         }
   }
   catch (Exception& ex)
   {
         if (m_pLogger != NULL)
         {
             m_pLogger->error(ex.displayText());
         }
   }

   if (pHandler != NULL)
   {
         delete pHandler;
   }

   delete this;
}  /**
  * 为HttpExchange 设置 Handler 处理回调类,以后具体的逻辑由 CHttpHandler 继承的具体业务处理逻辑类完成。
  * pHandler CHttpHandler HTTP 事件处理类
  */
  CHttpExchange.SetHandler(CHttpHandler * pHandler);
  /**
  * 当收到响应完成时的事件回调函数,传入 HttpStatus 和 文本的Response,
  * 目前仅支持文本 Response,即:Resonse.getContentType() 为 text 类型
  */
  CHttpHandler.OnResponseComplate(HTTPResponse::HTTPStatus status, const string& responseContent);
  CCheckVersionHandler : public CHttpHandler


void CCheckVersionHandler::OnResponseComplete(HTTPResponse::HTTPStatus status, const string& responseContent)
{
   CHttpHandler::OnResponseComplete(status, responseContent);

   if (status == HTTPResponse::HTTP_OK)
   {
         const string& version = responseContent;

         m_pMainWnd->ShowVersionMessage(version);
   }
}  (一)CHttpClient CHttpExchange CHttpHandler 类图:
  (二)代码:
  HttpClient.h


//////////////////////////////////////////////////////////////////////////
//HttpClient.h
//Author: Kylin.dai @kylindai
//Date: 2011-05-21
//////////////////////////////////////////////////////////////////////////

#pragma once

#include "Poco/AutoPtr.h"
#include "Poco/Logger.h"
#include "Poco/PatternFormatter.h"
#include "Poco/FormattingChannel.h"
#include "Poco/ConsoleChannel.h"
#include "Poco/FileChannel.h"
#include "Poco/Message.h"
#include "Poco/Exception.h"
#include "Poco/StreamCopier.h"
#include "Poco/ThreadPool.h"
#include "Poco/Thread.h"
#include "Poco/Mutex.h"
#include "Poco/Runnable.h"
#include "Poco/Stopwatch.h"
#include "Poco/Net/HTTPClientSession.h"
#include "Poco/Net/HTTPRequest.h"
#include "Poco/Net/HTTPResponse.h"

#include <string>
#include <iostream>
#include <sstream>
#include <fstream>
#include <map>

using namespace std;
using namespace Poco;
using namespace Poco::Net;

//////////////////////////////////////////////////////////////////////////
// CHttpHandler Class
//////////////////////////////////////////////////////////////////////////
class CHttpHandler
{
public:
   CHttpHandler(char * pLogFileName = NULL);
   ~CHttpHandler();

private:
   map<const string, LPVOID> m_attributeMap;

protected:
   Logger * m_pLogger;

public:
   void SetAttribute(const string& name, LPVOID value);
   LPVOID GetAttribute(const string& name);

   virtual void OnException();
   virtual void OnExpire();
   virtual void OnRequestComplete();
   virtual void OnResponseComplete(HTTPResponse::HTTPStatus status, const string& responseContent);
};

//////////////////////////////////////////////////////////////////////////
// CHttpExchange Class
//////////////////////////////////////////////////////////////////////////
class CHttpExchange
{
public:
   CHttpExchange();
   ~CHttpExchange();

private:
   int m_timeout;
   HTTPRequest * m_pRequest;
   CHttpHandler * m_pHandler;

public:
   HTTPRequest& GetHttpRequest();

   void SetMethod(const string& strMethod);
   const string& GetMethod() const;

   void SetContentType(const string& strContentType);
   const string& GetContentType() const;

   void SetHost(const string& strHost);
   const string& GetHost() const;

   void SetUri(const string& strUri);
   const string& GetUri() const;

   void SetHandler(CHttpHandler * pHandler);
   CHttpHandler * GetHandler();
};

//////////////////////////////////////////////////////////////////////////
// CHttpTask Class
//////////////////////////////////////////////////////////////////////////
class CHttpTask : public Runnable
{
public:
   CHttpTask(CHttpExchange * pExchange, int timeout = 15000, Logger * pLogger = NULL);
   ~CHttpTask();

private:
   CHttpExchange * m_pExchange;
   int m_timeout;

protected:
   Logger * m_pLogger;

public:
   void run();
};

//////////////////////////////////////////////////////////////////////////
// CHttpClient Class
//////////////////////////////////////////////////////////////////////////
class CHttpClient
{
public:
   CHttpClient();
   ~CHttpClient();

private:
   int m_timeout;
   int m_threads;
   char * m_pLogFileName;

protected:
   Logger * m_pLogger;

public:
   void Create(int timeout = 15000, int threads = 20, char * pLogFileName = NULL);
   void Start();
   void Stop();
   void Send(CHttpExchange * pExchange);
};  HttpClient.cpp


//////////////////////////////////////////////////////////////////////////
//HttpClient.cpp
//Author: Kylin.dai @kylindai
//Date: 2011-05-21
//////////////////////////////////////////////////////////////////////////

#include "HttpClient.h"

//////////////////////////////////////////////////////////////////////////
// CHttpHandler Methods
//////////////////////////////////////////////////////////////////////////
CHttpHandler::CHttpHandler(char * pLogFileName)
{
   m_pLogger = NULL;

   // create logger
   if (pLogFileName != NULL)
   {
         try
         {
             string logFileName(pLogFileName);

             FormattingChannel* pFCFile = new FormattingChannel(new PatternFormatter("%Y-%m-%d %H:%M:%S.%c %N[%P]:%s:%q:%t"));
             pFCFile->setChannel(new FileChannel(logFileName));
             pFCFile->open();

             m_pLogger = & Logger::create("HttpHandler", pFCFile, Message::PRIO_DEBUG); //Message::PRIO_WARNING);
         }
         catch (Exception& ex)
         {
         }
   }
}

CHttpHandler::~CHttpHandler()
{
   if (m_pLogger != NULL)
   {
         m_pLogger->getChannel()->close();
   }
}

void CHttpHandler::SetAttribute(const string& name, LPVOID value)
{
   m_attributeMap = value;
}

LPVOID CHttpHandler::GetAttribute(const string& name)
{
   return m_attributeMap;
}

void CHttpHandler::OnException()
{

}

void CHttpHandler::OnExpire()
{

}

void CHttpHandler::OnRequestComplete()
{

}

void CHttpHandler::OnResponseComplete(HTTPResponse::HTTPStatus status, const string& responseContent)
{
   if (m_pLogger != NULL)
   {
         stringstream messageStream;
         messageStream << "HTTP STATUS:" << status << " : " << responseContent << endl;

         m_pLogger->debug(messageStream.str());
   }
}

//////////////////////////////////////////////////////////////////////////
// CHttpExchange Methods
//////////////////////////////////////////////////////////////////////////
CHttpExchange::CHttpExchange()
{
   m_pRequest = new HTTPRequest();
   m_pRequest->setVersion(HTTPRequest::HTTP_1_1);
}

CHttpExchange::~CHttpExchange()
{
   if (m_pRequest != NULL)
   {
//      delete m_pRequest;
   }
}

HTTPRequest& CHttpExchange::GetHttpRequest()
{
   return * m_pRequest;
}

void CHttpExchange::SetMethod(const string& strMethod)
{
   m_pRequest->setMethod(strMethod);
}

const string& CHttpExchange::GetMethod() const
{
   return m_pRequest->getMethod();
}

void CHttpExchange::SetContentType(const string& strContentType)
{
   m_pRequest->setContentType(strContentType);
}

const string& CHttpExchange::GetContentType() const
{
   return m_pRequest->getContentType();
}

void CHttpExchange::SetHost(const string& strHost)
{
   m_pRequest->setHost(strHost);
}

const string& CHttpExchange::GetHost() const
{
   return m_pRequest->getHost();
}

void CHttpExchange::SetUri(const string& strUri)
{
   m_pRequest->setURI(strUri);
}

const string& CHttpExchange::GetUri() const
{
   return m_pRequest->getURI();
}

void CHttpExchange::SetHandler(CHttpHandler * pHandler)
{
   m_pHandler = pHandler;
}

CHttpHandler * CHttpExchange::GetHandler()
{
   return m_pHandler;
}

//////////////////////////////////////////////////////////////////////////
// CHttpTask Methods
//////////////////////////////////////////////////////////////////////////
CHttpTask::CHttpTask(CHttpExchange * pExchange, int timeout, Logger * pLogger):
   m_pExchange(pExchange),
   m_timeout(timeout),
   m_pLogger(pLogger)
{
   
}

CHttpTask::~CHttpTask()
{
   if (m_pExchange != NULL)
   {
         delete m_pExchange;
   }
}

void CHttpTask::run()
{
   CHttpHandler * pHandler = m_pExchange->GetHandler();

   try
   {
         HTTPClientSession httpClientSession;
         httpClientSession.setHost(m_pExchange->GetHost());
         httpClientSession.setTimeout(m_timeout);
         httpClientSession.sendRequest(m_pExchange->GetHttpRequest());

         HTTPResponse response;
         istream& rs = httpClientSession.receiveResponse(response);

         if (pHandler != NULL)
         {
             const string& contentType = response.getContentType();
             if (contentType.find("text/") >= 0)
             {
               stringstream responseStream;
               StreamCopier::copyStream(rs, responseStream);

               string responseContent;
               responseStream >> responseContent;

               pHandler->OnResponseComplete(response.getStatus(), responseContent);
             }
         }
   }
   catch (Exception& ex)
   {
         if (m_pLogger != NULL)
         {
             m_pLogger->error(ex.displayText());
         }
   }

   if (pHandler != NULL)
   {
         delete pHandler;
   }

   delete this;
}

//////////////////////////////////////////////////////////////////////////
// CHttpClient Methods
//////////////////////////////////////////////////////////////////////////
CHttpClient::CHttpClient()
{
   m_pLogger = NULL;
}

CHttpClient::~CHttpClient()
{
   if (m_pLogger != NULL)
   {
         m_pLogger->getChannel()->close();
   }
}

void CHttpClient::Create(int timeout, int threads, char * pLogFileName)
{
   m_timeout = timeout;
   m_threads = threads;
   m_pLogFileName = pLogFileName;

   ThreadPool::defaultPool().addCapacity(threads);

   // create logger
   if (pLogFileName != NULL)
   {
         try
         {
             string logFileName(pLogFileName);

             FormattingChannel* pFCFile = new FormattingChannel(new PatternFormatter("%Y-%m-%d %H:%M:%S.%c %N[%P]:%s:%q:%t"));
             pFCFile->setChannel(new FileChannel(logFileName));
             pFCFile->open();

             m_pLogger = & Logger::create("fileLogger", pFCFile, Message::PRIO_WARNING);
         }
         catch (Exception& ex)
         {
         }
   }
   /*
   else
   {
         FormattingChannel* pFCConsole = new FormattingChannel(new PatternFormatter("%s: %p: %t"));
         pFCConsole->setChannel(new ConsoleChannel);
         pFCConsole->open();

         m_pLogger = & Logger::create("ConsoleLogger", pFCConsole, Message::PRIO_DEBUG);
   }
   */
}

void CHttpClient::Start()
{

}

void CHttpClient::Stop()
{
//    ThreadPool::defaultPool().joinAll();
}

void CHttpClient::Send(CHttpExchange * pExchange)
{
   try
   {
         CHttpTask * pHttpTask = new CHttpTask(pExchange, m_timeout, m_pLogger);
         ThreadPool::defaultPool().start(* pHttpTask);
   }
   catch (Exception& ex)
   {
         if (m_pLogger != NULL)
         {
             m_pLogger->error(ex.displayText());
         }
   }
}
页: [1]
查看完整版本: 基于 C++ POCO 库封装的异步多线程的 CHttpClient 类