完全なライブラリコードは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