C++によるhttpサーバー/webサーバーの作成
冒頭のくだり
実はこの記事、ずっと書きたいと思っていたのですが、時間不足に悩まされ、一気に仕上げてしまいたいのです。帰国前夜、上海の小さなホテルの中で、C++で簡単なhttpサーバーを構築する方法を書こうと珍しく張り切っていた。
私はほとんどの人が自分のウェブサイト(でも壊れて壊れてもそれを自分で行う)に憧れたいと信じて、私は最初はとても熱心だった、私はそれが2020年8月だったことを覚えて、私は最終的にソケットを学んだ情報のすべての種類をチェックしたので、私はチャットプログラムを書くために待つことができなかった、マッピングを移植する方法を勉強してきました。当時はまだ、接続が来たらスレッドを開いて、抜けたらスレッドを破棄するというのは非常に愚かで甘かったし、スレッドの生成と破棄のオーバーヘッドが大きいことも知っていましたが、それ以外の良い方法は考えもしませんでした。今振り返ると、この問題は非常に愚かで、なぜスレッドプーリングの技術を使うことを考えなかったのだろう?学習はステップバイステップの進化のプロセスであり、誰をうらやむことはありません、誰と比較することはありません、唯一の自分以上で
この記事は、多くの複雑な概念を伴わないし、読みにくいテンプレート関数を書いていない、コードはシンプルで読みやすい、この記事は、C++でhttpサーバーを書きたいすべての人のためのものです! この章では、私がhttpを学ぶときに遭遇した問題やアイデアを書いていきます。
主な記事
簡単なhttpサーバーはどう書けばいいの?簡単です、最も基本的な3つのことを返せばいいのです。
- ステータスコード
- 送信ファイルの長さ
- 送信するファイルの種類
ステータスコード 200(リクエストファイルが見つかりました)、404(リクエストファイルが見つかりません)など、この2つのステータスコードを比較的簡単に実装することも行いました。
ファイルの長さ 例えば、お客様がindex.htmlのページをリクエストした場合、ブラウザはこのファイルを受信したことをどうやって知るのでしょうか?それは、ファイルの長さに依存します。
文書タイプ htmlはhtml形式、cssはcss形式、画像は画像形式、zipはzip形式です ファイル形式に対応するファイルタイプの一覧表
これらの3つとリクエストされたファイルをクライアントに返すのは問題ありません(リクエストされたファイルが存在する場合)。
もっと複雑にしたい場合は、httpの様々なパラメータについて説明したこちらの記事を参照してください。 記事へのリンク
Http ワークフロー
ここでは、一般的なhttpのワークフローを大まかに説明します。クライアントはURLを通じてあなたのウェブサイトにアクセスします(クライアントは能動的に接続を要求し、サーバーは受動的に接続していることを常に覚えておいてください)。httpのデフォルトのポート番号が80であることを除いて、実際にはip+portを通じてアクセスされます。例えば、まだドメイン名やクラウドサーバーなどを持っていない場合、ローカルでどのようにテストするのでしょうか?それは、ブラウザのボックスにip:portを入力し、例えば127.0.0.1:9996と入力し、エンターキーを押してローカルに自分のhttpサーバーにアクセスすることである。もちろん、httpで顧客(リクエスト元)にホームページを提供し、顧客がページを指定しない場合は、ドメイン名や127.0.0.1:9996を入力するだけで、顧客にデフォルトホームページを提供する、これも実現したいことである。お客様がリクエストファイルを書き込むと、それが存在するかどうかを判断し、存在する場合はステータスコード200とリクエストファイルの内容を返します。これは、最も単純なGETを例にして、どのように判断しているのか、他もほぼ同じなので、興味のある学生は他を勉強してください。我々は、使用する 正規表現 を解析するために、顧客によって送信された要求がGETまたはPOSTまたは他の要求で、ファイルを要求するために分析アウトです。そして、ステートマシンの考え方で、ファイルが存在するかどうかを判断し、存在する場合はファイルの種類を判断します。というのが大まかな流れです。
Http.h
#pragma once
#include
#include
#include
#include
Http.cpp
#include "Http.h"
#include "TcpClient.h"
#include "Logger.h"
#include
#include
#include
#include
using namespace std;
void Http::addHeader(const string& head)
{
if (!head.empty())
{
header_ += head;
header_ += "\r\n";
// Console.WriteLine("I'm here head!= null" + header_);
}
// automatically add a closing
else
{
header_ += "\r\n";
// Console.WriteLine("I'm here head == null" + header_);
}
}
void Http::Header(bool flag)
{
// determine the header to send true means 200 false means 404
if(flag == true)
{
header_ = "HTTP/1.1 200 OK\r\n";
}
else
{
header_ = "HTTP/1.1 404 NOTFOUND\r\nContent-Length:0\r\n\r\n";
}
}
void Http::processHead()
{
string ContentType = "Content-Type:";
if (fileType_ == "html")
{
ContentType += "text/html";
}
else if (fileType_ == "js")
{
ContentType += "application/x-javascript";
}
else if(fileType_ == "css")
{
ContentType += "text/css";
}
else if(fileType_ == "jpg" || fileType_ == "png")
{
ContentType += "image/" + fileType_;
}
else if (fileType_== "zip" || fileType_ == "tar")
{
ContentType += "application/" + fileType_;
}
addHeader(ContentType);
// substitute perfect, to open the file filePath_ is the path to the requested file
fileSize_ = fileStat_.st_size;
string ContentLength = "Content-Length:" + to_string(fileSize_);
addHeader(ContentLength);
// add a closing at the end
addHeader("");
// Console.WriteLine("process fileContent_:" + );
}
void Http::addFilePath(const string& requestFile)
{
filePath_ += requestFile;
}
void Http::analyseFileType(const string& requestFile)
{
for (int i = 0; i < requestFile.size(); ++i)
{
if (requestFile[i] == '.')
{
// get what the request file ends with
fileType_ = requestFile.substr(i + 1);
}
}
}
bool Http::fileIsExist(){
fileFd_ = ::open(filePath_.c_str(),O_CLOEXEC | O_RDWR);
if (fileFd_ < 0)
{ // means the requested file was found
return false;
}
return true;
}
bool Http::analyseFile(const string& request)
{
// call the header of
// The ^ in [] starts with something or other and is placed inside [] to mean right or wrong
string pattern = "^([A-Z]+) ([A-Za-z./1-9-]*)";
regex reg(pattern);
smatch mas;
regex_search(request,mas,reg);
// because the subscript 0 is for the whole match
if(mas.size() < 3){
LOG_INFO("Not a normal request");
// return false directly for nothing
return false;
}
string requestMode = mas[1];
if(requestMode == "POST"){
isPostMode_ = true;
cout << "POST request !!!!! " << endl;
}
// The specific file to request
string requestFile = mas[2];
// Get the requested file first
bool flag;
if (requestFile == "/")
{ // give default value if it is /
filePath_.clear(); // Clear a zero first
filePath_ = path_;
filePath_ += "/run.html";
// Add the type of the file to the person as well
fileType_ = "html";
}
else
{
filePath_.clear(); // Clear a zero first
filePath_ = path_;
addFilePath(requestFile);
// Use the open function
}
flag = fileIsExist();
// if the file is not found
if(!flag){
LOG_INFO("The file the client wants was not found");
cout << filePath_ << endl;
return false;
}
::fstat(fileFd_,&fileStat_);
// If the file doesn't exist then there is no need to analyze the type
analyseFileType(requestFile);
return true;
}
void Http::SendFile(int clientFd,bool isRequestOk
Do not consider the case of high concurrency, the design of a synchronous blocking epoll can be, look at the three elements necessary for http has been able to write a server, my underlying socket using their own encapsulated network library, Reactor model, one loop per thread code file is more, so did not put up, but as long as the status code, the type of file (that large if, the length of the file) these three implementation will be able to build a simple http server. But as long as the status code, the file type (the big if), and the length of the file are realized, you can build a simple http server. You can use sendfile zero copy to send files
To add to this, the http protocol ends in \r\n. There is a \r\n at the end, for example, 404, HTTP/1.1 404 NOTFOUND\r\nContent-Length:0\r\n\r\n, followed by a \r\n at the end, and a \r\n at the end of the paragraph
Personal website links
Personal website
Source code address
2021.10.25 more, recently finally had time to refactor their own code, I feel that the writing is okay, attached links
github link
If you are interested, you can click a star hahahahaha, you can check it out on github
#include "Http.h"
#include "TcpClient.h"
#include "Logger.h"
#include
#include
#include
#include
using namespace std;
void Http::addHeader(const string& head)
{
if (!head.empty())
{
header_ += head;
header_ += "\r\n";
// Console.WriteLine("I'm here head!= null" + header_);
}
// automatically add a closing
else
{
header_ += "\r\n";
// Console.WriteLine("I'm here head == null" + header_);
}
}
void Http::Header(bool flag)
{
// determine the header to send true means 200 false means 404
if(flag == true)
{
header_ = "HTTP/1.1 200 OK\r\n";
}
else
{
header_ = "HTTP/1.1 404 NOTFOUND\r\nContent-Length:0\r\n\r\n";
}
}
void Http::processHead()
{
string ContentType = "Content-Type:";
if (fileType_ == "html")
{
ContentType += "text/html";
}
else if (fileType_ == "js")
{
ContentType += "application/x-javascript";
}
else if(fileType_ == "css")
{
ContentType += "text/css";
}
else if(fileType_ == "jpg" || fileType_ == "png")
{
ContentType += "image/" + fileType_;
}
else if (fileType_== "zip" || fileType_ == "tar")
{
ContentType += "application/" + fileType_;
}
addHeader(ContentType);
// substitute perfect, to open the file filePath_ is the path to the requested file
fileSize_ = fileStat_.st_size;
string ContentLength = "Content-Length:" + to_string(fileSize_);
addHeader(ContentLength);
// add a closing at the end
addHeader("");
// Console.WriteLine("process fileContent_:" + );
}
void Http::addFilePath(const string& requestFile)
{
filePath_ += requestFile;
}
void Http::analyseFileType(const string& requestFile)
{
for (int i = 0; i < requestFile.size(); ++i)
{
if (requestFile[i] == '.')
{
// get what the request file ends with
fileType_ = requestFile.substr(i + 1);
}
}
}
bool Http::fileIsExist(){
fileFd_ = ::open(filePath_.c_str(),O_CLOEXEC | O_RDWR);
if (fileFd_ < 0)
{ // means the requested file was found
return false;
}
return true;
}
bool Http::analyseFile(const string& request)
{
// call the header of
// The ^ in [] starts with something or other and is placed inside [] to mean right or wrong
string pattern = "^([A-Z]+) ([A-Za-z./1-9-]*)";
regex reg(pattern);
smatch mas;
regex_search(request,mas,reg);
// because the subscript 0 is for the whole match
if(mas.size() < 3){
LOG_INFO("Not a normal request");
// return false directly for nothing
return false;
}
string requestMode = mas[1];
if(requestMode == "POST"){
isPostMode_ = true;
cout << "POST request !!!!! " << endl;
}
// The specific file to request
string requestFile = mas[2];
// Get the requested file first
bool flag;
if (requestFile == "/")
{ // give default value if it is /
filePath_.clear(); // Clear a zero first
filePath_ = path_;
filePath_ += "/run.html";
// Add the type of the file to the person as well
fileType_ = "html";
}
else
{
filePath_.clear(); // Clear a zero first
filePath_ = path_;
addFilePath(requestFile);
// Use the open function
}
flag = fileIsExist();
// if the file is not found
if(!flag){
LOG_INFO("The file the client wants was not found");
cout << filePath_ << endl;
return false;
}
::fstat(fileFd_,&fileStat_);
// If the file doesn't exist then there is no need to analyze the type
analyseFileType(requestFile);
return true;
}
void Http::SendFile(int clientFd,bool isRequestOk
関連
-
解決策:エラー:'cout'は型名ではありません。
-
vs2015 はソースファイル stdio.h を見つけることができない 解決策
-
error: 'vector' does not name a type
-
VCのグローバル変数が*.objで既に定義されている場合の問題点
-
void* から char* への無効な変換」および「文字列定数から 'char*' への非推奨の変換」を解決 "
-
error: label 'xxxxxxx' [-fpermissive] にジャンプします。
-
ISO C++ではポインタと整数の比較は禁止されています[-fpermissive]。
-
文字列がこのスコープで宣言されていない 問題の解決
-
c/c++の "undefined reference to "の解決法
-
C++ inet_pton, inet_ntop 関数
最新
-
nginxです。[emerg] 0.0.0.0:80 への bind() に失敗しました (98: アドレスは既に使用中です)
-
htmlページでギリシャ文字を使うには
-
ピュアhtml+cssでの要素読み込み効果
-
純粋なhtml + cssで五輪を実現するサンプルコード
-
ナビゲーションバー・ドロップダウンメニューのHTML+CSSサンプルコード
-
タイピング効果を実現するピュアhtml+css
-
htmlの選択ボックスのプレースホルダー作成に関する質問
-
html css3 伸縮しない 画像表示効果
-
トップナビゲーションバーメニュー作成用HTML+CSS
-
html+css 実装 サイバーパンク風ボタン
おすすめ
-
std::logic_error' のインスタンスを投げた後に呼び出された実行エラー終了 what(): basic_string::_S_const
-
C++:ソースファイルを開くことができない問題
-
戦闘機ゲームのC++実装(ソースコード)
-
致命的なエラー LNK1169: 1つ以上の多重定義されたシンボルが見つかりました 解決策
-
C++のコンパイルエラーで修飾子が破棄される [-fpermissive] 。
-
c++ experience summary(1):linux c compile with warning: assign makes pointer from integer without cast reason.
-
munmap_chunk():不正なポインタとSegmentation faultのバグを解決。
-
std::allocator<char>::~allocator()' への未定義の参照
-
c++ベクトル使用集
-
デバッグエラー Assertion Failed 問題について