C ++最短Webサーバー

前回の記事で 、httpおよびhttpsプロトコルを使用して単一のファイルを転送する単純なサーバーを作成する方法について説明しました。 時間が経ち、私はこのコードからユニバーサルライブラリを作成して、サーバーをすばやく作成することにしました。



完全なライブラリコードはgithubで表示できます。簡単に言うと、いくつかの「エジプトブラケット」、新しい機能を備えたラムダ関数、およびテンプレートを追加しました。 これまでのところ、結果は、合計サイズが22.5キロバイトの5つのファイルで構成される、非同期サーバーを作成するためのクロスプラットフォームライブラリになりました。 Linux用のライブラリのバージョンは、サイズが18キロバイトの単一ファイル(517行のコード)で構成されています。



この記事では、ライブラリの仕組みを簡単に説明し、ライブラリを使用して静的サイト用の完全に機能するWebサーバーを作成する方法を示します。





送信したいWebサーバーコードはすべて2つのファイルにあります。

最初のファイルはserv.cppと呼ばれ、最小限のコードが含まれています。

#include "http_server.h" using namespace server; CServer<CHttpClient> s(8085, 1111); int main() {return 0;}
      
      







最初の行には、残りのWebサーバーコードが記述されているファイルが含まれています。 4行目は低レベルのライブラリを開始します。これについては投稿の冒頭で述べました。



ご覧のとおり、ライブラリを操作するには、最初にCServer型の変数を作成する必要があります。この変数に、ユーザークラスの名前とサーバーがリッスンするポート番号を渡す必要があります。

上記の例では、ライブラリはtcp接続を受信するためのポート8085とsslのポート1111でクラスCHttpClient(下記参照)で開始されます。



ライブラリは、カスタムクラスを使用したメッセージング上に構築されます。 現在、次のメッセージが定義されています。

  enum MESSAGE { I_READY_EPOLL, I_ACCEPTED, I_READ, I_ALL_WROTE, PLEASE_READ, PLEASE_WRITE_BUFFER, PLEASE_WRITE_FILE, PLEASE_STOP };
      
      







「I_」で始まるメッセージはライブラリによって送信され、「PLEASE_」で始まるメッセージはライブラリに送信できます。

Webサーバーを実装するには、そのようなクラスを記述するだけで十分です。

  class CHttpClient { public: const MESSAGE OnAccepted(shared_ptr<vector<unsigned char>> pvBuffer) {***} const MESSAGE OnWrote(shared_ptr<vector<unsigned char>> pvBuffer) {***} const MESSAGE OnRead(shared_ptr<vector<unsigned char>> pvBuffer) {***} };
      
      





これらの3つのパブリック関数は、メッセージとデータを交換するためにライブラリによって呼び出されるため、必要です。

データ交換は、関数の入力パラメーターと出力パラメーターの両方である「クリップボード」を介して行われます。



最初は、ここでCHttpClientクラスの作成を段階的にペイントしたかったのですが、必要に応じて、Habrovskの人々は私なしでコメント付きの115行のコードを理解できると判断しました。 したがって、ここでその全体を簡単に説明します。

