基于 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);
2 {
3 try
4 {
5 CHttpTask * pHttpTask = new CHttpTask(pExchange, m_timeout, m_pLogger);
6 ThreadPool::defaultPool().start(* pHttpTask);
7 }
8 catch (Exception& ex)
9 {
10 if (m_pLogger != NULL)
11 {
12 m_pLogger->error(ex.displayText());
13 }
14 }
15 }
/**
* 线程池中任务 CHttpTask.run 处理Http请求及响应
*/
CHttpTask : public Runnable
2 {
3 CHttpHandler * pHandler = m_pExchange->GetHandler();
4
5 try
6 {
7 HTTPClientSession httpClientSession;
8 httpClientSession.setHost(m_pExchange->GetHost());
9 httpClientSession.setTimeout(m_timeout);
10 httpClientSession.sendRequest(m_pExchange->GetHttpRequest());
11
12 HTTPResponse response;
13 istream& rs = httpClientSession.receiveResponse(response);
14
15 if (pHandler != NULL)
16 {
17 const string& contentType = response.getContentType();
18 if (contentType.find("text/") >= 0)
19 {
20 stringstream responseStream;
21 StreamCopier::copyStream(rs, responseStream);
22
23 string responseContent;
24 responseStream >> responseContent;
25
26 pHandler->OnResponseComplete(response.getStatus(), responseContent);
27 }
28 }
29 }
30 catch (Exception& ex)
31 {
32 if (m_pLogger != NULL)
33 {
34 m_pLogger->error(ex.displayText());
35 }
36 }
37
38 if (pHandler != NULL)
39 {
40 delete pHandler;
41 }
42
43 delete this;
44 }
/**
* 为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
2 {
3 CHttpHandler::OnResponseComplete(status, responseContent);
4
5 if (status == HTTPResponse::HTTP_OK)
6 {
7 const string& version = responseContent;
8
9 m_pMainWnd->ShowVersionMessage(version);
10 }
11 }
(一)CHttpClient CHttpExchange CHttpHandler 类图:
(二)代码:
HttpClient.h
2 // HttpClient.h
3 // Author: Kylin.dai @kylindai
4 // Date: 2011-05-21
5 //////////////////////////////////////////////////////////////////////////
6
7 #pragma once
8
9 #include "Poco/AutoPtr.h"
10 #include "Poco/Logger.h"
11 #include "Poco/PatternFormatter.h"
12 #include "Poco/FormattingChannel.h"
13 #include "Poco/ConsoleChannel.h"
14 #include "Poco/FileChannel.h"
15 #include "Poco/Message.h"
16 #include "Poco/Exception.h"
17 #include "Poco/StreamCopier.h"
18 #include "Poco/ThreadPool.h"
19 #include "Poco/Thread.h"
20 #include "Poco/Mutex.h"
21 #include "Poco/Runnable.h"
22 #include "Poco/Stopwatch.h"
23 #include "Poco/Net/HTTPClientSession.h"
24 #include "Poco/Net/HTTPRequest.h"
25 #include "Poco/Net/HTTPResponse.h"
26
27 #include <string>
28 #include <iostream>
29 #include <sstream>
30 #include <fstream>
31 #include <map>
32
33 using namespace std;
34 using namespace Poco;
35 using namespace Poco::Net;
36
37 //////////////////////////////////////////////////////////////////////////
38 // CHttpHandler Class
39 //////////////////////////////////////////////////////////////////////////
40 class CHttpHandler
41 {
42 public:
43 CHttpHandler(char * pLogFileName = NULL);
44 ~CHttpHandler();
45
46 private:
47 map<const string, LPVOID> m_attributeMap;
48
49 protected:
50 Logger * m_pLogger;
51
52 public:
53 void SetAttribute(const string& name, LPVOID value);
54 LPVOID GetAttribute(const string& name);
55
56 virtual void OnException();
57 virtual void OnExpire();
58 virtual void OnRequestComplete();
59 virtual void OnResponseComplete(HTTPResponse::HTTPStatus status, const string& responseContent);
60 };
61
62 //////////////////////////////////////////////////////////////////////////
63 // CHttpExchange Class
64 //////////////////////////////////////////////////////////////////////////
65 class CHttpExchange
66 {
67 public:
68 CHttpExchange();
69 ~CHttpExchange();
70
71 private:
72 int m_timeout;
73 HTTPRequest * m_pRequest;
74 CHttpHandler * m_pHandler;
75
76 public:
77 HTTPRequest& GetHttpRequest();
78
79 void SetMethod(const string& strMethod);
80 const string& GetMethod() const;
81
82 void SetContentType(const string& strContentType);
83 const string& GetContentType() const;
84
85 void SetHost(const string& strHost);
86 const string& GetHost() const;
87
88 void SetUri(const string& strUri);
89 const string& GetUri() const;
90
91 void SetHandler(CHttpHandler * pHandler);
92 CHttpHandler * GetHandler();
93 };
94
95 //////////////////////////////////////////////////////////////////////////
96 // CHttpTask Class
97 //////////////////////////////////////////////////////////////////////////
98 class CHttpTask : public Runnable
99 {
100 public:
101 CHttpTask(CHttpExchange * pExchange, int timeout = 15000, Logger * pLogger = NULL);
102 ~CHttpTask();
103
104 private:
105 CHttpExchange * m_pExchange;
106 int m_timeout;
107
108 protected:
109 Logger * m_pLogger;
110
111 public:
112 void run();
113 };
114
115 //////////////////////////////////////////////////////////////////////////
116 // CHttpClient Class
117 //////////////////////////////////////////////////////////////////////////
118 class CHttpClient
119 {
120 public:
121 CHttpClient();
122 ~CHttpClient();
123
124 private:
125 int m_timeout;
126 int m_threads;
127 char * m_pLogFileName;
128
129 protected:
130 Logger * m_pLogger;
131
132 public:
133 void Create(int timeout = 15000, int threads = 20, char * pLogFileName = NULL);
134 void Start();
135 void Stop();
136 void Send(CHttpExchange * pExchange);
137 };
HttpClient.cpp
2 // HttpClient.cpp
3 // Author: Kylin.dai @kylindai
4 // Date: 2011-05-21
5 //////////////////////////////////////////////////////////////////////////
6
7 #include "HttpClient.h"
8
9 //////////////////////////////////////////////////////////////////////////
10 // CHttpHandler Methods
11 //////////////////////////////////////////////////////////////////////////
12 CHttpHandler::CHttpHandler(char * pLogFileName)
13 {
14 m_pLogger = NULL;
15
16 // create logger
17 if (pLogFileName != NULL)
18 {
19 try
20 {
21 string logFileName(pLogFileName);
22
23 FormattingChannel* pFCFile = new FormattingChannel(new PatternFormatter("%Y-%m-%d %H:%M:%S.%c %N[%P]:%s:%q:%t"));
24 pFCFile->setChannel(new FileChannel(logFileName));
25 pFCFile->open();
26
27 m_pLogger = & Logger::create("HttpHandler", pFCFile, Message::PRIO_DEBUG); //Message::PRIO_WARNING);
28 }
29 catch (Exception& ex)
30 {
31 }
32 }
33 }
34
35 CHttpHandler::~CHttpHandler()
36 {
37 if (m_pLogger != NULL)
38 {
39 m_pLogger->getChannel()->close();
40 }
41 }
42
43 void CHttpHandler::SetAttribute(const string& name, LPVOID value)
44 {
45 m_attributeMap[name] = value;
46 }
47
48 LPVOID CHttpHandler::GetAttribute(const string& name)
49 {
50 return m_attributeMap[name];
51 }
52
53 void CHttpHandler::OnException()
54 {
55
56 }
57
58 void CHttpHandler::OnExpire()
59 {
60
61 }
62
63 void CHttpHandler::OnRequestComplete()
64 {
65
66 }
67
68 void CHttpHandler::OnResponseComplete(HTTPResponse::HTTPStatus status, const string& responseContent)
69 {
70 if (m_pLogger != NULL)
71 {
72 stringstream messageStream;
73 messageStream << "HTTP STATUS:" << status << " : " << responseContent << endl;
74
75 m_pLogger->debug(messageStream.str());
76 }
77 }
78
79 //////////////////////////////////////////////////////////////////////////
80 // CHttpExchange Methods
81 //////////////////////////////////////////////////////////////////////////
82 CHttpExchange::CHttpExchange()
83 {
84 m_pRequest = new HTTPRequest();
85 m_pRequest->setVersion(HTTPRequest::HTTP_1_1);
86 }
87
88 CHttpExchange::~CHttpExchange()
89 {
90 if (m_pRequest != NULL)
91 {
92 // delete m_pRequest;
93 }
94 }
95
96 HTTPRequest& CHttpExchange::GetHttpRequest()
97 {
98 return * m_pRequest;
99 }
100
101 void CHttpExchange::SetMethod(const string& strMethod)
102 {
103 m_pRequest->setMethod(strMethod);
104 }
105
106 const string& CHttpExchange::GetMethod() const
107 {
108 return m_pRequest->getMethod();
109 }
110
111 void CHttpExchange::SetContentType(const string& strContentType)
112 {
113 m_pRequest->setContentType(strContentType);
114 }
115
116 const string& CHttpExchange::GetContentType() const
117 {
118 return m_pRequest->getContentType();
119 }
120
121 void CHttpExchange::SetHost(const string& strHost)
122 {
123 m_pRequest->setHost(strHost);
124 }
125
126 const string& CHttpExchange::GetHost() const
127 {
128 return m_pRequest->getHost();
129 }
130
131 void CHttpExchange::SetUri(const string& strUri)
132 {
133 m_pRequest->setURI(strUri);
134 }
135
136 const string& CHttpExchange::GetUri() const
137 {
138 return m_pRequest->getURI();
139 }
140
141 void CHttpExchange::SetHandler(CHttpHandler * pHandler)
142 {
143 m_pHandler = pHandler;
144 }
145
146 CHttpHandler * CHttpExchange::GetHandler()
147 {
148 return m_pHandler;
149 }
150
151 //////////////////////////////////////////////////////////////////////////
152 // CHttpTask Methods
153 //////////////////////////////////////////////////////////////////////////
154 CHttpTask::CHttpTask(CHttpExchange * pExchange, int timeout, Logger * pLogger):
155 m_pExchange(pExchange),
156 m_timeout(timeout),
157 m_pLogger(pLogger)
158 {
159
160 }
161
162 CHttpTask::~CHttpTask()
163 {
164 if (m_pExchange != NULL)
165 {
166 delete m_pExchange;
167 }
168 }
169
170 void CHttpTask::run()
171 {
172 CHttpHandler * pHandler = m_pExchange->GetHandler();
173
174 try
175 {
176 HTTPClientSession httpClientSession;
177 httpClientSession.setHost(m_pExchange->GetHost());
178 httpClientSession.setTimeout(m_timeout);
179 httpClientSession.sendRequest(m_pExchange->GetHttpRequest());
180
181 HTTPResponse response;
182 istream& rs = httpClientSession.receiveResponse(response);
183
184 if (pHandler != NULL)
185 {
186 const string& contentType = response.getContentType();
187 if (contentType.find("text/") >= 0)
188 {
189 stringstream responseStream;
190 StreamCopier::copyStream(rs, responseStream);
191
192 string responseContent;
193 responseStream >> responseContent;
194
195 pHandler->OnResponseComplete(response.getStatus(), responseContent);
196 }
197 }
198 }
199 catch (Exception& ex)
200 {
201 if (m_pLogger != NULL)
202 {
203 m_pLogger->error(ex.displayText());
204 }
205 }
206
207 if (pHandler != NULL)
208 {
209 delete pHandler;
210 }
211
212 delete this;
213 }
214
215 //////////////////////////////////////////////////////////////////////////
216 // CHttpClient Methods
217 //////////////////////////////////////////////////////////////////////////
218 CHttpClient::CHttpClient()
219 {
220 m_pLogger = NULL;
221 }
222
223 CHttpClient::~CHttpClient()
224 {
225 if (m_pLogger != NULL)
226 {
227 m_pLogger->getChannel()->close();
228 }
229 }
230
231 void CHttpClient::Create(int timeout, int threads, char * pLogFileName)
232 {
233 m_timeout = timeout;
234 m_threads = threads;
235 m_pLogFileName = pLogFileName;
236
237 ThreadPool::defaultPool().addCapacity(threads);
238
239 // create logger
240 if (pLogFileName != NULL)
241 {
242 try
243 {
244 string logFileName(pLogFileName);
245
246 FormattingChannel* pFCFile = new FormattingChannel(new PatternFormatter("%Y-%m-%d %H:%M:%S.%c %N[%P]:%s:%q:%t"));
247 pFCFile->setChannel(new FileChannel(logFileName));
248 pFCFile->open();
249
250 m_pLogger = & Logger::create("fileLogger", pFCFile, Message::PRIO_WARNING);
251 }
252 catch (Exception& ex)
253 {
254 }
255 }
256 /*
257 else
258 {
259 FormattingChannel* pFCConsole = new FormattingChannel(new PatternFormatter("%s: %p: %t"));
260 pFCConsole->setChannel(new ConsoleChannel);
261 pFCConsole->open();
262
263 m_pLogger = & Logger::create("ConsoleLogger", pFCConsole, Message::PRIO_DEBUG);
264 }
265 */
266 }
267
268 void CHttpClient::Start()
269 {
270
271 }
272
273 void CHttpClient::Stop()
274 {
275 // ThreadPool::defaultPool().joinAll();
276 }
277
278 void CHttpClient::Send(CHttpExchange * pExchange)
279 {
280 try
281 {
282 CHttpTask * pHttpTask = new CHttpTask(pExchange, m_timeout, m_pLogger);
283 ThreadPool::defaultPool().start(* pHttpTask);
284 }
285 catch (Exception& ex)
286 {
287 if (m_pLogger != NULL)
288 {
289 m_pLogger->error(ex.displayText());
290 }
291 }
292 }