「はじめに」の記事の内容は、アプリケーション層プロトコルのHTTPプロトコルについて大まかに説明したものです。
「所属コラム」ネットワークプログラミング
「ホームページリンク」個人ホームページ
《作者》 メイプルリーフ氏(fy)
「メイプルリーフさんはちょっと文学的」「文章シェア」
ことわざにあるように、弓を開いたら後戻りはできず、結果は 3 つだけです。矢が折れる、矢が落ちる、矢が的に当たるという 3 つだけです。
——ジャン・シャオイン『蘇東坡:世界で一番真実の愛』
目次
1. HTTP プロトコルの概要
HTTP(Hyper Text Transfer Protocol)
このプロトコルはハイパーテキスト転送プロトコルとも呼ばれ、アプリケーション層で動作する要求/応答プロトコルです。
アプリケーション層プロトコルは自分でカスタマイズできると言いましたが、実際には、優秀なエンジニアがすでにいくつかの既製プロトコルを定義しており、アプリケーション層プロトコルの HTTP (Hypertext Transfer Protocol) は、直接参照するためのその 1 つです。
次に、URL を知る
通常、私たちが一般的に「URL」と呼んでいるものは、実際にはURL
URL(Uniform Resource Lacator)
これはユニフォーム リソース ロケーターと呼ばれ、通常 URL と呼ばれるものです。
URL は大まかに次の部分で構成されます。
(1) プロトコルスキーム名
http://
プロトコルの名前を示しますhttp
。リクエストを行うときに使用する必要があるプロトコルを示します。私たちが日常生活でインターネット上でよく目にするプロトコルは次のとおりです。説明したいのは、このプロトコルはセキュアプロトコルと呼ばれているということですhttp
。データ送信プロトコルについては、次の章で説明します。https
http协议
https
(2) ログイン情報
usr:pass
ログインユーザーのユーザー名やパスワードなどのログイン認証情報を示します。このフィールドはほとんどの URL で省略されるようになりました。
(3) サーバーアドレス
www.example.jp
サーバー アドレス (ドメイン名とも呼ばれます) を示します。このドメイン名はホストを一意に識別するためのアドレスであり、このドメイン名はアドレスIP
に解決され、ドメイン名解決サーバーによってドメイン名解決が完了します。IP
Linux では、ping
次のコマンドでドメイン名を解決できます。
(4) サーバーのポート番号
80
サーバーのポート番号を示します。http
プロトコルのデフォルトのポート番号は です80
。https
プロトコルのデフォルトのポート番号は です443
。- URLでは、サービスとポート番号の対応が明確である(コードが記述されている)ため、一般的にサーバのポート番号は省略されており、利用時にプロトコルに対応したポート番号を指定する必要はありません。プロトコル
http
_
(5) 階層化されたファイルパス
/dir/index.htm
アクセスするリソースが配置されているパスを示します- 1 つ目は
/
、Web のルート ディレクトリです。Linux のルート ディレクトリではありません。Web のルート ディレクトリは、Linux 上の任意のディレクトリです。 - サーバーにアクセスする目的は、サーバー上の特定のリソースを取得することです。対応するサーバー プロセスは、前のドメイン名とポートを通じてすでに見つかります。この時点で行う必要があるのは、リソースが配置されているパスを指定することです。 。
http
プロトコルとは、リモート サーバーからローカル サーバーにリソースを取得するためのプロトコルであり、
テキスト、音声、画像、Web ページなど、インターネット上で目にするものはすべてリソースです。これらのリソース (ファイル) は、特定のサーバーに保存する必要があります。HTTP
このプロトコルはさまざまな種類のファイルリソースを送信できるため、テキスト転送プロトコルではなくハイパーテキスト転送プロトコルと呼ばれます。転送できるファイルリソースの種類は超
ワードに反映されます。
(6) クエリ文字列
uid=1
リクエスト時に指定されたパラメータを&
記号で区切って表します。
(7) フラグメント識別子
ch1
リソースの部分的な補足であるフラグメント識別子を表します。
3、urlencode と urldecode
URL 内の や などの文字は、/
URL?
によって特別な意味として解釈されます。したがって、これらの文字はランダムに出現することはできません。
たとえば、これらの特殊文字がパラメータで必要な場合は、最初に特殊文字をエスケープする必要があります。
エスケープのルールは次のとおりです。
トランスコードする必要がある文字を に変換し16进制
、右から左に 4 桁 (4 桁未満で直接処理) を取得し、2 桁ごとに 1 桁を作成し、前に追加して、次のようにエンコードします%
。%XY
たとえば、ブラウザで何かを検索すると、次のようになります。
たとえば、 を検索する場合C++
、wd
次のすべてが検索パラメータ (wd
パラメータの名前) であり、+
プラス記号は URL 内の特殊記号であり、+
文字が 16 進数に変換された後の値は である0x2B
ため、次のように+
なります。注にエンコードされます%2B
: 漢字と特殊文字は変換する必要があります。このプロセスは URL になります。encode
サーバーがリクエストを受信すると、%xx
特殊記号をデコードします。このプロセスは URL と呼ばれますdecode
。を使用してC++
サーバーを作成する場合は、この作業を行う必要があります (ソース コードはインターネット上で入手できるので、それを直接使用します)
デコード プロセスを確認しましょう。インターネットでオンラインの URL デコード ツールを検索して使用します。
4 番目に、HTTP プロトコルのリクエストとレスポンスの形式
HTTP は、リクエストとレスポンスに基づくアプリケーション層サービスです。クライアントとして、サーバーへのリクエストを開始できます。request
サーバーはこれを受信するrequest
と、request
それを分析してアクセスしたいリソースを見つけます。これを完了するための応答response
(HTTP リクエスト) を作成します。この作業メソッドに基づいて、これはorモードrequest&response
と呼ばれます。c は を意味し、s は を意味し、b はブラウザがプロトコルのクライアントであることを意味します。つまり、プロトコルを使用するためにクライアントを記述する必要はありません。cs
bs
client
server
browser
http
http
4.1 HTTPリクエストプロトコルのフォーマット
HTTP リクエストのプロトコル形式は大まかに次のとおりです。
HTTP リクエストは 4 つの部分で構成されます。
- リクエスト行: [リクエストメソッド]+[URL]+[http バージョン]+[\r\n]
- リクエスト ヘッダー: リクエストの属性。これらの属性は
name:value
[\r\n] で終わる + の形式でリストされます。 - 空行: 空行 (\r\n) がある場合は、リクエスト ヘッダーの終わりを示します。
- リクエスト本文: リクエスト本文は空の文字列にすることができ、空にすることもできます。
Content-Length
リクエストボディが存在する場合、リクエストボディの長さを識別するためにリクエストヘッダーにリクエストボディが存在します。
知らせ: http は特殊記号 (\r\n) を使用してコンテンツを分割します
通常、最初の 3 つの部分は HTTP プロトコルに含まれており、リクエスト本文の最後の部分は省略できます (空の文字列)。リクエストはパッケージ化された後、次の層であるトランスポート層に直接配信されます。それを処理する
4.2 HTTP 応答プロトコルの形式
HTTP 応答プロトコルの形式はおおよそ次のとおりです。
HTTP 応答は 4 つの部分で構成されます。
- ステータス行: [http バージョン]+[ステータス コード]+[ステータス コードの説明]]+[\r\n]
- 応答ヘッダー: 応答の属性。これらの属性は
name:value
[\r\n] で終わる + の形式でリストされます。 - 空行: 空行 (\r\n) がある場合は、応答ヘッダーの終わりを示します。
- 応答本文: 応答本文は空の文字列にすることができ、空にすることもできます。
Content-Length
応答本文が存在する場合、応答ヘッダーには応答本文の長さを識別する属性が含まれます。
知らせ: http は特殊記号 (\r\n) で区切られています。
コンテンツの最初の 3 つの部分は、通常、HTTP プロトコルによって提供されます。応答本文の最後の部分は省略できます (空の文字列)。リクエストがパッケージ化された後、これは次の層であるトランスポート層に直接配信され、トランスポート層によって処理されます。
4.3 質問
http リクエストとレスポンスがアプリケーション層で完全に読み取られるようにするにはどうすればよいでしょうか? ?
- まず、リクエストとレスポンスについては、行ごとに読み取ることができます (各行には が付いています
\r\n
)。 - すべての要求ヘッダーまたは応答ヘッダーが読み取られ、読み取りが完了したことを示す空行が読み取られるまで、ループを使用して完全な行(
while
分割用) を読み取ります。\r\n
- 次のステップはテキストを読むことですが、テキストが確実に読まれるようにするにはどうすればよいでしょうか? ? 本文中に特別な記号はありません
- リクエストまたはレスポンスのヘッダーが読み取られていることはすでに確認済みであり、ヘッダーにはフィールドが存在する必要があります:
Content-Length
。このフィールドは、レスポンス本文またはリクエスト本文の長さを識別するために使用されます。 - 解析の場合
Content-Length
、テキストの長さを取得します。これにより、読み取られたテキストが完全であることを確認でき、解析された長さに応じてテキストを直接読み取ることができます。
これにより、http リクエストとレスポンスがアプリケーション層で完全に読み取られることが保証されます。
http リクエストとレスポンスはどのようにシリアル化および逆シリアル化されますか? ?
- シリアル化と逆シリアル化は
http
特殊文字を使用して独自に実装されており\r\n
、第一行 + 请求/响应报头
特殊文字を 1 行ずつ読み込む限り、文字列全体を取得できます。 - 本体をシリアル化および逆シリアル化する必要はありません。必要に応じて、自分でカスタマイズしてください。
上記はhttp
プロトコルのマクロ理解であり、http
プロトコルを理解するために次のコードを記述します。
5、HTTPテストコード
5.1 HTTPリクエスト
単純な TCP サーバーを作成してみましょう。このサーバーが行う必要があるのは、ブラウザーによって送信された HTTP リクエストを出力することです。
httpサーバー.hpp
#pragma once
#include <iostream>
#include <string>
#include <functional>
#include <strings.h>
#include <unistd.h>
#include <pthread.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include "protocol.hpp"
static const int gbacklog = 5;
using func_t = std::function<bool(const httpRequest &req, httpResponse &resp)>;
// 错误类型枚举
enum
{
UAGE_ERR = 1,
SOCKET_ERR,
BIND_ERR,
LISTEN_ERR
};
// 业务处理
void handlerHttp(int sockfd, func_t func)
{
char buffer[4096];
httpRequest req;
httpResponse resp;
size_t n = recv(sockfd, buffer, sizeof(buffer) - 1, 0);
if (n > 0)
{
buffer[n] = 0;
req.inbuffer = buffer;
func(req, resp);
send(sockfd, resp.outbuffer.c_str(), resp.outbuffer.size(), 0);
}
}
class ThreadDate
{
public:
ThreadDate(int sockfd, func_t func) : _sockfd(sockfd), _func(func)
{
}
public:
int _sockfd;
func_t _func;
};
class httpServer
{
public:
httpServer(const uint16_t &port) : _listensock(-1), _port(port)
{
}
// 初始化服务器
void initServer()
{
// 1.创建套接字
_listensock = socket(AF_INET, SOCK_STREAM, 0);
if (_listensock == -1)
{
std::cout << "create socket error" << std::endl;
exit(SOCKET_ERR);
}
std::cout << "create socket success: " << _listensock << std::endl;
// 2.绑定端口
// 2.1 填充 sockaddr_in 结构体
struct sockaddr_in local;
bzero(&local, sizeof(local)); // 把 sockaddr_in结构体全部初始化为0
local.sin_family = AF_INET; // 未来通信采用的是网络通信
local.sin_port = htons(_port); // htons(_port)主机字节序转网络字节序
local.sin_addr.s_addr = INADDR_ANY; // INADDR_ANY 就是 0x00000000
// 2.2 绑定
int n = bind(_listensock, (struct sockaddr *)&local, sizeof(local)); // 需要强转,(struct sockaddr*)&local
if (n == -1)
{
std::cout << "bind socket error" << std::endl;
exit(BIND_ERR);
}
std::cout << "bind socket success" << std::endl;
// 3. 把_listensock套接字设置为监听状态
if (listen(_listensock, gbacklog) == -1)
{
std::cout << "listen socket error" << std::endl;
exit(LISTEN_ERR);
}
std::cout << "listen socket success" << std::endl;
}
// 启动服务器
void start(func_t func)
{
for (;;)
{
// 4. 获取新链接,accept从_listensock套接字里面获取新链接
struct sockaddr_in peer;
socklen_t len = sizeof(peer);
// 这里的sockfd才是真正为客户端请求服务
int sockfd = accept(_listensock, (struct sockaddr *)&peer, &len);
if (sockfd < 0) // 获取新链接失败,但不会影响服务端运行
{
std::cout << "accept error, next!" << std::endl;
continue;
}
std::cout << "accept a new line success, sockfd: " << sockfd << std::endl;
// 5. 为sockfd提供服务,即为客户端提供服务
// 多线程版
pthread_t tid;
ThreadDate *td = new ThreadDate(sockfd, func);
pthread_create(&tid, nullptr, threadRoutine, td);
}
}
static void *threadRoutine(void *args)
{
pthread_detach(pthread_self()); // 线程分离
ThreadDate *td = static_cast<ThreadDate *>(args);
handlerHttp(td->_sockfd, td->_func); // 业务处理
close(td->_sockfd); // 必须关闭,由新线程关闭
delete td;
return nullptr;
}
~httpServer()
{
}
private:
int _listensock; // listen套接字,不是用来数据通信的,是用来监听链接到来
uint16_t _port; // 端口号
};
httpサーバー.cc
#include "httpServer.hpp"
#include <memory>
// 使用手册
// ./httpServer port
static void Uage(std::string proc)
{
std::cout << "\nUage:\n\t" << proc << " local_port\n\n";
}
bool get(const httpRequest &req, httpResponse &resp)
{
std::cout << "----------------------http start----------------------" << std::endl;
std::cout << req.inbuffer;
std::cout << "----------------------http end ----------------------" << std::endl;
}
int main(int argc, char *argv[])
{
if (argc != 2)
{
Uage(argv[0]);
exit(UAGE_ERR);
}
uint16_t port = atoi(argv[1]); // string to int
std::unique_ptr<httpServer> tsvr(new httpServer(port));
tsvr->initServer(); // 初始化服务器
tsvr->start(get); // 启动服务器
return 0;
}
プロトコル.hpp
#pragma once
#include <iostream>
#include <string>
#include <vector>
class httpRequest
{
public:
std::string inbuffer;
};
class httpResponse
{
public:
std::string outbuffer;
};
サーバープログラムを実行し、ブラウザからアクセスすると、サーバーはブラウザからのHTTPリクエストを受信し、出力します。
コード内には何もないので、以下の情報のみが表示されます。
サーバーは、ブラウザのリクエスト 受信した HTTP リクエストを表示し、それらを出力します (一度しかアクセスされませんが、ブラウザの動作により複数の HTTP リクエストを受け取ります)
GET / HTTP/1.1
Host: 119.3.185.15:8080
Connection: keep-alive
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36 Edg/114.0.1823.67
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6
説明:
- ブラウザはリクエストを開始するときにデフォルトで HTTP プロトコルを使用するため、ブラウザの URL ボックスに URL を入力するときに、HTTP プロトコルを指定せずにサーバーのパブリック ネットワーク アドレスとポート番号を直接入力できます。
- 最初の行はステータス行です:これ
GET / HTTP/1.1
はGET
リクエストメソッドであり、ブラウザのデフォルトです。URL は です\
。特定のリクエストがないため、ブラウザは\
デフォルトで (Web ルートディレクトリ)にアクセスします。HTTP/1.1
HTTPのバージョン番号
残りはすべてリクエスト ヘッダーであり、name: value
行形式で表示されるさまざまなリクエスト属性です。
空行も出力されます。リクエスト本文がないため、デフォルトは空の文字列であり、情報は出力されません
クライアントによって表示されるホストのバージョン情報:
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36 Edg/114.0.1823.67
User-Agent
これは、リクエストを開始したクライアント ホストのバージョン情報を表示するためのものです。
たとえば、ダウンロードするものを検索すると、デフォルトで自分のオペレーティング システムに一致するダウンロードが表示されます。パソコン版をダウンロードしますか??
その理由は、リクエストを開始するときに、リクエストにはすでにオペレーティング システムのバージョン情報が含まれているためです。残り
は、クライアントが現在サポートしているもの (エンコード形式、テキストの種類など) をサーバーに伝えることです。
HTTP ヘッダーをペイロードから分離するにはどうすればよいですか?
- HTTP の場合、ステータス行と応答/リクエスト ヘッダーは HTTP ヘッダー情報であり、ここでの応答/リクエスト ボディは実際には HTTP ペイロードです。
- 空行が読み取られた場合は、ヘッダーが読み取られたことを意味し、空行は HTTP ヘッダーとペイロードを分離する鍵となります。
- つまり、http はヘッダーとペイロードを分離するために特殊な記号を使用します。
HTTP に対話型バージョンが必要なのはなぜですか?
- HTTP リクエストのリクエスト行と HTTP レスポンスのステータス行には、両方とも http バージョン情報が含まれています。。HTTP リクエストはクライアントによって送信されるため、HTTP リクエストはクライアントの http バージョンを示し、HTTP 応答はサーバーによって送信されるため、HTTP 応答はサーバーの http バージョンを示します
- クライアントとサーバーが通信するとき、主に互換性の問題のために、双方の http バージョンを交換します。サーバーとクライアントは異なる http バージョンを使用する可能性があるため、異なるバージョンのクライアントが対応するサービスを享受できるようにするには、通信当事者はバージョン ネゴシエーションを実行する必要があります。
- たとえば、バージョン 1.0 のアプリケーションが今日 2.0 にアップグレードされます (新機能は提供されますが、古いバージョンは提供されません)。アップグレードするユーザーもいるし、アップグレードしないユーザーもいます。このとき、次の問題が発生します。バージョンの違い。古いバージョンはサーバーにアクセスできますが、新しいバージョンのサーバーにはアクセスできません。古いバージョンには古いサーバーへのアクセスを許可する必要があります。このとき、双方のバージョン情報を交換する必要があるため、異なるバージョンのクライアントが接続できるようになります。対応したサービスをお楽しみいただけます。
- したがって、良好な互換性を確保するには、双方がバージョン情報を交換する必要があります。
5.2 HTTPレスポンス
ちょっとしたコードを追加するだけです。HTTP 応答を観察してみましょう。
bool get(const httpRequest &req, httpResponse &resp)
{
std::cout << "----------------------http request start----------------------" << std::endl;
std::cout << req.inbuffer;
std::cout << "+++++++++++++++++++++++++++++" << std::endl;
std::cout << "request method: " << req.method << std::endl;
std::cout << "request url: " << req.url << std::endl;
std::cout << "request httpversion: " << req.httpversion << std::endl;
std::cout << "request path: " << req.path << std::endl;
std::cout << "request file suffix: " << req.suffix << std::endl;
std::cout << "request body size: " << req.size << "字节" << std::endl;
std::cout << "----------------------http request end ----------------------" << std::endl;
std::cout << "----------------------http response start ----------------------" << std::endl;
std::string respline = "HTTP/1.1 200 OK\r\n"; // 响应状态行
std::string respheader = Util::suffixToDesc(req.suffix);
std::string respblank = "\r\n"; // 响应空行
std::string respbody; // 响应正文
respbody.resize(req.size);
if (!Util::readFile(req.path, (char *)respbody.c_str(), req.size)) // 访问资源不存在,打开404html
{
struct stat st;
stat(html_404.c_str(), &st);
respbody.resize(st.st_size);
Util::readFile(html_404, (char *)respbody.c_str(), st.st_size); // 一定成功
}
resp.outbuffer = respline;
respheader += "Content-Length: ";
respheader += std::to_string(respbody.size());
respheader += respblank;
resp.outbuffer += respheader;
resp.outbuffer += respblank;
std::cout << resp.outbuffer;
resp.outbuffer += respbody;
std::cout << "----------------------http response end ----------------------" << std::endl;
return true;
}
コードが多すぎると投稿できません。gitee リンク:リンク
操作の結果、サーバーは応答を返します (ブラウザがサーバーにアクセスすると、サーバーはindex.html
このファイルをブラウザに応答します。デフォルトのindex.html
ファイルは、訪問した Web サイトのホームページです)
と、応答情報の一部が出力されます。
ノート: ほんの一例として、HTTP レスポンスを構築するときに、レスポンス ヘッダーに追加される属性情報は 2 つだけであり、実際の HTTP レスポンス ヘッダーには多数の属性情報が含まれます。
6、HTTPメソッド
HTTP の一般的なメソッドは次のとおりです: (リクエスト内)
最も一般的に使用されるのは GET メソッドと POST メソッドです。
フロントエンド データとバックエンド データを操作する場合、本質的には、フロントエンドがform
フォームを介して送信し、ブラウザーがフォームのコンテンツをGET/POST
リクエストに自動的に変換するということです。
たとえば、フロントエンド フォーム送信ページ
action="/a/test.py"
フォームが指定されたパス ファイルに送信されることを意味します。method="GET"
これは、http アクセスを意味します。メソッドはGET
、サーバーを起動し、ブラウザにアクセスして
コンテンツを送信します。たとえば、Zhang San, 123123 は、
アクセスされたページが/a/test.py
存在しないため、サーバーが出力したリクエスト情報を表示する404
ページ(自分で設定)
GET
メソッドがパラメータを送信すると、パラメータの送信は URL の後ろに結合されます
/a/test.py?
前面はリクエストしたいリソースで、背面はxxxname=%E5%BC%A0%E4%B8%89&yyypwd=123123
フォームによって送信された情報です。ブラウザの URL バーにも送信されたコンテンツが表示されます。次の方法を試して、
送信フォームにアクセスするようにPOST
HTML
ブラウザを変更してください。
ブラウザの URL バーには表示されません 送信したコンテンツですが、アクセスしたリソースは表示されます
サーバーによって出力されたリクエスト情報を表示します
POST
このメソッドはフォーム情報を送信し、送信されたパラメータは http リクエストの本文に配置されます。
ブラウザの URL バーには、送信したコンテンツは表示されませんが、アクセスしたリソースは表示されます
概要:
GET/POST
http リクエストメソッドの違い
GET
メソッド送信パラメータは、URL を通じてパラメータを渡します。次に例を示します。http://ip:port/xxx/yyy?name=value&name2=value2...
POST
メソッド送信パラメータは、http リクエスト本文を通じてパラメータを送信します。POST
このメソッドはリクエスト本文を通じてパラメータを送信します。リクエスト本文は通常ユーザーには見えず、プライバシーに優れています。GET
メソッド送信パラメータは URL を通じて渡されるパラメータであり、誰でも見ることができます。GET
メソッドは URL を通じてパラメータを渡し、パラメータはあまり大きくならないように設定されていますが、POST
メソッドは本体を通じてパラメータを渡し、本体は非常に大きくなる可能性があります。
知らせ: プライバシー! = セキュリティ、HTTP セキュリティは良くない、他人に直接捕らえられる可能性がある
7、HTTPステータスコード
HTTP ステータス コードは次のとおりです。
ノート: 1xx は 1 で始まるステータス コードを表します。ステータス コードは 3 桁で構成されます。たとえば、404 は、次
のような最も一般的なステータス コードです。200(OK), 404(Not Found), 403(Forbidden), 302(Redirect, 重定向), 504(Bad Gateway)
リダイレクト (リダイレクト ステータス コード) について話しましょう
リダイレクトとは、さまざまなネットワークリクエストをさまざまな方法で他の場所にリダイレクトすることです。このとき、サーバーは誘導サービスを提供することに相当します。リダイレクトはクライアントによって行われ、サーバーはクライアントにリダイレクトするように指示します。一時的なリダイレクトに分けられ
ます
。ステータス コード 301 は永続的なリダイレクトを示し、ステータス コード 302 および 307 は一時的なリダイレクトを示します。
永久に移動、永久にリダイレクト
- 永続的とは、最初にアクセスしたリソースが完全に削除され、クライアントは新しい URI アクセスに従ってリダイレクトされる必要があることを意味します。
一時的なリダイレクト
- 一時的とは、最初に場所 URI を使用してアクセスされたリソースに一時的にアクセスできるが、古いリソースはまだ存在しており、次回アクセスするときにリダイレクトする必要がないことを意味します。
- 302 リダイレクトには URL ハイジャック (URL ハイジャック) が発生する可能性があります。たとえば、検索結果には URL A が表示されますが、使用されている Web ページのコンテンツは URL B のコンテンツです。この状況は URL ハイジャックと呼ばれます。
リダイレクトの詳細については、次の記事へのリンクを参照してください:リダイレクト
これは一時的なリダイレクトのデモです
- LocationフィールドはHTTPヘッダー内の属性情報で、リダイレクト先のWebサイトを示します。
HTTP 応答のステータス コードを 307 に変更し、対応するステータス コードの説明に従ってください。さらに、HTTP 応答ヘッダーに Location フィールドを追加する必要があります。この Location の後に、リダイレクト先の Web ページが続きます。ここにあるように、CSDN のホームページとして設定します。
このとき、ブラウザがサーバーにアクセスすると、すぐに CSDN のホームページにジャンプします。
サーバーは印刷情報を返します。
8、HTTP共通ヘッダー
一般的な HTTP ヘッダーは次のとおりです。
Content-Type
: データ型(text/htmlなど)Content-Length
: ボディの長さHost
: クライアントは、要求されたリソースがホストのどのポートにあるかをサーバーに通知します。User-Agent
: ユーザーのオペレーティング システムとブラウザのバージョン情報を宣言します。Referer
: 現在のページがどのページからリダイレクトされるかLocation
: 3xx ステータス コードとともに使用して、クライアントに次に訪問する場所を伝えます。Cookie
: クライアント側で少量の情報を保存するために使用され、通常はセッション機能の実装に使用されます。
ホスト
Host
このフィールドは、クライアントがアクセスしたいサービスの IP とポートを示します。たとえば、ブラウザが当社のサーバーにアクセスすると、ブラウザから送信される HTTP リクエストのホスト フィールドには、当社の IP とポートが入力されます。
ユーザーエージェント
前述したように、User-Agent
クライアントに対応するオペレーティングシステムやブラウザのバージョン情報を表します。
参照
Referer
現在どのページからジャンプしているかを表します。Referer
前のページを記録する利点は、ロールバックに便利である一方で、現在のページと前のページの相関関係を知ることができることです。
キープアライブ (長時間接続)
Keep-Alive
ロング接続とも呼ばれる、クライアントとサーバー間の永続的な接続を維持し、各リクエストの遅延とリソース消費を削減するために HTTP プロトコルで使用されるテクノロジーです。
従来の HTTP プロトコルでは、クライアントがリクエストを送信するたびに、サーバーはすぐに応答を返し、接続を閉じます。このような接続をショート接続と呼びます。長い接続とは、クライアントとサーバーの間で接続が確立された後、その接続を通じて複数のリクエストを送信し、複数の応答を受信できることです。
長い接続の利点は次のとおりです。
- 接続の確立と切断のオーバーヘッドを削減する: 短い接続では、各リクエストで接続を確立および切断する必要がありますが、長い接続では確立された接続を再利用できるため、これらのオーバーヘッドが削減されます。
- 遅延の削減: 短い接続では、リクエストごとに接続を再確立する必要がありますが、長い接続ではこの遅延を回避し、応答速度を向上させることができます。
- リソース消費の削減: 接続が短い場合は、リクエストごとに接続を再確立する必要があり、接続が長い場合はサーバー リソースの消費を削減できます。
ご了承ください: 長い接続は永続的ではありません。サーバーとクライアントの両方が積極的に接続を閉じることができます。HTTP
要求または応答ヘッダーのConnect
フィールドに対応する値は、Keep-Alive
長い接続がサポートされていることを意味します。
それについて詳しく話しましょうCookie和Session
ナイン、クッキー、セッション
HTTP は実際にはステートレス プロトコルであり、HTTP の各リクエスト/レスポンス間に関係はありませんが、ブラウザを使用するとそうではないことがわかります。
例えば、bilibiliなどのWebサイトにログインすると、一度ログインした後、長時間ログイン状態が残り、一度bilibiliのWebサイトを閉じて再度開くと、アカウントがログインしたままになっている場合があります。再度ログインする必要はありません。ブラウザも閉じてください
これは
cookie
とによってsession
実現されます。これはセッション永続性と呼ばれます。
知らせ: 厳密に言えば、セッション保持は http の本来の機能ではなく、後の使用後にセッション保持が必要であることがわかります。
http プロトコルはステートレスですが、ユーザーにはそれが必要です。ユーザーがWebページの操作を行う際には、新たなWebページを閲覧する必要があり、ページジャンプが発生すると、新たなページではどのユーザーであるかが識別できなくなり、再度ログインする必要があることは明らかです。したがって、ユーザーは
一度ログインすると、自分の ID に従って Web サイト全体にアクセスできますが、これにはセッションの永続性が必要です。
セッションの永続化 (古い方法)
- ユーザーが Web サイトにアクセスすると、Web サイトはユーザーにログインを促します。ユーザーがログインした後、クライアントのブラウザーはユーザーのアカウント番号とパスワードを保存します。将来、ユーザーが同じ Web サイトにアクセスする限り、ブラウザは保存された履歴を自動的にプッシュします。情報、認証
- ブラウザはアカウント番号とパスワードを保存します。この手法はと呼ばれます。
cookie
クッキー
Cookie
これはユーザーのブラウザに保存される小さなテキスト ファイルで、ユーザーの ID 認証情報や個人設定などを保存するために使用されます。ユーザーが Web サイトにアクセスすると、サーバーは何らかの情報を保存し、Cookie
今後のリクエストでCookie
その情報をサーバーに送信します。cookie
保存にはcookie文件
「保存」とcookie内存
「保存」の 2 つの方法があります。- ブラウザを閉じて再度開き、以前にログインしたウェブサイトにアクセスします。アカウント番号とパスワードを再入力する必要がある場合は、以前にログインしたときにブラウザに保存されたCookie情報がメモリレベルにあることを意味します。
- ブラウザを閉じるか、コンピュータを再起動して再度開き、以前にログインしたWebサイトにアクセスします。アカウントとパスワードを再入力する必要がない場合は、以前にログインしたときにブラウザに保存されたCookie情報が保存されていることを意味します。ファイルレベルです
これはcookie
ブラウザで管理でき、cookie
これらをすべて削除すると、すべての Web サイトで再度ログインする必要があります。Web
サイトでは、ログイン後、cookie
テストのために Web サイトを表示することもできますcookie
。削除後、Web サイトを削除します。ユーザーはログインしていません。再度ログインする必要があります
使用
cookie
上の問題
通常は問題ありませんが、
ユーザーの危険な操作によりウイルス、ワーム、トロイの木馬などに感染すると、ユーザー自身の情報がcookie
漏洩してしまいます。
- ワーム: ユーザー ホストを直接攻撃し (主に CPU、メモリなどを攻撃)、システム リソースの枯渇を引き起こすことを目的としています。
- トロイの木馬ウイルス: トロイの木馬は、古代の伝説に登場するトロイの木馬に似ており、敵の兵士を隠し、夜に出てきて敵を破壊します。トロイの木馬は、コンピュータの破壊を目的としたものではなく、一見通常のプログラムの中に隠されており、ハッカーと連携して内外と連携し、ユーザー情報を盗み、コンピュータを遠隔操作することを目的としています。ユーザーホストを悪意を持って攻撃する
cookie
悪意を持った者が入手すると、ハッカーは自分のブラウザからサーバーに直接アクセスできるようになり、サーバーはユーザーがサーバーにアクセスしていると誤って認識してしまいます(社会に多大な損害を与えます
)session
セッション
session
これは、ユーザー セッション情報を保存するためのサーバー側のストレージ テクノロジです。- ユーザーが初めて Web サイトにアクセスすると、サーバーはユーザーの一意の ID を作成し
Session ID
、その IDCookie
をブラウザーに保存してブラウザーに送信します。ブラウザは、後続のリクエストでこれをサーバーに自動的に送信しますSession ID
。サーバーは、Session ID
次に従って、対応するセッション情報を見つけます。 session
これはサーバー側に保存され、各ユーザーはサーバー上で一意の を 1 つ持ちますsession文件
(session ID
文字列)。- クライアント ブラウザはユーザーのアカウント パスワードを保存する必要はなく、
sessionID
単に保存するだけです。つまり、sessionID
パスワードcookie
を
SessionID
初めて Web サイトにログインし、アカウント番号とパスワードを入力すると、サーバー認証がSessionID
成功した後、サーバーは対応するものを生成し、
応答する際には、生成された SessionID 値がブラウザーに応答されます。ブラウザが応答を受信すると、Session ID
値が自動的に抽出され、ブラウザcookie
ファイルに保存されます。後でサーバーにアクセスすると、これは対応する HTTP リクエストに自動的に組み込まれますSession ID
。
- 現時点では、ユーザー情報の漏洩は大幅に改善されましたが、依然として問題はあります
- ハッカーがユーザーのセッション ファイルを盗みました。ハッカーはユーザーとしてサーバーにアクセスできるため、サーバーは不正なユーザーを正規のユーザーと誤認してしまい、解決できません。
- このとき、IPなどの何らかの戦略を使って
session ID
無効にし、パスワードを知っている人だけがログインできるようにし、再度ログインに成功することでsession ID
、session ID
盗難の問題をある程度軽減します(治りました)
安全性は相対的なものである
- セキュリティ上の懸念には実際には対応していませんが、このアプローチは比較的安全です。インターネットには絶対的なセキュリティという概念はなく、いかなるセキュリティも相対的なものであり、ネットワーク上に送信される情報を暗号化したとしても、他人に解読される可能性があります。
- セキュリティの分野には法則があります。情報をクラッキングするコストが、クラッキング後に得られる利益よりもはるかに大きい場合 (これを行うと損失が生じることを示します)、その情報は安全であると言えます。
以下を確認してください。クライアントは Cookie 情報を保持します。
- ブラウザがサーバーにアクセスするとき、サーバーからブラウザへの HTTP 応答にフィールドが含まれている場合、
Set-Cookie
ブラウザがサーバーに再度アクセスするときにこのcookie
情報が伝えられます。
j 上記のコードを変更するだけです。コードが多すぎる場合は、貼り付けずにリンクしてください:コード
サーバーの応答ヘッダーにフィールドを追加して、Set-Cookie
ブラウザが 2 回目に HTTP リクエストを開始するときにこのSet-Cookie
フィールドを表示するかどうかを確認します。
サーバーを実行した後、ブラウザを使用して当社のサーバーにアクセスします。cookie
値は当社によって設定されます1234567asdf
。この時点で、そのような Cookie はブラウザに書き込まれます。
クライアントの 2 番目のリクエストには既に Cookie 情報が含まれています
。その後、すべての http リクエストは、サーバーが認証動作を実行できるように設定されているすべての Cookie を自動的に送信します。これは http セッション保持の機能です。
推奨ツール:
postman: HTTP デバッグ ツール、ブラウザーの動作をシミュレートする
fiddler: パケット キャプチャ ツール、HTTP ツール
--------------------- END --------- -- -----------
「 作者 」 枫叶先生
「 更新 」 2023.7.11
「 声明 」 余之才疏学浅,故所撰文疏漏难免,
或有谬误或不准确之处,敬请读者批评指正。