Mongoose Embedded Compact Webサーバー

C / C ++でさまざまなプロジェクトを開発するプロセスでは、外部システムと通信したり、HTTPを介してクライアントにデータを送信したりすることがしばしば必要です。 例は、ルーター、ビデオ監視システムなどのWebインターフェイスを備えたデバイスだけでなく、すべてのWebサービスです。



この場合、通常何が行われますか? そうです、よく踏まれたパス-Apache / nginx + PHP。 そして、次の理由で地獄が始まります:



1.これらすべてをインストールして構成する必要があります。

2.これはすべて、かなりの量のリソースを消費します。

3. PHPから、どういうわけか、開発したシステムからデータを取得する必要があります。 運がよければ、DBMSにアクセスできます。



したがって、私は、他の多くの開発者のように、これらのすべての機能を開発中のシステムに直接押し込みたいと思っています。 これにより、紛れもない利点が得られます。



1.外部依存性が少ないため、インストールと構成が容易になります。

2.理論的にはリソース消費が少なくなります。

3.仲介なしで、製品から直接データを提供できます。

しかし同時に、HTTP接続の処理、解析などの微妙な点に煩わされたくありません。



そのような解決策があります。 また、この記事では、それらの1つであるMongoose Embedded Server(MongoDBと混同しないでください)を表面的に紹介したいと思います。



主な機能



Mongooseは元々、組み込みWebサーバーとして位置付けられていました。 これは、C / C ++プロジェクトがある場合-プロジェクトにmongoose.cとmongoose.hの2つのコンパクトファイルを含めるだけで、数十行のコードを書くだけで、HTTPリクエストを処理できることを意味します!



しかし、近年、Mongooseは深刻な成長を遂げており、現在では単なる組み込みWebサーバーではなく、組み込みの「ネットワークライブラリ」全体となっています。 つまり、HTTPサーバーに加えて、その助けを借りて、TCPおよびUDPソケット、HTTPクライアント、WebSocket、MQTT、DNSクライアント、DNSサーバーなども実装できます。



また、このライブラリの大きな利点は、非同期で動作することです。 任意のイベント(接続の確立、切断、データの受信、送信、要求の受信など)で呼び出されるイベントハンドラー関数を記述するだけで、プログラムのメインループに、発生するたびにハンドラーを呼び出す関数を挿入します。イベント。



したがって、プログラムはブロックせずにシングルスレッドにすることができ、リソースと生産性の節約にプラスの効果があります。



使用例



わかりやすくするための抽象的な例:



#include "mongoose.h" //     struct mg_mgr mg_manager; //  http- struct mg_connection *http_mg_conn; //  http- struct mg_serve_http_opts s_http_server_opts; const char *example_data_buf = "{ \"some_response_data\": \"Hello world!\" }"; const char *html_error_template = "<html>\n" "<head><title>%d %s</title></head>\n" "<body bgcolor=\"white\">\n" "<center><h1>%d %s</h1></center>\n" "</body>\n" "</html>\n"; //----------------------------------------------------------------------------- //     void http_request_handler(struct mg_connection *conn, int ev, void *ev_data) { switch (ev) { case MG_EV_ACCEPT: { //   -      conn->sock break; } case MG_EV_HTTP_REQUEST: { struct http_message *http_msg = (struct http_message *)ev_data; //  HTTP- // http_msg->uri - URI  // http_msg->body -   //    if (mg_vcmp(&http_msg->uri, "/api/v1.0/queue/get") == 0) { mg_printf(conn, "HTTP/1.1 200 OK\r\n" "Server: MyWebServer\r\n" "Content-Type: application/json\r\n" "Content-Length: %d\r\n" "Connection: close\r\n" "\r\n", (int)strlen(example_data_buf)); mg_send(conn, example_data_buf, strlen(example_data_buf)); //      conn->flags // ,        : conn->flags |= MG_F_SEND_AND_CLOSE; } //    404 else if (strncmp(http_msg->uri.p, "/api", 4) == 0) { char buf_404[2048]; sprintf(buf_404, html_error_template, 404, "Not Found", 404, "Not Found"); mg_printf(conn, "HTTP/1.1 404 Not Found\r\n" "Server: MyWebServer\r\n" "Content-Type: text/html\r\n" "Content-Length: %d\r\n" "Connection: close\r\n" "\r\n", (int)strlen(buf_404)); mg_send(conn, buf_404, strlen(buf_404)); conn->flags |= MG_F_SEND_AND_CLOSE; } //   URI -   else mg_serve_http(conn, http_msg, s_http_server_opts); break; } case MG_EV_RECV: { //  *(int *)ev_data  break; } case MG_EV_SEND: { //  *(int *)ev_data  break; } case MG_EV_CLOSE: { //   break; } default: { break; } } } bool flag_kill = false; //----------------------------------------------------------------------------- void termination_handler(int) { flag_kill = true; } //--------------------------------------------------------------------------- int main(int, char *[]) { signal(SIGTERM, termination_handler); signal(SIGSTOP, termination_handler); signal(SIGKILL, termination_handler); signal(SIGINT, termination_handler); signal(SIGQUIT, termination_handler); //    s_http_server_opts.document_root = "/var/www"; //       s_http_server_opts.enable_directory_listing = "no"; //   mg_mgr_init(&mg_manager, NULL); //    localhost:8080    -  http_request_handler http_mg_conn = mg_bind(&mg_manager, "127.0.0.1:8080", http_request_handler); if (!http_mg_conn) return -1; //   http mg_set_protocol_http_websocket(http_mg_conn); while (!flag_kill) { //    -   //    mg_connection->sock   //   (   )      select/poll, //     sleep- // ... // int ms_wait = 1000; //           ms_wait   //     bool has_other_work_to_do = false; //       mg_mgr_poll(&mg_manager, has_other_work_to_do ? 0 : ms_wait); } //    mg_mgr_free(&mg_manager); return 0; }
      
      





接続は、クライアントが閉じるまで、または明示的に閉じるまで(conn->フラグを使用)、開いたままになることに注意してください。 これは、ハンドラー関数を終了した後でもリクエストを処理できることを意味します。



したがって、リクエストの非同期処理では、リクエストキューと接続制御のみを実装できます。 そして、データベースと外部データソース/コンシューマーに対して非同期クエリを作成できます。



理論的には、非常に美しいソリューションを取得する必要があります!

コンパクトなデバイスを管理するためのWebベースのインターフェース(AJAX上)の作成や、HTTPプロトコルを使用したさまざまなAPIの作成に最適です。



そのシンプルさにもかかわらず、これはスケーラブルなソリューションでもあるように思えます(もちろん、これがアプリケーションのアーキテクチャ全体に適用される場合)。 あなたはnginxプロキシを前に置くことができます:

  location /api { proxy_pass http://127.0.0.1:8080; }
      
      





それでは、バランサーをいくつかのインスタンスに接続できます...



おわりに



GitHubプロジェクトのページから判断すると、まだ活発に開発中です。



軟膏の巨大なハエはライセンスのままです-GPLv2、および小規模プロジェクトの商用ライセンスの価格は噛み付きます。



読者の一人が、特に本番環境でこのライブラリを使用している場合は、コメントを残してください!



All Articles