ソース解説
まずは、ソースの上から順に見ていきましょう。
インクルード・ファイル
インクルードするファイルは次のようになります。
#include "stdafx.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/ServerSocket.h" #include "Poco/Util/ServerApplication.h" #include "Poco/File.h"
Foundation、Util、Netコンポーネントを利用します。なお、Foundationコンポーネントのインクルードファイルは、POCOディレクトリ直下にありますので、"Poco/Foundation/File.h"ではなく、"Poco/File.h"となります。名前空間もFoundationは定義されておらず、Poco::Fileのように書き、Foundationは不要です。
次の行からはクラスの定義と実装になりますが、個々のクラスは後回しにして、プログラム本体の処理を見てみましょう。
メイン処理
最後の部分がメイン処理です。たったの5行です。
int _tmain(int argc, _TCHAR* argv[]) { MiniWebServer app; return app.run(argc, argv); }
MiniWebServerクラスがメインのアプリケーションクラスです。ここでは、そのクラスをインスタンス化し、run関数をコールして実行しているだけです。すべての処理は、MiniWebServerクラスに記述されています。
アプリケーションクラス
MiniWebServerクラスの定義と実装を見てみましょう。
class MiniWebServer: public Poco::Util::ServerApplication { protected: int main(const std::vector<std::string>& args) { int maxThreads = 1; // クライアント最大同時接続数 // 接続スレッド数設定 Poco::ThreadPool::defaultPool().addCapacity(maxThreads); // 設定値クラス Poco::Net::HTTPServerParams* pParams = new Poco::Net::HTTPServerParams; pParams->setMaxQueued(100); // 接続要求ソケット最大数 pParams->setMaxThreads(maxThreads); Poco::Net::ServerSocket svs(9980); // 9980ポート使用 // HTTPサーバクラスのインスタンス Poco::Net::HTTPServer srv(new MiniRequestHandlerFactory(), svs, pParams); srv.start(); // サーバ動作開始 waitForTerminationRequest(); // CTRL-C が押されるまで、 // またはプロセスが // 強制終了されるまで待機 srv.stop(); // サーバ動作ストップ return Application::EXIT_OK; } };
MiniWebServerクラスの1つ上の親クラスはPoco::Util::ServerApplicationクラスで、その親は、Poco::Util::Applicationクラスです。

通常のアプリケーションでは、Poco::Util::Applicationクラスを継承して、初期化、実行、および終了処理をオーバーライドしたクラスを実装することになります。Poco::Util::ServerApplicationクラスは、アプリケーションをサーバとして利用する場合に便利な機能が追加されています。
今回のアプリケーションは、サーバとして機能させるので、Poco::Util::ServerApplicationクラスを継承しています。最低限オーバライドすべき関数は、メインの実行処理を記述するmain関数です。
main関数は、protectedになっているように、外部のクラスからは直接呼ぶことはできません。最初の_tmain関数のところで書いたように、アプリケーションの実行は、公開関数であるrun関数をコールすることで実行します。
Applicationクラスのrun関数内部で、main関数がコールされています。また、main関数の例外を補足した場合のロギング処理などがカプセル化されています。
今回は、最小限の機能しか実装しませんので、main関数のみオーバーライドします。main関数と同様にオーバライドすべき関数には、初期化処理を記述するinitialize()、解放処理を記述するuninitialize()があります。
HTTPServerクラス
main関数では、Poco::Net::HTTPServerクラスをインスタンス化しています。HTTPServerクラスは、Webサーバのほとんどの機能をカプセル化しており、追加する処理は少なくてすみます。ここでは、HTTPServerクラスのインスタンスを生成した後、開始、終了待ち、終了処理の関数をコールしているだけです。
Poco::Net::ServerSocketクラスはソケットのクラスで、Poco::Net::HTTPServerParamsクラスは、HTTPServerオブジェクトの設定値用データクラスです。
今回のサーバは、クライアントもローカルの1接続と想定して、クライアント最大同時接続数を1にしています。もちろん、この値を増やせば、通常のWebサーバのように、複数クライアントの処理も可能です。
リクエストハンドラ・ファクトリ
HTTPServerクラスのコンストラクタに、引数としてMiniRequestHandlerFactoryクラスのオブジェクトを渡しています。名前から想像できるように、デザインパターンのFactoryMethodパターンの構造になっています。
class MiniRequestHandlerFactory: public Poco::Net::HTTPRequestHandlerFactory { public: Poco::Net::HTTPRequestHandler* createRequestHandler( const Poco::Net::HTTPServerRequest& request) { std::string fpass = "."; fpass += request.getURI(); Poco::File f(fpass); if ( !f.exists() || !f.isFile() ) return new NotFileHandler(); return new FileRequestHandler(); } };
MiniRequestHandlerFactoryクラスの親クラスは、Poco::Net::HTTPRequestHandlerFactoryクラスで、HTTPのリクエストハンドラ生成を一手に処理しています。HTTPリクエストハンドラとは、WebブラウザなどのHTTPクライアントからのリクエストに対して、サーバとしての処理を実装しているオブジェクトです。
MiniRequestHandlerFactoryクラスでは、FileRequestHandlerクラスとNotFileHandlerクラスの二つのハンドラオブジェクトを生成しています。
クライアントからリクエストされたURIをそのまま要求されたHTMLファイル名とみなし、カレントディレクトリに、そのファイルがあるかどうかを判定しています。ファイルがあれば、FileRequestHandlerクラスのインスタンスを生成、なければ、NotFileHandlerクラスのインスタンスを生成します。
リクエストハンドラ
すべてのリクエストハンドラは、Poco::Net::HTTPRequestHandlerクラスから継承します。実装しないといけない処理は、HTTPレスポンスコードと応答メッセージの設定です。handleRequest関数をオーバーライドして記述します。
FileRequestHandlerクラスでは要求されたファイルをオープンして、response.sendFile関数でその中身をそのままレスポンス文字列に設定しています。ファイル内容は、まったくチェックしていません。ちょっと乱暴ですので、実際の使用時には適宜エラー処理を追加してください。なお、正常応答の場合は、HTTPレスポンスコードの設定は不要です。
class NotFileHandler: public Poco::Net::HTTPRequestHandler { public: void handleRequest(Poco::Net::HTTPServerRequest& request, Poco::Net::HTTPServerResponse& response) { response.setStatusAndReason( Poco::Net::HTTPResponse::HTTP_NOT_FOUND); response.setContentType("text/html"); std::ostream& ostr = response.send(); ostr << "<html><head>"; ostr << "<title>MiniWebServer powered by POCO</title>"; ostr << "</head><body>ファイルがありません<br />"; ostr << "</body></html>"; } }; class FileRequestHandler: public Poco::Net::HTTPRequestHandler { public: void handleRequest(Poco::Net::HTTPServerRequest& request, Poco::Net::HTTPServerResponse& response) { try{ std::string fpass = "."; fpass += request.getURI(); response.sendFile(fpass, "text/html"); } catch(Poco::Exception& ) { } } };
もう1つのNotFileHandlerクラスは、エラーレスポンスです。おなじみの「要求されたファイルが見つからない」というエラーの処理です。ここではレスポンスの文字列を直接プログラムで記述しています。FileRequestHandlerクラスと同様に、ファイルを読み込む方法でもかまいせん。
実際にアプリケーションに組み込んで利用する場合は、適宜リクエストハンドラを追加することになります。本記事では単にファイルを返す処理しか実装していませんが、いくらでも自由に動的な処理を追加できます。
ビルドと実行
特に指定しないでビルドすると、POCOのインポートライブラリがリンクされ、実行時にDLLファイルを利用する形になります。Foundation、Util、Net、XMLの4つのコンポーネントすべて使用しますので、DLLファイルのパスを通すか、実行ファイルと同じフォルダにコピーしておきましょう。
もう1つ、実行する前に、あらかじめ動作確認用の適当なHTMLファイル(例えばindex.html)を作っておいてください。そのHTMLファイルを実行ファイルと同じところにコピーして、サーバを実行します。
そしてクライアントのブラウザを起動して、「http://localhost:9980/index.html」と入力してみてください。HTMLファイルが表示されるはずです。
最後に
POCOを使えば、この程度のコード量でWebサーバを動作させることができます。IISやApacheなどの本格的なWebサーバにはかないませんが、アプリケーションに組み込みたい場合や、ローカル用にWebサーバが使いたい場合などで、重宝すると思います。
今回紹介した以外にも、実用的なクラスが数多く揃っています。「車輪の再発明」をしないためにも、POCOの利用を一度検討してみてはいかがでしょうか。
参考資料
- 『詳説C++ 第2版』(大城 正典 著、ソフトバンククリエイティブ、2005年5月)
