POCO C++是一个开源的C++类库的集合,它主要提供简单的、快速的网络和可移植应用程序的C++开发,这个类库和C++标准库可以很好的集成并填补C++标准库的功能空缺。POCO库的模块化、高效的设计及实现使得POCO特别适合嵌入式开发
模块说明
POCO由4个核心库及若干个附加库组成。核心库是:Foundation、XML、Util和Net。其中的两个附加库是:NetSSL,为Net 库中的网络类提供SSL 支持;Data,用来以统一的形式访问不同的SQL 数据库。POCO致力于进行以网络功能为核心的跨平台C++软件的开发,可以类比于Ruby on Rails对于Web开发的作用——一个功能强大而又简单易用的平台,用来构建妳自己的应用程序。POCO是严格围绕标准ANSI/ISO C++来开发的,并且支持标准库。贡献者们努力地达到以下要素之间的平衡:使用高级的C++特性;保持那些类的可理解性;保持代码的干净、一致及易维护性。
对于我来说,POCO C++的可学习之处至少有以下几点:
1. 跨平台库的封装
2. Application的应用架构的模块化。
3. 不同操作系统的底层API使用
4. 类的设计和设计模式的应用
5. 泛型
接下来的一系列文章就是我在学习时的一些体会。(本文对应的Poco库代码版本为poco-1.4.2p1)
线程
Poco实现线程的机制,它将线程Thread和线程运行的实体Runnable分离开来,就像一个框架,Thread管理线程优先级,堆栈,维护线程局部变量;而运行的代码是在Runnable的子类run方法中实现的。#include "Poco/Thread.h" #include "Poco/Runnable.h" #include <iostream> class HelloRunnable: public Poco::Runnable { virtual void run() { std::cout << "Hello, bingzhe!" << std::endl; } }; int main(int argc, char** argv) { HelloRunnable runnable; Poco::Thread thread; thread.start(runnable); thread.join(); return 0; } 线程池
线程池初始化可以设置最少线程数和最多线程数。每次start使用一个线程,超出最大线程数后会有异常。#include "Poco/ThreadPool.h" #include "Poco/Runnable.h" #include <iostream> class HelloRunnable: public Poco::Runnable { virtual void run() { std::cout << "Hello, bingzhe" << std::endl; } }; int main(int argc, char** argv) { Poco::ThreadPool pool(3, 6);//最少2个线程,最多6个线程 HelloRunnable runnable; if( pool.available() > 0 ) { pool.start(runnable); } pool.joinAll(); return 0; }内存池
内存池内部使用block数组实现,每次获取到一个block,初始化时可以设置block大小。
#include "Poco/MemoryPool.h" #include <string> #include <iostream> using Poco::MemoryPool; int main(int argc, char** argv) { MemoryPool pool(1024); // unlimited number of 1024 byte blocks char* buffer = reinterpret_cast<char*>(pool.get()); std::cin.read(buffer, pool.blockSize()); std::streamsize n = std::cin.gcount(); std::string s(buffer, n); pool.release(buffer); std::cout << s << std::endl; return 0; } 日志
日志模块对日志经过一系列channel过滤,最后写入文件。FormattingChannel对日志进行格式化,Channel对日志进行管理和输出。
1. 控制台日志#include "Poco/ConsoleChannel.h" #include "Poco/FormattingChannel.h" #include "Poco/PatternFormatter.h" #include "Poco/Logger.h" #include "Poco/AutoPtr.h" using Poco::ConsoleChannel; using Poco::FormattingChannel; using Poco::PatternFormatter; using Poco::Logger; using Poco::AutoPtr; int main(int argc, char** argv) { AutoPtr<ConsoleChannel> pCons(new ConsoleChannel); AutoPtr<PatternFormatter> pPF(new PatternFormatter); pPF->setProperty("pattern", "%Y-%m-%d %H:%M:%S %s: %t"); AutoPtr<FormattingChannel> pFC(new FormattingChannel(pPF, pCons)); Logger::root().setChannel(pFC); Logger::get("TestChannel").information("This is a test"); return 0; } 文件日志
FileChannel属性说明:
Path:指明日志文件名
Rotation:指明日志文件控制策略,如按文件大小管理日志文件,超过一定大小后会生成新的文件。
Archive:指明日志文件命名和压缩策略。#include "Poco/Logger.h" #include "Poco/FileChannel.h" #include "Poco/AutoPtr.h" using Poco::Logger; using Poco::FileChannel; using Poco::AutoPtr; int main(int argc, char** argv) { AutoPtr<FileChannel> pChannel(new FileChannel); pChannel->setProperty("path", "sample.log"); pChannel->setProperty("rotation", "2 K"); pChannel->setProperty("archive", "timestamp"); Logger::root().setChannel(pChannel); Logger& logger = Logger::get("TestLogger"); // inherits root channel for (int i = 0; i < 100; ++i) logger.information("Testing FileChannel"); return 0; } HTTPhttp server
实现HTTPRequestHandler和HTTPRequestHandlerFactory,创建HttpServer时创建HTTPRequestHandlerFactory对象,在HTTPRequestHandlerFactory中处理请求时创建与请求对应的HTTPRequestHandler处理器。#include <Poco/Net/HTTPServer.h> #include <Poco/Net/TCPServer.h> #include <Poco/Net/TCPServerConnectionFactory.h> #include "Poco/Net/DNS.h" #include "Poco/Net/ServerSocket.h" #include "Poco/Net/StreamSocket.h" #include "Poco/Net/SocketStream.h" #include "Poco/Net/SocketAddress.h" #include "Poco/Net/SocketAddress.h" #include "Poco/Net/StreamSocket.h" #include "Poco/Net/SocketStream.h" #include "Poco/StreamCopier.h" #include <Poco/Net/HTTPServerParams.h> #include <Poco/Net/HTTPServerRequest.h> #include <Poco/Net/HTTPRequestHandler.h> #include <Poco/Net/HTTPServerResponse.h> #include <iostream> using Poco::Net::DNS; using Poco::Net::IPAddress; using Poco::Net::HostEntry; class RootHandler: public Poco::Net::HTTPRequestHandler { public: void handleRequest(Poco::Net::HTTPServerRequest& request, Poco::Net::HTTPServerResponse& response) { response.setChunkedTransferEncoding(true); response.setContentType("text/html"); std::ostream& ostr = response.send(); ostr << "<html><head><title>HTTP Server powered by POCO C++ Libraries</title></head>"; ostr << "<body>"; ostr << "<h1>hello</h1>"; ostr << "</body></html>"; } }; class MyRequestHandlerFactory : public Poco::Net::HTTPRequestHandlerFactory{ public: MyRequestHandlerFactory(){}; ~MyRequestHandlerFactory(){}; public: virtual Poco::Net::HTTPRequestHandler* createRequestHandler(const Poco::Net::HTTPServerRequest& request) { return new RootHandler; }; }; int main(int argc, char** argv) { Poco::UInt16 port = 1080; Poco::Net::HTTPServerParams * pParams = new Poco::Net::HTTPServerParams; pParams->setMaxQueued(100); pParams->setMaxThreads(16); Poco::Net::ServerSocket svs(port); // set-up a server socket Poco::Net::HTTPServer srv(new MyRequestHandlerFactory(), svs, pParams); // start the HTTPServer srv.start(); //waitForTerminationRequest(); getchar(); // Stop the HTTPServer srv.stop(); return 0; } HTTP Get
HTTPClientSession 表示http session,HTTPRequest 表示http请求,HTTPResponse 表示http响应。#include "Poco/JSON/Parser.h" #include "Poco/JSON/ParseHandler.h" #include "Poco/JSON/JSONException.h" #include "Poco/StreamCopier.h" #include "Poco/Dynamic/Var.h" #include "Poco/JSON/Query.h" #include "Poco/JSON/PrintHandler.h" #include "Poco/Net/HTTPClientSession.h" #include "Poco/Net/HTTPRequest.h" #include "Poco/Net/HTTPResponse.h" #include "Poco/StreamCopier.h" #include "Poco/Net/NetException.h" #include "Poco/Net/HTMLForm.h" #include "Poco/URI.h" #include <iostream> using namespace Poco::Dynamic; using namespace Poco; using std::string; using namespace Poco::JSON; using namespace Poco::Net; int main() { try { std::string strUrl("http://10.0.1.77:8999/blacklist?id=132623198111196400"); Poco::URI uri(strUrl); HTTPClientSession session(uri.getHost(), uri.getPort()); HTTPRequest request(HTTPRequest::HTTP_GET, uri.getPathAndQuery()); session.sendRequest(request); HTTPResponse response; std::istream &is = session.receiveResponse(response); const HTTPResponse::HTTPStatus &status = response.getStatus(); if (HTTPResponse::HTTPStatus::HTTP_OK == status) { std::string result; StreamCopier::copyToString(is, result); std::cout << result << result.size() << std::endl; } else { std::cout << status << std::endl; } } catch (Poco::Exception &e) { std::cout << e.message() << std::endl; } return 0; }HTTP Post
HTTPClientSession 表示http session,HTTPRequest 表示http请求,HTTPResponse 表示http响应,HTMLForm 表示表单。#include <iostream> #include <string> #include "Poco/Net/HTTPClientSession.h" #include "Poco/Net/HTTPRequest.h" #include "Poco/Net/HTTPResponse.h" #include "Poco/StreamCopier.h" #include "Poco/Net/NetException.h" #include "Poco/Net/HTMLForm.h" #include "Poco/URI.h" int main() { try{ URI url("http://test.xheart.cn/xheartdevelop05/user/login/validate"); HTTPClientSession session(url.getHost(),url.getPort()); HTTPRequest request(HTTPRequest::HTTP_POST,url.getPath(),HTTPRequest::HTTP_1_1); HTMLForm form; form.add("userName", "1220002"); form.add("password", "1234567"); form.prepareSubmit(request); form.write(session.sendRequest(request)); HTTPResponse res; std::istream & is = session.receiveResponse(res); StreamCopier::copyStream(is, std::cout); } catch(NetException & ex){ std::cout << ex.displayText(); } } WebsocketServer
实现HTTPRequestHandler和HTTPRequestHandlerFactory,创建HttpServer时创建HTTPRequestHandlerFactory对象,在HTTPRequestHandlerFactory中处理请求时创建与请求对应的HTTPRequestHandler处理器。
#include "Poco/Net/HTTPServer.h" #include "Poco/Net/HTTPRequestHandler.h" #include "Poco/Net/HTTPRequestHandlerFactory.h" #include "Poco/Net/HTTPServerParams.h" #include "Poco/Net/HTTPServerRequest.h" #include "Poco/Net/HTTPServerResponse.h" #include "Poco/Net/HTTPServerParams.h" #include "Poco/Net/ServerSocket.h" #include "Poco/Net/WebSocket.h" #include "Poco/Net/NetException.h" #include "Poco/Util/ServerApplication.h" #include "Poco/Util/Option.h" #include "Poco/Util/OptionSet.h" #include "Poco/Util/HelpFormatter.h" #include "Poco/Format.h" #include <iostream> using Poco::Net::ServerSocket; using Poco::Net::WebSocket; using Poco::Net::WebSocketException; using Poco::Net::HTTPRequestHandler; using Poco::Net::HTTPRequestHandlerFactory; using Poco::Net::HTTPServer; using Poco::Net::HTTPServerRequest; using Poco::Net::HTTPResponse; using Poco::Net::HTTPServerResponse; using Poco::Net::HTTPServerParams; using Poco::Timestamp; using Poco::ThreadPool; using Poco::Util::ServerApplication; using Poco::Util::Application; using Poco::Util::Option; using Poco::Util::OptionSet; using Poco::Util::HelpFormatter; class PageRequestHandler: public HTTPRequestHandler /// Return a HTML document with some JavaScript creating /// a WebSocket connection. { public: void handleRequest(HTTPServerRequest& request, HTTPServerResponse& response) { response.setChunkedTransferEncoding(true); response.setContentType("text/html"); std::ostream& ostr = response.send(); ostr << "<html>"; ostr << "<head>"; ostr << "<title>WebSocketServer</title>"; ostr << "<script type=\"text/javascript\">"; ostr << "function WebSocketTest()"; ostr << "{"; ostr << " if (\"WebSocket\" in window)"; ostr << " {"; ostr << " var ws = new WebSocket(\"ws://" << request.serverAddress().toString() << "/ws\");"; ostr << " ws.onopen = function()"; ostr << " {"; ostr << " ws.send(\"Hello, world!\");"; ostr << " };"; ostr << " ws.onmessage = function(evt)"; ostr << " { "; ostr << " var msg = evt.data;"; ostr << " alert(\"Message received: \" + msg);"; ostr << " ws.close();"; ostr << " };"; ostr << " ws.onclose = function()"; ostr << " { "; ostr << " alert(\"WebSocket closed.\");"; ostr << " };"; ostr << " }"; ostr << " else"; ostr << " {"; ostr << " alert(\"This browser does not support WebSockets.\");"; ostr << " }"; ostr << "}"; ostr << "</script>"; ostr << "</head>"; ostr << "<body>"; ostr << " <h1>WebSocket Server</h1>"; ostr << " <p><a href=\"javascript:WebSocketTest()\">Run WebSocket Script</a></p>"; ostr << "</body>"; ostr << "</html>"; } }; class WebSocketRequestHandler: public HTTPRequestHandler /// Handle a WebSocket connection. { public: void handleRequest(HTTPServerRequest& request, HTTPServerResponse& response) { Application& app = Application::instance(); try { WebSocket ws(request, response); app.logger().information("WebSocket connection established."); char buffer[1024]; int flags; int n; do { n = ws.receiveFrame(buffer, sizeof(buffer), flags); app.logger().information(Poco::format("Frame received (length=%d, flags=0x%x).", n, unsigned(flags))); ws.sendFrame(buffer, n, flags); } while (n > 0 && (flags & WebSocket::FRAME_OP_BITMASK) != WebSocket::FRAME_OP_CLOSE); app.logger().information("WebSocket connection closed."); } catch (WebSocketException& exc) { app.logger().log(exc); switch (exc.code()) { case WebSocket::WS_ERR_HANDSHAKE_UNSUPPORTED_VERSION: response.set("Sec-WebSocket-Version", WebSocket::WEBSOCKET_VERSION); // fallthrough case WebSocket::WS_ERR_NO_HANDSHAKE: case WebSocket::WS_ERR_HANDSHAKE_NO_VERSION: case WebSocket::WS_ERR_HANDSHAKE_NO_KEY: response.setStatusAndReason(HTTPResponse::HTTP_BAD_REQUEST); response.setContentLength(0); response.send(); break; } } } }; class RequestHandlerFactory: public HTTPRequestHandlerFactory { public: HTTPRequestHandler* createRequestHandler(const HTTPServerRequest& request) { Application& app = Application::instance(); app.logger().information("Request from " + request.clientAddress().toString() + ": " + request.getMethod() + " " + request.getURI() + " " + request.getVersion()); for (HTTPServerRequest::ConstIterator it = request.begin(); it != request.end(); ++it) { app.logger().information(it->first + ": " + it->second); } if(request.find("Upgrade") != request.end() && Poco::icompare(request["Upgrade"], "websocket") == 0) return new WebSocketRequestHandler; else return new PageRequestHandler; } }; int main(int argc, char** argv) { Poco::UInt16 port = 1080; Poco::Net::HTTPServerParams * pParams = new Poco::Net::HTTPServerParams; pParams->setMaxQueued(100); pParams->setMaxThreads(16); Poco::Net::ServerSocket svs(port); // set-up a server socket Poco::Net::HTTPServer srv(new RequestHandlerFactory(), svs, pParams); // start the HTTPServer srv.start(); //waitForTerminationRequest(); getchar(); // Stop the HTTPServer srv.stop(); return 0; } Client
和Http client类似,增加了一个WebSocket类来处理websocket数据。#include <iostream> #include <Poco/Net/WebSocket.h> #include <Poco/Net/HTTPClientSession.h> #include <Poco/Net/HTTPRequest.h> #include <Poco/Net/HTTPResponse.h> #include <Poco/Net/ServerSocket.h> #include <Poco/Net/NetException.h> #include <Poco/Exception.h> using Poco::Net::HTTPClientSession; using Poco::Net::HTTPRequest; using Poco::Net::HTTPResponse; using Poco::Net::HTTPServerRequest; using Poco::Net::HTTPServerResponse; using Poco::Net::WebSocket; using Poco::Net::WebSocketException; using Poco::Exception; using namespace std; int main() { char buffer[1024]; int flags; int n; std::string payload; try { HTTPClientSession cs("echo.websocket.org", 80); HTTPRequest request(HTTPRequest::HTTP_GET, "/", "HTTP/1.1"); HTTPResponse response; std::string cmd; WebSocket * ws = new WebSocket(cs, request, response); // Causes the timeout payload = "SGClient: Hello World!"; cout << "Send: SGClient: Hello World!" << endl; ws->sendFrame(payload.data(), payload.size(), WebSocket::FRAME_TEXT); n = ws->receiveFrame(buffer, sizeof(buffer), flags); buffer[n] = '\0'; cout << "Received: " << buffer << endl; while (cmd != "exit") { cmd = ""; cout << "Please input[exit]:"; std::cin >> cmd; ws->sendFrame(cmd.data(), cmd.size(), WebSocket::FRAME_TEXT); n = ws->receiveFrame(buffer, sizeof(buffer), flags); buffer[n] = '\0'; if (n > 0) { std::cout << "Receive: " << buffer << std::endl; } } ws->shutdown(); } catch (Exception ex) { cout << ex.displayText() << endl; cout << ex.what() << endl; return -1; } }数据库
数据库操作首先要注册相应的Connector,Session类管理数据库和表,Statement类对表记录进行操作。
SQLite
#include "Poco/SharedPtr.h" #include "Poco/Tuple.h" #include "Poco/Data/SessionFactory.h" #include "Poco/Data/Session.h" #include "Poco/Data/SQLite/Connector.h" #include <vector> #include <iostream> using namespace Poco::Data::Keywords; using Poco::Data::Session; using Poco::Data::Statement; int main(int argc, char** argv) { typedef Poco::Tuple<std::string, std::string, int> Person; typedef std::vector<Person> People; // register SQLite connector Poco::Data::SQLite::Connector::registerConnector(); // create a session Session session("SQLite", "sample.db"); // drop sample table, if it exists session << "DROP TABLE IF EXISTS Person", now; // (re)create table session << "CREATE TABLE Person (Name VARCHAR(30), Address VARCHAR, Age INTEGER(3))", now; // insert some rows People people; people.push_back(Person("Bart Simpson", "Springfield", 12)); people.push_back(Person("Lisa Simpson", "Springfield", 10)); Statement insert(session); insert << "INSERT INTO Person VALUES(:name, :address, :age)", use(people), now; people.clear(); // a simple query Statement select(session); select << "SELECT Name, Address, Age FROM Person", into(people), now; for (People::const_iterator it = people.begin(); it != people.end(); ++it) { std::cout << "Name: " << it->get<0>() << ", Address: " << it->get<1>() << ", Age: " << it->get<2>() <<std::endl; } return 0; } JSON
格式化JSON使用JSON::Object 类,JSON解析使用JSON::Parser类。使用Dynamic::Var类对JSON对象和变量进行类型转换操作。#include <iostream> #include "Poco/Dynamic/Var.h" #include "Poco/Dynamic/Pair.h" #include "Poco/Dynamic/VarIterator.h" #include "Poco/JSON/Array.h" #include <vector> #include <map> #include "Poco/JSON/Parser.h" using Poco::Dynamic::Var; using Poco::JSON::Parser; using Poco::Dynamic::Pair; using Poco::JSON::Array; using Poco::JSON::Object; int main(int argc, const char * argv[]) { using namespace std; Poco::JSON::Object scores; scores.set("数学", 98); scores.set("英语", 99); scores.set("语文", 89); scores.set("化学", 100); scores.set("物理", 98); scores.set("生物", 96); Poco::JSON::Object student; student.set("name", "小南"); student.set("address", "四川省成都市锦江区锦华路一段7号爱家丽苑1栋1单元305室"); student.set("class", "四川省成都市第七中学2010级2班"); student.set("grade", Poco::Dynamic::Var(scores)); Poco::Dynamic::Var JSON(student); cout << JSON.toString() << endl; string theJSON = JSON.toString(); Poco::JSON::Parser parser; Poco::Dynamic::Var json = parser.parse(theJSON); Poco::JSON::Object theObj = *json.extract<Poco::JSON::Object::Ptr>(); Poco::Dynamic::Var theScores = theObj.get("grade"); Poco::Dynamic::Var name = theObj.get("name"); Poco::Dynamic::Var address = theObj.get("address"); Poco::Dynamic::Var theClass = theObj.get("class"); Poco::JSON::Object grade = *theScores.extract<Poco::JSON::Object::Ptr>(); Poco::Dynamic::Var math = grade.get("数学"); Poco::Dynamic::Var english = grade.get("英语"); Poco::Dynamic::Var chinese = grade.get("语文"); Poco::Dynamic::Var wuli = grade.get("物理"); Poco::Dynamic::Var shengwu = grade.get("生物"); Poco::Dynamic::Var huaxue = grade.get("化学"); cout << "\n\n姓名: " << name.toString() << endl << "班级: " << theClass.toString() << endl << "地址: " << address.toString() << endl << "数学: " << math.convert<int>() << endl << "语文: " << chinese.convert<int>() << endl << "化学: " << huaxue.convert<int>() << endl << "物理: " << wuli.convert<int>() << endl << "生物: " << shengwu.convert<int>() << endl; return 0; }XML
格式化XML
#include "Poco/DOM/Document.h" #include "Poco/DOM/Element.h" #include "Poco/DOM/Text.h" #include "Poco/DOM/AutoPtr.h" #include "Poco/DOM/DOMWriter.h" #include "Poco/XML/XMLWriter.h" #include <iostream> using Poco::XML::Document; using Poco::XML::Element; using Poco::XML::Text; using Poco::XML::AutoPtr; using Poco::XML::DOMWriter; using Poco::XML::XMLWriter; int main(int argc, char** argv) { // build a DOM document and write it to standard output. AutoPtr<Document> pDoc = new Document; AutoPtr<Element> pRoot = pDoc->createElement("root"); pDoc->appendChild(pRoot); AutoPtr<Element> pChild1 = pDoc->createElement("child1"); AutoPtr<Text> pText1 = pDoc->createTextNode("text1"); pChild1->appendChild(pText1); pRoot->appendChild(pChild1); AutoPtr<Element> pChild2 = pDoc->createElement("child2"); AutoPtr<Text> pText2 = pDoc->createTextNode("text2"); pChild2->appendChild(pText2); pRoot->appendChild(pChild2); DOMWriter writer; writer.setNewLine("\n"); writer.setOptions(XMLWriter::PRETTY_PRINT); writer.writeNode(std::cout, pDoc); return 0; } 解析XML/* 配置文件,名称test.xml <config> <prop1>value1</prop1> <prop2>123</prop2> <prop3> <prop4 attr="value3" /> <prop4 attr="value4" /> </prop3> </config> */ #include "Poco/AutoPtr.h" #include "Poco/Util/XMLConfiguration.h" #include <iostream> using namespace std; using Poco::AutoPtr; using Poco::Util::XMLConfiguration; int main(int argc, char** argv) { AutoPtr<XMLConfiguration> pConfig(new XMLConfiguration("test.xml")); std::string prop1 = pConfig->getString("prop1"); cout << prop1 << endl; int prop2 = pConfig->getInt("prop2"); cout << prop2 << endl; std::string prop3 = pConfig->getString("prop3"); cout << prop3 << endl; std::string prop4 = pConfig->getString("prop3.prop4"); cout << prop4 << endl; prop4 = pConfig->getString("prop3.prop4[@attr]"); cout << prop4 << endl; prop4 = pConfig->getString("prop3.prop4[1][@attr]"); cout << prop4 << endl; return 0; }