最も単純なイメージWebサーバーを作成する例としてのlibeventライブラリの知識

この記事では、 libeventライブラリを使用して、クライアントの要求に応じてjpegイメージファイルを生成する単純なWebサーバーを作成する方法を示します。



libeventライブラリは、プログラマーにクロスプラットフォームの非同期ネットワークAPIへのアクセスを提供します。 このライブラリに基づいて、高性能のネットワークアプリケーションを作成できます。 たとえば、 libeventはMemcached (分散キャッシュシステム)やTOR (分散匿名ネットワーク)などの有名なアプリケーションで使用されます。







サーバーコードはこちらからダウンロードできます。



main()関数でコードのレビューを始めましょう:



75:int main (int argc, char *argv[])

76:{

77: struct event_base *ev_base;

78: struct evhttp *ev_http;

79:

80: if (argc != 3) {

81: printf ("Usage: %s host port\n", argv[0]);

82: exit (1);

83: }

84:

85: ev_base = event_init ();

86:

87: ev_http = evhttp_new (ev_base);

88: if (evhttp_bind_socket (ev_http, argv[1], (u_short)atoi (argv[2]))) {

89: printf ("Failed to bind %s:%s\n", argv[1], argv[2]);

90: exit (1);

91: }

92: evhttp_set_cb (ev_http, "/img", http_img_cb, NULL);

93:

94: event_base_dispatch (ev_base);

95:

96: return 0;

97:}








Webサーバーは、コマンドラインから、サーバーがリッスンするIPアドレスとポートの2つのパラメーターを受け入れます。 行80〜83では、コマンドラインから渡されたパラメーターの数を確認します。 argv []配列の最初の要素は常にアプリケーション自体の名前であることを忘れないでください。



行85は、 libeventライブラリを初期化し、「 struct event_base 」データ構造へのポインターを返します。 libeventライブラリ関数への以降のすべての呼び出しでは、この変数が渡されます。



87行目から92行目まで、HTTPサーバーが作成され、コマンドラインで指定されたアドレスで盗聴を初期化する関数が呼び出されます。 突然このアドレスが他のアプリケーションによって既にリッスンされている場合、またはこのアドレスを占有する権限がない場合(ルートとして起動されたアプリケーションのみが最大1024までポートを占有できることに注意)、プログラムは画面にメッセージを表示して実行を終了します。 次に、「/ img」URIのコールバック関数を設定します。



libeventライブラリ全体は、いわゆる「コールバック」関数(コールバック関数)に基づいて構築され、次のように機能します。イベントが発生すると(「イベント」-英語の「イベント」、したがってライブラリの名前)関数が呼び出されました。 これを行うには、 libevent APIを使用して、特定のイベント(タイマーイベント、データを受信する準備ができているソケット、URI要求など)に関数を登録します。 将来、このイベントが発生すると、必要なすべてのパラメーターが転送される関数が呼び出されます。



行94はイベントループを開始します。 この例では、HTTPサーバーのシャットダウンを提供しなかったため、ループは無限の回数実行されます。 このサイクルを終了してサーバーをシャットダウンするには、コンソールでCTRL + Cを押す必要があります。



ここで、「/ img」HTTP要求の処理を検討します。



13:static void http_img_cb (struct evhttp_request *request, void *ctx)