ソースhttp_server.h
 #include "server.h" #define ROOT_PATH "./wwwroot" #define ERROR_PAGE "error.html" #define DEFAULT_PAGE "index.html" namespace server { class CHttpClient { int m_nSendFile; off_t m_nFilePos; unsigned long long m_nFileSize; enum STATES { S_READING_HEADER, S_READING_BODY, S_WRITING_HEADER, S_WRITING_BODY, S_ERROR }; STATES m_stateCurrent; map<string, string> m_mapHeader; void SetState(const STATES state) {m_stateCurrent = state;} const bool ParseHeader(const string strHeader) { m_mapHeader["Method"] = strHeader.substr(0, strHeader.find(" ") > 0 ? strHeader.find(" ") : 0); if (m_mapHeader["Method"] != "GET") return false; const int nPathSize = strHeader.find(" ", m_mapHeader["Method"].length()+1)-m_mapHeader["Method"].length()-1; if (nPathSize < 0) return false; m_mapHeader["Path"] = strHeader.substr(m_mapHeader["Method"].length()+1, nPathSize); return true; } const MESSAGE OnReadHeader(const string strHeader, shared_ptr<vector<unsigned char>> pvBuffer) { cout << "Header read\n"; if (!ParseHeader(strHeader)) m_mapHeader["Path"] = ERROR_PAGE; if (m_mapHeader["Path"] == "/") m_mapHeader["Path"] += DEFAULT_PAGE; cout << "open file" << ROOT_PATH << m_mapHeader["Path"].c_str() << "\n"; if ((m_nSendFile = _open((ROOT_PATH+m_mapHeader["Path"]).c_str(), O_RDONLY|O_BINARY)) == -1) return PLEASE_STOP; struct stat stat_buf; if (fstat(m_nSendFile, &stat_buf) == -1) return PLEASE_STOP; m_nFileSize = stat_buf.st_size; //    http  std::ostringstream strStream; strStream << "HTTP/1.1 200 OK\r\n" << "Content-Length: " << m_nFileSize << "\r\n" << "\r\n"; //  pvBuffer->resize(strStream.str().length()); memcpy(&pvBuffer->at(0), strStream.str().c_str(), strStream.str().length()); return PLEASE_WRITE_BUFFER; } explicit CHttpClient(CHttpClient &client) {} public: CHttpClient() : m_nSendFile(-1), m_nFilePos(0), m_nFileSize(0), m_stateCurrent(S_READING_HEADER) {} ~CHttpClient() { if (m_nSendFile != -1) _close(m_nSendFile); } const MESSAGE OnAccepted(shared_ptr<vector<unsigned char>> pvBuffer) {return PLEASE_READ;} const MESSAGE OnWrote(shared_ptr<vector<unsigned char>> pvBuffer) { switch(m_stateCurrent) { case S_WRITING_HEADER: if (m_nSendFile == -1) return PLEASE_STOP; SetState(S_WRITING_BODY); pvBuffer->resize(sizeof(int)); memcpy(&pvBuffer->at(0), &m_nSendFile, pvBuffer->size()); return PLEASE_WRITE_FILE; default: return PLEASE_STOP; } } const MESSAGE OnRead(shared_ptr<vector<unsigned char>> pvBuffer) { switch(m_stateCurrent) { case S_READING_HEADER: { //  http     const std::string strInputString((const char *)&pvBuffer->at(0)); if (strInputString.find("\r\n\r\n") == strInputString.npos) return PLEASE_READ; switch(OnReadedHeader(strInputString.substr(0, strInputString.find("\r\n\r\n")+4), pvBuffer)) { case PLEASE_READ: SetState(S_READING_BODY); return PLEASE_READ; case PLEASE_WRITE_BUFFER: SetState(S_WRITING_HEADER); return PLEASE_WRITE_BUFFER; default: SetState(S_ERROR); return PLEASE_STOP; } } default: return PLEASE_STOP; } } }; }
      
      









少し説明:

最初の行は、低レベルのマイクロライブラリをオンにします。

次に、サイトのデフォルトのディレクトリとページが決定され、

次に、クラスの状態を制御し、リクエストヘッダーを解析する関数があります。

コールバック関数は、クラスの現在の状態に応じてメッセージをライブラリに返します。



そのため、githubでVisual Studio 2012の完成したプロジェクトを見つけることができます。

Linuxの場合、serv.cpp、server.h、http_server.h、およびca-cert.pemファイルのみが必要です。 Gcc 4.5コンパイラ以上:「g ++ -std = c ++ 0x -L / usr / lib -lssl -lcrypto serv.cpp」



コードを何も変更しない場合、サーバーの動作を確認するには、少なくともindex.htmlを./wwwrootディレクトリに配置する必要があります



稼働中のサーバーは次の場所で確認できます。

http://unblok.us:8085/



または

https://unblok.us:1111

サーバーはhabroeffectのテストに失敗しました。理由はわかります。 ちょっと考え出した-修正した。



All Articles