SHOEISHA iD

※旧SEメンバーシップ会員の方は、同じ登録情報(メールアドレス&パスワード)でログインいただけます

DeveloperZine(デベロッパージン)- エンジニアの意思決定を支える技術情報メディア ProductZine

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

オープンソースC++用クラスライブラリPOCO活用講座

POCO::Netライブラリによる組み込みWebサーバの実装

オープンソースC++用クラスライブラリPOCO活用講座(1)


ソース解説

 まずは、ソースの上から順に見ていきましょう。

インクルード・ファイル

 インクルードするファイルは次のようになります。

ヘッダインクルード部分
#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月)

この記事は参考になりましたか?

連載通知を行うには会員登録(無料)が必要です。
既に会員の方はを行ってください。
オープンソースC++用クラスライブラリPOCO活用講座連載記事一覧

もっと読む

この記事の著者

山田 祥寛(ヤマダ ヨシヒロ)

静岡県榛原町生まれ。一橋大学経済学部卒業後、NECにてシステム企画業務に携わるが、2003年4月に念願かなってフリーライターに転身。Microsoft MVP for Visual Studio and Development Technologies。執筆コミュニティ「WINGSプロジェクト」代表。主な著書に「独習シリーズ(Java・C#・Python・PHP・Ruby・JSP&サーブレットなど)」「速習シリーズ(ASP.NET Core・Vue.js・React・TypeScript・ECMAScript、Laravelなど)」「改訂3版JavaScript本格入門」「これからはじめるLaravel実践入門」「はじめてのAndroidアプリ開発 Kotlin編 」他、著書多数

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

WINGSプロジェクト 高江 賢(タカエ ケン)

WINGSプロジェクトについて>有限会社 WINGSプロジェクトが運営する、テクニカル執筆コミュニティ(代表 山田祥寛)。主にWeb開発分野の書籍/記事執筆、翻訳、講演等を幅広く手がける。 2026年時点での登録メンバは約50名で、現在も執筆メンバを募集中。興味のある方は、どしどし応募頂きたい。著書記事多数。 RSS X: @WingsPro_info(公式)、@WingsPro_info/wings(メンバーリスト) Facebook

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

この記事は参考になりましたか?

この記事をシェア

CodeZine(コードジン)
https://codezine.jp/article/detail/1792 2007/11/20 13:20

イベント

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

新規会員登録無料のご案内

  • ・全ての過去記事が閲覧できます
  • ・会員限定メルマガを受信できます

メールバックナンバー