14:{

15: struct evbuffer *evb;

16: int fd;

17: const char *fname;

18: struct stat stbuf;

19: int total_read_bytes, read_bytes;

20: struct evkeyvalq uri_params;

21:

22: evb = evbuffer_new ();

23:

24: printf ("Request from: %s:%d URI: %s\n", request->remote_host, request->remote_port, request->uri);

25:

26: evhttp_parse_query (request->uri, &uri_params);

27: fname = evhttp_find_header (&uri_params, "name");

28:

29: if (!fname) {

30: evbuffer_add_printf (evb, "Bad request");

31: evhttp_send_reply (request, HTTP_BADREQUEST, "Bad request", evb);

32: evhttp_clear_headers (&uri_params);

33: evbuffer_free (evb);

34: return;

35: }








行13は、 http_img_cb()関数へのコールバックを宣言しています 関数が呼び出されると、引数として、構造体「 struct evhttp_request 」へのポインターが与えられます。これには、HTTP要求に関するすべての必要な情報とユーザーデータへのポインターが含まれます。 この例では、変数「 ctx 」は使用されていません。

行22は、タイプstruct evbuffer *の変数を初期化します。



libeventでは 、「 struct evbuffer 」構造は、I / Oデータ(「I / O」)を操作するための主要なタイプです。 「 struct evbuffer 」を操作するためのAPIを使用すると、データを効率的に読み取り、書き込み、検索できます。



26行目は、 libevent関数「 evhttp_parse_query ()」を呼び出します。この関数は、URIストリングを取得し、URIパラメーターから「key」→「value」という値を持つリストを返します。 たとえば、リクエスト「http:// serverIP:port / img?Aa = bb&cc = dd」 が発生し、関数「 evhttp_parse_query(request-> uri、&uri_params) 」が呼び出された場合、ペア「aa」→「uri_paramsに含まれます」 bb "、" cc "→" dd "。 libevent APIには、「 struct evkeyvalq 」タイプを操作するためのいくつかの関数が含まれています。



27行目は、これらの関数の1つを呼び出します。これらの関数は、 struct evkeyvalq構造へのポインターと、キーのある行を取ります。 この関数はキー値を返します。キーが見つからない場合は「 NULL 」を返します。 この例では、URIパラメーターのキー「名前」を受け入れます。これには、必要なファイルの名前が含まれます。



行29〜35は、「名前」キーがURIパラメーターで指定されていることを確認します。 指定されていない場合、関数「 evhttp_send_reply ()」を使用して、HTTPコード400とユーザー「Bad request」のメッセージを含む応答をクライアントに送信します。 次に、関数「 evhttp_clear_headers ()」を呼び出してリスト「キー」→「値」をクリアし、関数「 evbuffer_free ()」を呼び出して構造体「 struct evbuffer 」が占有しているメモリを解放し、関数を終了します。



関数evhttp_send_reply ()は、HTTPメッセージをクライアントに送信する主な方法です。パラメーターでは、構造体struct evhttp_requestへのポインター、HTTPコード( libeventに HTTPコードを持つ定義済み定数がいくつかあります)、ブラウザーの短いテキストメッセージと構造体「 struct evbuffer 」-このデータはクライアントに直接表示されます。



37: if ((fd = open (fname, O_RDONLY)) < 0) {

38: evbuffer_add_printf (evb, " File %s not found", fname);

39: evhttp_send_reply (request, HTTP_NOTFOUND, "File not found", evb);

40: evhttp_clear_headers (&uri_params);

41: evbuffer_free (evb);

42: return;

43: }

44: if (fstat (fd, &stbuf) < 0) {

45: evbuffer_add_printf (evb, "File %s not found", fname);

46: evhttp_send_reply (request, HTTP_NOTFOUND, "File not found", evb);

47: evhttp_clear_headers (&uri_params);

48: evbuffer_free (evb);

49: close (fd);

50: return;

51: }








行37〜51は、読み取り用にファイルを開きます。 ファイル名は「name」パラメーターから取得されます。 次に、 fstat ()関数が呼び出され、ファイルに関するさまざまなシステム情報が返されます。 関数のいずれかでエラーが発生した場合、HTTPコード404とユーザーへのメッセージ「File not found」を含む応答をクライアントに送信し、メモリを解放して関数を終了します。



53: total_read_bytes = 0;

54: while (total_read_bytes < stbuf.st_size) {

55: read_bytes = evbuffer_read (evb, fd, stbuf.st_size);

56: if (read_bytes < 0) {

57: evbuffer_add_printf (evb, "Error reading file %s", fname);

58: evhttp_send_reply (request, HTTP_NOTFOUND, "File not found", evb);

59: evhttp_clear_headers (&uri_params);

60: evbuffer_free (evb);

61: close (fd);

62: return;

63: }

64: total_read_bytes += read_bytes;

65: }








53〜65行目は、開いているファイルからstruct evbuffer構造体にデータを読み込みます。 関数 " evbuffer_read ()"は、構造体 " struct evbuffer "へのポインター、開いているファイルのハンドル、および読み込むバイト数をパラメーターに取ります。 この関数は、ファイルから読み取ったバイト数を返します。 このような状況では、ファイルが大きすぎて「 evbuffer_read ()」の1回の呼び出しと見なすことができない可能性があります。その後、「 fstat ()」の呼び出しから取得したファイルサイズで既に読み取ったバイト数をチェックし、必要に応じて読み取りを繰り返します。



66: evhttp_add_header (request->output_headers, "Content-Type", "image/jpeg");

67: evhttp_send_reply (request, HTTP_OK, "HTTP_OK", evb);

68:

69: evhttp_clear_headers (&uri_params);

70: evbuffer_free (evb);

71: close (fd);

72:}








