Poco::Net::HTTPServer を使うと、簡単に HTTP Server を実現することができます。
ここでは、HTTP Server を装備しているハードウェアの自己診断機能を、Web browser からの指示で実行し、実行結果を表示するという想定のサンプルを作ってみました。
HTTPServerTest.cpp
・Poco::ActiveDispatcher を継承した ActiveDiagnosticsDispatcher クラスで、テスト番号に応じた
自己診断処理を実行。
ActiveDiagnosticsDispatcher::testImpl に実際のハードウェア自己診断処理を実装するという想定。
・Poco::Net::HTTPRequestHandler を継承した FormRequestHandler で、HTTP リクエストを処理。
・Poco::Net::HTTPRequestHandlerFactory を継承した FormRequestHandlerFactory から、
FormRequestHandler を生成。
・MyHTTPServer クラスは Poco::Util::ServerApplication を継承。
内部で Poco::Logger を生成しているので、ScopedLogMessage や ScopedElapsedTime を使う時の
PrepareConsoleLogger() は不要。
・コマンドライン起動時の help (-h, –help) オプション部実装。
・MyHTTPServer::main() で、ポート番号 9980 で HTTP Server 開始。
実行ファイル(HTTPServerTest)と同じディレクトリに HTTPServerTest.properties があり、
例えば MyHTTPServer.port = 9981 と定義されていれば、そのポート番号に置き換わる。
defineOptions() の記述が一風変わっているが、詳しくは Poco::Util::Option 参照。
・POCO_SERVER_MAIN(MyHTTPServer) が展開され、main() になる。
(Poco/Util/ServerApplication.h 参照)
#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/HTMLForm.h> #include <Poco/Net/PartHandler.h> #include <Poco/Net/MessageHeader.h> #include <Poco/Net/ServerSocket.h> #include <Poco/CountingStream.h> #include <Poco/NullStream.h> #include <Poco/StreamCopier.h> #include <Poco/Util/ServerApplication.h> #include <Poco/Util/Option.h> #include <Poco/Util/OptionSet.h> #include <Poco/Util/HelpFormatter.h> #include <Poco/NumberParser.h> #include <Poco/Format.h> #include <Poco/ActiveMethod.h> #include <Poco/ActiveDispatcher.h> #include <Poco/Tuple.h> #include <iostream> #include "ScopedElapsedTime.h" #include "ScopedLogMessage.h" typedef Poco::Tuple<std::size_t, std::size_t, bool> FunctionTestResultType; const int kNumFunction = 3; const std::size_t kFailFunction = 1; struct DiagnosticsResult { std::size_t testNum; int result; }; class ActiveDiagnosticsDispatcher : public Poco::ActiveDispatcher { public: ActiveDiagnosticsDispatcher() : test(this, &ActiveDiagnosticsDispatcher::testImpl) , m_msg(" ActiveDiagnosticsDispatcher ", "start\n", "end") { } Poco::ActiveMethod<DiagnosticsResult, std::size_t, ActiveDiagnosticsDispatcher, Poco::ActiveStarter<Poco::ActiveDispatcher> > test; private: // Single Threaded Execution pattern DiagnosticsResult testImpl(const std::size_t& testNum) { ScopedElapsedTime msg(" ActiveDiagnosticsDispatcher::testImpl ", "start", "end"); // simulate time consuming job Poco::Thread::sleep(50); // 50mSec DiagnosticsResult rtn; rtn.testNum = testNum; rtn.result = (kFailFunction == testNum) ? -1:0; // test #(kFailFunction) always fails msg.Message(Poco::format(" test #%z result: %d", rtn.testNum, rtn.result)); return rtn; } ScopedLogMessage m_msg; }; class FormRequestHandler : public Poco::Net::HTTPRequestHandler { public: FormRequestHandler( std::vector<FunctionTestResultType>& functionTestResults , ActiveDiagnosticsDispatcher& diagnosticsDispatcher) : m_functionTestResults(functionTestResults) , m_diagnosticsDispatcher(diagnosticsDispatcher) , m_msg(" FormRequestHandler ", "start", "end\n") { } void handleRequest(Poco::Net::HTTPServerRequest& request, Poco::Net::HTTPServerResponse& response) { m_msg.Message(" Request from " + request.clientAddress().toString()); Poco::Net::HTMLForm form(request, request.stream()); response.setChunkedTransferEncoding(true); response.setContentType("text/html"); std::deque< Poco::ActiveResult<DiagnosticsResult> > activeResults; if(!form.empty()) { Poco::Net::NameValueCollection::ConstIterator itr = form.begin(); Poco::Net::NameValueCollection::ConstIterator end = form.end(); for(; itr != end; ++itr) { activeResults.push_back(m_diagnosticsDispatcher.test(Poco::NumberParser::parse(itr->first))); } } bool changedFlag = false; bool changedItem[kNumFunction] = {false}; while(0 != activeResults.size()) { Poco::ActiveResult<DiagnosticsResult>& result = activeResults.front(); result.wait(); if(0 == result.data().result) { m_functionTestResults[result.data().testNum].set<0>(1 + m_functionTestResults[result.data().testNum].get<0>()); } else { m_functionTestResults[result.data().testNum].set<1>(1 + m_functionTestResults[result.data().testNum].get<1>()); } changedFlag = true; changedItem[result.data().testNum] = true; activeResults.pop_front(); } if(changedFlag) { for(int i=0; i<kNumFunction; ++i) { m_functionTestResults[i].set<2>(changedItem[i]); } } std::ostream& ostr = response.send(); ostr << "<html>\n" "<head>\n" "<title>Diagnostics test</title>\n" "</head>\n" "<body>\n" "<h2>Diagnostics test</h2>\n" "<form method='POST' action='/'>\n" "<table border='1'>\n" "<tbody>\n"; for(std::size_t i=0; i<m_functionTestResults.size(); ++i) { ostr << Poco::format("<tr><td>Function #%z</td><td><input type='checkbox' name='%z' %s></td></tr>\n" , i+1 , i , std::string(m_functionTestResults[i].get<2>() ? "checked":"") ); } ostr << "<tr><td colspan='2' align='center'><input type='submit' value='Test It!'></td></tr>\n" "</tbody>\n" "</table>\n" "</form>\n" "<h2>Test results</h2>\n" "<form method='POST' action='/'>\n" "<table border='1'>\n" "<tbody>\n" "<tr><th></th><th>Trial</th><th>OK</th><th>NG</th></tr>\n"; for(std::size_t i=0; i<m_functionTestResults.size(); ++i) { ostr << Poco::format( "<tr><td>Function #%z</td><td>%z</td><td>%z</td><td>%z</td></tr>\n" , i+1 , m_functionTestResults[i].get<0>() + m_functionTestResults[i].get<1>() , m_functionTestResults[i].get<0>() , m_functionTestResults[i].get<1>() ); } ostr << "<tr><td colspan='4' align='center'><input type='submit' value='Update'></td></tr>\n" "</tbody>\n" "</table>\n"; ostr << Poco::format("<font color=\"red\">(Function #%z %always fails)</font>\n", kFailFunction+1); ostr << "</form>\n" "</body>\n"; } private: std::vector<FunctionTestResultType>& m_functionTestResults; ActiveDiagnosticsDispatcher& m_diagnosticsDispatcher; ScopedLogMessage m_msg; }; class FormRequestHandlerFactory : public Poco::Net::HTTPRequestHandlerFactory { public: FormRequestHandlerFactory( std::vector<FunctionTestResultType>& functionTestResults , ActiveDiagnosticsDispatcher& diagnosticsDispatcher) : m_functionTestResults(functionTestResults) , m_diagnosticsDispatcher(diagnosticsDispatcher) { } Poco::Net::HTTPRequestHandler* createRequestHandler(const Poco::Net::HTTPServerRequest& request) { return new FormRequestHandler(m_functionTestResults, m_diagnosticsDispatcher); } private: std::vector<FunctionTestResultType>& m_functionTestResults; ActiveDiagnosticsDispatcher& m_diagnosticsDispatcher; }; class MyHTTPServer : public Poco::Util::ServerApplication { public: MyHTTPServer() : m_helpRequested(false) , m_functionTestResults(kNumFunction, FunctionTestResultType(0, 0, true)) { } ~MyHTTPServer() { } protected: void initialize(Poco::Util::Application& self) { loadConfiguration(); // load default configuration files, if present Poco::Util::ServerApplication::initialize(self); } void uninitialize() { Poco::Util::ServerApplication::uninitialize(); } void defineOptions(Poco::Util::OptionSet& options) { Poco::Util::ServerApplication::defineOptions(options); options.addOption( Poco::Util::Option("help", "h", "display help information on command line arguments") .required(false) .repeatable(false)); } void handleOption(const std::string& name, const std::string& value) { Poco::Util::ServerApplication::handleOption(name, value); if(name == "help") { m_helpRequested = true; } } void displayHelp() { Poco::Util::HelpFormatter helpFormatter(options()); helpFormatter.setCommand(commandName()); helpFormatter.setUsage("OPTIONS"); helpFormatter.setHeader("A web server for diagnostics test."); helpFormatter.format(std::cout); } int main(const std::vector<std::string>& args) { ScopedLogMessage msg(" main() ", "start", "end"); if(m_helpRequested) { displayHelp(); } else { unsigned short port = (unsigned short) config().getInt("MyHTTPServer.port", 9980); ActiveDiagnosticsDispatcher diagnosticsDispatcher; Poco::Net::ServerSocket svs(port); Poco::Net::HTTPServer srv(new FormRequestHandlerFactory(m_functionTestResults , diagnosticsDispatcher), svs, new Poco::Net::HTTPServerParams); srv.start(); // wait for CTRL-C or kill waitForTerminationRequest(); srv.stop(); } return Poco::Util::Application::EXIT_OK; } private: bool m_helpRequested; std::vector<FunctionTestResultType> m_functionTestResults; }; POCO_SERVER_MAIN(MyHTTPServer) |
Results of execution
スレッド動作確認のため、MyHTTPServerTest を実行しているマシン(以下自機)と他のマシン
(以下他機)の 2 台から接続してテスト。
・01 – 02 行: コンソールで、MyHTTPServerTest を起動。
・04 – 06 行: 他機 (192.168.1.7) の Web browser で、http://192.168.1.8:9980/ を開く。
自機のアドレスは 192.168.1.8。(仮)
FormRequestHandler は Thread ID = 2 で実行。
・08 – 10 行: 自機の Web browser で、http://localhost:9980/ を開く。
FormRequestHandler は Thread ID = 3 で実行。
・12 – 29 行(19 – 20 行を除く):
他機の Web browser で、Test It! ボタンを押す。
FormRequestHandler は Thread ID = 2 で、
ActiveDiagnosticsDispatcher は Thread ID = 1 で実行。
・19 – 20 行、31 – 42 行:
直後に自機の Web browser で、Test It! ボタンを押す。
FormRequestHandler は Thread ID = 3 で、
ActiveDiagnosticsDispatcher は Thread ID = 1 で実行。
他機発の処理が終了してから自機の処理が始まることに注意。
・44 – 46 行: 他機の Web browser で、Update ボタンを押す。
テスト結果表示が更新される。
・48 – 50 行: コンソールで、Control – C を入力し、MyHTTPServerTest を終了。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 | [0] main() start [0] ActiveDiagnosticsDispatcher start [2] FormRequestHandler start [2] Request from 192.168.1.7:49259 [2] FormRequestHandler end [3] FormRequestHandler start [3] Request from 127.0.0.1:49411 [3] FormRequestHandler end [2] FormRequestHandler start [2] Request from 192.168.1.7:49259 [1] ActiveDiagnosticsDispatcher::testImpl start [1] test #0 result: 0 [1] Elepsed time = 50.176mSec [1] ActiveDiagnosticsDispatcher::testImpl end [1] ActiveDiagnosticsDispatcher::testImpl start [3] FormRequestHandler start [3] Request from 127.0.0.1:49411 [1] test #1 result: -1 [1] Elepsed time = 50.114mSec [1] ActiveDiagnosticsDispatcher::testImpl end [1] ActiveDiagnosticsDispatcher::testImpl start [1] test #2 result: 0 [1] Elepsed time = 50.182mSec [1] ActiveDiagnosticsDispatcher::testImpl end [1] ActiveDiagnosticsDispatcher::testImpl start [2] FormRequestHandler end [1] test #0 result: 0 [1] Elepsed time = 50.172mSec [1] ActiveDiagnosticsDispatcher::testImpl end [1] ActiveDiagnosticsDispatcher::testImpl start [1] test #1 result: -1 [1] Elepsed time = 50.153mSec [1] ActiveDiagnosticsDispatcher::testImpl end [1] ActiveDiagnosticsDispatcher::testImpl start [1] test #2 result: 0 [1] Elepsed time = 50.570mSec [1] ActiveDiagnosticsDispatcher::testImpl end [3] FormRequestHandler end [2] FormRequestHandler start [2] Request from 192.168.1.7:49259 [2] FormRequestHandler end ^C [0] ActiveDiagnosticsDispatcher end [0] main() end |
Downloads
・ここをクリックすると、makefile や VC++ プロジェクトなど一式がダウンロードできます。
(2010.09.10 5:10 updated)
・2010年5月7日からのダウンロード数:1330
Subversion
・フリーの Subversion ホスティングサービス Assemblaで、ソースコードを管理しています。
Reference
・http://pocoproject.org にある Applications のプレセンテーション。(PDF)
![]() |
Copyright © 2010 Round Square Inc. All rights reserved. |
---|
0 Comments.