行66〜72は、ファイルのコンテンツをHTTPコード200でクライアントに送信します。「Content-Type:image / jpeg」ヘッダーをHTTP応答に追加した後、これはクライアントブラウザーが画像のコンテンツを正しく表示するために必要です。 その後、メモリを解放し、ファイルを閉じて関数を終了します。



組み立てとテスト。



いくつかのjpegイメージを現在のディレクトリにコピーし、ファイル名を覚えておいてください。



このコードをコンパイルするには、コンピューターにlibeventバージョン1.4.XXライブラリーをインストールする必要があります(XXを利用可能な最新バージョンに置き換えてください)。 一部のLinuxディストリビューションでは、「 libevent-dev 」パッケージをインストールする必要があります。

Gentooの場合: emerge libevent





Ubuntuの場合: aptitude install libevent-dev







ライブラリのインストール後、プログラムコードをmain.cファイルに保存すると、コンパイルを試行できます。

gcc main.c -o web_server -levent







エラーがなかった場合、web_serverファイルが現在のディレクトリに表示されます。 IPアドレスとポートのパラメーターで開始します。次に例を示します。

./web_server 127.0.0.1 8090







マシンでブラウザを開き、次のURLを入力します。 http://127.0.0.1:8090/img?name=_jpeg_





画像が表示されます。 おっと、Webサーバーを作成したばかりです!



セキュリティについて少し。

この形式のWebサーバーは、アクセスされているファイルの検証がないため、インターネットでの使用には適していません。 次のリクエストを送信できるとします: " http://serverIP:port/img?name=/etc/passwd



http://serverIP:port/img?name=/etc/passwd



"およびシステムユーザーのリストを含むファイルがクライアントに送信されます。 これは別個の記事全体のトピックであるため、セキュリティの考慮を意図的に省略しました。 「 -static 」フラグを付けてコンパイルした後、「 chroot 」環境でそのようなWebサーバーを実行することが安全であることのみをアドバイスできます。



次は何ですか。

おそらく、すぐに簡単なWebサーバーの開発に関する続編を書いて、画像ファイルをキャッシュする方法を示します。 つい最近、バイナリプロトコルとlibevent APIを使用して、サーバーとFlashクライアントの相互作用を整理する方法を説明したい記事を書き始めました。

libevent APIを使用して実行できるさまざまな興味深いことがあります。 libeventを使用したプログラムの開発を区別する主なものは、その単純さと、同時に、結果のプログラムの実行の効率と速度です。 「 Learning Libevent 」という本を必ず読んでください。 コーディング成功!



何らかの理由で強調表示されたPSコードは表示されません; /



All Articles