【計算機網絡——應用層】http協議

文章目錄

  • 1. http協議
    • 1.1 http協議簡介
    • 1.2 url組成
    • 1.3 urlencode與urldecode
  • 2. http協議的格式
    • 2.1 http協議的格式
    • 2.2 一些細節問題
  • 3. http的方法、狀態碼和常見響應報頭
    • 3.1 http請求方法
    • 3.2 http狀態碼
    • 3.3 http常見的響應報頭屬性
  • 4. 一個非常簡單的http協議服務端
  • 5. http長鏈接
  • 6. http會話保持

1. http協議

1.1 http協議簡介

在上一篇文章中我們了解到應用層協議是可以由程序員自己定制的

計算機領域經過了這么長時間的發展,肯定會出現很多已經寫好的協議,我們直接拿來用就可以了的。事實也確實如此,http協議(超文本傳輸協議)就是其中之一。

這個協議是用于客戶端向服務端請求“資源”,包括文本、圖片、音頻、視頻等資源的協議。因為它不只能拿文本資源,所以叫超文本傳輸協議。

1.2 url組成

我們平常說的網址,其實就是URL,這個URL有很多個部分組成的

image-20240229152800115

在客戶端向服務端發起通信的時候,通過DNS將這個服務器地址轉換成IP地址,在其后面應該有端口號的,但是http協議的端口號固定就是80,https端口號固定是443,就能通過這個I P地址+端口號找到指定服務器的指定進程,然后通過對應的資源地址在web根目錄下找到對應的資源

1.3 urlencode與urldecode

對于像 / + : ?等字符, 已經被url特殊處理了。比如, 某個參數中需要帶有這些特殊字符, 就必須先對特殊字符進行轉義.

轉義的規則如下:取出字符的ASCII碼,轉成16進制,然后前面加上百分號即可。編碼成%XY格式。服務器收到url請求,將會對%XY進行解碼,該過程稱為decode

2. http協議的格式

2.1 http協議的格式

http協議的請求和響應都分為四個部分。對于請求,分為1. 請求行; 2. 請求報頭; 3. 一個空行; 4.請求正文;對于響應,分為1. 狀態行; 2.響應報頭; 3. 一個空行; 4. 響應正文

image-20240229155740289

其中在請求行,有三個部分內容,通過空格來區分,這三個部分分別是1. 請求方法; 2. url 3. http版本,這個版本現在有1.0;1.1;2.0

格式是http/版本號,例如http/1.1

2.2 一些細節問題

1. 請求和響應怎么保證讀取完了?

每次可以讀取完整的一行 ==> 循環讀取每一行,直到遇到空行 ==> 此時就讀取了所有的請求報頭和請求行 ==> 在請求報頭里面有一個屬性Content-Length表示正文長度,解析這個長度,然后按照指定長度讀取正文即可

2. 請求和響應是怎么做到序列化和反序列化的?

http不用關注json等序列化和反序列化工具,直接發送即可。服務器解析客戶端的請求,獲取其中的信息填充至響應緩沖區。服務器通過響應報頭的方式返回請求的參數,在響應正文中返回請求的資源。

3. http的方法、狀態碼和常見響應報頭

3.1 http請求方法

請求方法說明支持的http協議版本
GET獲取資源(表單在url中攜帶)1.0/1.1
POST傳輸實體主體(表單在請求正文中攜帶)1.0/1.1

其他方法不常用,這里就不列出來了

我們經常會在網頁填寫一些內容提交,如果使用GET方法的話,這些內容會被瀏覽器拼接到url后面(使用?作為分隔符),如果使用PSOT方法的話,這些內容就會在請求正文中

1、GET方法通過URL傳遞參數。例如http://ip:port/XXX/YY?key1=value1&key2=value2。像百度的搜索就是用的GET方法。GET方法通過url傳遞參數,參數注定不能太大,例如上傳視頻等巨長的二進制文件就不適合用GET了。

2、POST提交參數通過http請求正文提交參數。請求正文可以很大,可以提交視頻等巨長的文件。

3、POST方法提交參數,用戶是看不到的,私密性更高,而GET方法不私密。私密性不等于安全性,POST方法和GET方法其實都不安全!(http請求都是可以被抓到的,想要安全必須加密,使用https協議)

3.2 http狀態碼

http協議在響應的時候就會在狀態行給出本次請求的響應狀態,可以理解成是這個請求的“退出碼”。

一般來說,http的狀態碼分為5類

類別原因短語
1xxInformational(信息性狀態碼)接收的請求正在處理
2xxSuccess(成功狀態碼)接收的請求處理完畢
3xxRedirection(重定向狀態碼)需要進行附加操作以完成請求
4xxClinet Error(客戶端錯誤狀態碼)服務器無法完成請求
5xxServer Error(服務器錯誤狀態碼)服務器完成請求出錯

幾個比較常見的狀態碼, 比如 200(OK), 404(Not Found), 403(Forbidden), 302(Redirect, 重定向), 504(Bad Gateway)

3.3 http常見的響應報頭屬性

  • Content-Type: 響應正文的數據類型(text/html等)
  • Content-Length: 響應正文的長度
  • Host: 客戶端告知服務器, 所請求的資源是在哪個主機的哪個端口上;
  • User-Agent: 聲明用戶的操作系統和瀏覽器版本信息;
  • referer: 當前頁面是從哪個頁面跳轉過來的;
  • location: 搭配3xx狀態碼使用, 告訴客戶端接下來要去哪里訪問;
  • Cookie: 用于在客戶端存儲少量信息. 通常用于實現會話(session)的功能;

4. 一個非常簡單的http協議服務端

設計思路:我們日常使用的瀏覽器就是http協議的客戶端,我們現在只需要實現服務端即可。既然要實現支持http協議的服務端,那么只需要按照tcp協議的方式構建傳輸層,然后按照http協議的約定來解析客戶端發過來的消息,然后按照約定的響應格式發送數據給客戶端

那么其實我們之前實現的socket編程的代碼是可以用上的

enum
{USAGE_ERR = 1,SOCKET_ERR,BIND_ERR,LISTEN_ERR
};static const uint16_t gport = 8080;
static const int gbacklog = 5;typedef std::function<bool(const HttpRequest &req, HttpResponse &resp)> func_t;class HttpServer
{public:HttpServer(func_t func, const uint16_t &port = gport) : _port(port), _func(func){}void initServer(){// 1. 創建socket文件套接字對象_listensock = socket(AF_INET, SOCK_STREAM, 0);if (_listensock == -1){exit(SOCKET_ERR);}// 2.bind自己的網絡信息sockaddr_in local;local.sin_family = AF_INET;local.sin_port = htons(_port);local.sin_addr.s_addr = INADDR_ANY;int n = bind(_listensock, (struct sockaddr *)&local, sizeof local);if (n == -1){exit(BIND_ERR);}// 3. 設置socket為監聽狀態if (listen(_listensock, gbacklog) != 0) // listen 函數{exit(LISTEN_ERR);}}void start(){while (true){struct sockaddr_in peer;socklen_t len = sizeof peer;int sock = accept(_listensock, (struct sockaddr *)&peer, &len);if (sock < 0){continue;}pid_t id = fork();if (id == 0){close(_listensock);if (fork() > 0)exit(0);handleHttp(sock); // 這里就是需要服務端執行的內容了(傳輸層上層的內容)close(sock);exit(0);}waitpid(id, nullptr, 0);close(sock);}}void handleHttp(int sock) // 服務端調用{// 1. 讀到完整的http請求// 2. 反序列化// 3. 調用回調函數// 4. 將resp序列化// 5. sendchar buffer[4096];HttpRequest req;HttpResponse resp;ssize_t n = recv(sock, buffer, sizeof(buffer) - 1, 0);if(n > 0){buffer[n] = 0; // 添加一個字符串的結尾req.inbuffer = buffer;req.parse(); // 解析調用的內容_func(req, resp); // req -> respsend(sock, resp.outbuffer.c_str(), resp.outbuffer.size(), 0);}}~HttpServer() {}private:uint16_t _port;int _listensock;func_t _func;
};

在應用層我們就要設計我們服務端的”http協議了“

#pragma once#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>#include <string>
#include <sstream>
#include <iostream>#include "Util.hpp" // 這是工具類,提供了一些工具函數// 一些配置文件,這里寫死(可以集成為一個配置文件,在服務器啟動的時候加載)
const std::string sep = "\r\n"; // 分隔符
const std::string default_root = "./webroot"; // web根目錄
const std::string home_page = "index.html"; // 首頁
const std::string html_404 = "404.html"; // 找不到頁面顯示的頁面class HttpRequest // http請求類
{
public:HttpRequest(){}~HttpRequest(){}bool parse() // 解析{// 1. 提取inbuffer中的第一行內容std::string line = Util::getOneline(inbuffer, sep);if (line.empty())return false;// 2. 解析內容 method url httpversionstd::stringstream ss(line);ss >> method >> url >> httpversion;// 3. 添加默認路徑path += default_root;path += url;if(path[path.size() - 1] == '/') // 訪問不合法資源path += home_page;// 4. 獲取path對應的資源后綴(資源類型)auto pos = path.rfind(".");if(pos == std::string::npos)suffix = ".html";elsesuffix = path.substr(pos);// 5. 獲取的資源大小struct stat st;int n = stat(path.c_str(), &st);if(n != 0) stat((default_root + html_404).c_str(), &st);size = st.st_size;return true;}public:std::string inbuffer; // 緩沖區,保存接收到的所有內容std::string method;      // 瀏覽器請求方法std::string url;         // 相對于default_root的資源路徑std::string httpversion; // http協議版本std::string path;        // 要訪問的資源路徑std::string suffix;      // 資源后綴int size;                // 資源大小
};class HttpResponse // http響應類
{
public:std::string outbuffer; // 這里保存所有序列化之后的結果,最終發送這個outbuffer中的數據即可
};

同時我們需要設計一下服務端的回調函數

/*httpServer.cc*/
#include <memory>
#include <iostream>#include "httpServer.hpp"using namespace Server;
using namespace std;static void Usage(std::string proc)
{std::cout << "\n\tUsage: " << proc << " port\n";
}
static std::string suffixToDesc(const std::string &suffix)
{std::string ct = "Content-Type: ";if (suffix == ".html")ct += "text/html";else if (suffix == "jpg")ct += "application/x-jpg";elsect += "text/html";ct += "\r\n";return ct;
}
bool Get(const HttpRequest &req, HttpResponse &resp)
{cout << "-------------------http start-----------------------" << endl;cout << req.inbuffer << endl;cout << "method: " << req.method << endl;cout << "url: " << req.url << endl;cout << "httpversion: " << req.httpversion << endl;cout << "path: " << req.path << endl;cout << "suffix: " << req.suffix << endl;cout << "size: " << req.size << "字節" << endl;cout << "-------------------http end-----------------------" << endl;std::string respline = "HTTP/1.1 200 OK\r\n";      // 返回的第一行std::string respheader = suffixToDesc(req.suffix); // 協議報頭std::string respblank = "\r\n";std::string body;body.resize(req.size + 1);if (Util::readFile(req.path, const_cast<char *>(body.c_str()), req.size)){// 沒有指定資源Util::readFile(html_404, const_cast<char *>(body.c_str()), req.size); // 這個頁面一定存在}respheader += "Content-Length: ";respheader += std::to_string(body.size());respheader += "\r\n";resp.outbuffer += respline;resp.outbuffer += respheader;resp.outbuffer += respblank;cout << "-------------------http response start-----------------------" << endl;cout << resp.outbuffer << endl;cout << "-------------------http response end-----------------------" << endl;resp.outbuffer += body;return true;
}int main(int argc, char *argv[])
{if (argc != 2){Usage(argv[0]);exit(USAGE_ERR);}uint16_t port = atoi(argv[1]);std::unique_ptr<HttpServer> hsvr(new HttpServer(Get, port));hsvr->initServer();hsvr->start();return 0;
}

同時,這里附上工具類的函數

#pragma once#include <string>
#include <iostream>
#include <fstream>class Util
{
public:static std::string getOneline(std::string &buffer, const std::string &sep) // 獲取一行內容{auto pos = buffer.find(sep);if(pos == std::string::npos) return "";std::string sub = buffer.substr(0, pos);buffer.erase(0, pos + sep.size());return sub;}static bool readFile(const std::string &resource, char* buffer, int size) // 二進制方式讀取文件{std::ifstream in(resource, std::ios::binary);if(!in.is_open()) return false; // open file failin.read(buffer, size); // 從in中使用二進制讀取的方式讀取size個字節到buffer中in.close();return true;}
};

運行結果:

image-20240229174354837

image-20240229174430018

我們在服務端看到了響應結果,會發現客戶端的一次點擊在服務端會接收到多次請求,這是因為我們看到的網頁是由多個資源組合而成的,所以要獲取一個完整的網頁效果瀏覽器就需要發起多次http請求,包括我們要請求的index.html網頁和相關圖標等

一些小細節

  1. http協議之所以在首行存在httpversion是因為http請求會交換通信雙方B/S的協議版本,以明確能夠接收/傳輸的資源類型和支持的協議內容
  2. 如果沒有找到指定訪問的資源,webServer會有默認的首頁

5. http長鏈接

我們知道http請求是基于tcp協議的,tcp在通信的過程中需要發起并建立連接。一個網頁中可能存在很多個元素,也就是說瀏覽器在將一個網頁顯示給用戶的時候會經過多次http請求,所以就會面臨著tcp頻繁創建連接的問題

所以為了減少連接次數,需要客戶端和服務器均支持長鏈接,建立一條連接,傳輸一份大的資源通過一條連接完成。

在http的請求報頭中,可能會看到這樣一行內容

Connection: keep-alive

表示支持長鏈接

6. http會話保持

嚴格意義上來說,會話保持并不是http天然所具備的,而是在后面使用的時候發現需要的

我們知道,http協議是無狀態的,但是用戶需要。

首先,用戶查看新的網頁是常規操作,如果網頁發生跳轉,那么新的網頁是不知道已經登錄的用戶的身份的,也就需要用戶重新進行身份驗證。然后每次切換網頁都重新輸入賬號密碼著也太扯了,因此人們使用了一個辦法:將用戶輸入的賬號和密碼保存起來,往后只要訪問同一個網站,瀏覽器就會自動推送保存的信息,這個保存起來的東西就叫做cookie。cookie有內存級和文件級的,這里不做區分和了解。

舉個最簡單的例子:我們在登錄CSDN的時候,只需要一次登錄,以后再訪問CSDN相關的網頁,就會發現我們會自動登錄,這就是因為瀏覽器保存了我們的賬號信息,也就是當前網頁的cookie信息.

但是本地的Cookie如果被不法分子拿到,那就危險了,所以信息的保存是在服務器上完成的,服務器會對每個用戶創建一份獨有的sessionid,并將其返回給瀏覽器,瀏覽器存到Cookie的其實是session id。但這樣只能保證原始的賬號密碼不會被泄漏,黑客盜取了用戶的session id后仍可以非法登錄,只能靠服務端的安全策略保障安全,例如賬號被異地登錄了,服務端察覺后只要讓session id失效即可,這樣異地登錄將會使用戶重新驗證賬號密碼或手機或人臉信息(盡可能確保是本人),一定程度上保障了信息的安全。

服務端可以通過在報頭加上Set-Cookie: 屬性將對應的cookie返回給客戶端。往后,每次http請求都會自動攜帶曾經設置的所有Cookie,幫助服務器的鑒權行為————http會話保持

respHeader += "Set-Cookie: name=12345abcde; Max-Age=120\r\n";//設置Cookie響應報頭,有效期2分鐘

實際上在瀏覽器也是能看到對應的cookie的


本節完…

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/710816.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/710816.shtml
英文地址,請注明出處:http://en.pswp.cn/news/710816.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

【X806開發板試用】文章一 ubuntu開發環境搭建

一、環境配置 官方鏈接&#xff1a; 環境配置 1.安裝必要的庫和軟件 sudo apt-get install build-essential gcc g make zlib* libffi-dev e2fsprogs pkg-config flex bison perl bc openssl libssl-dev libelf-dev libc6-dev-amd64 binutils binutils-dev libdwarf-dev u-b…

pix2pix-zero

pix2pix-zero&#xff1a;零樣本圖像到圖像轉換 論文介紹 Zero-shot Image-to-Image Translation 關注微信公眾號: DeepGoAI 項目地址&#xff1a;https://github.com/pix2pixzero/pix2pix-zero 論文地址&#xff1a;https://arxiv.org/abs/2302.03027 本文介紹了一種名為…

Golang 函數中 defer 和 return 的調用順序

先來看一段代碼&#xff1a; package mainimport "fmt"func f() (ret int) {defer func() {ret}()return 1 } func main() {fmt.Println(f()) }上面這段代碼的輸出是2&#xff0c;不是1 原因在于&#xff1a; 1&#xff09;ret 是在執行 return 1 語句后發生的 2&…

基于SpringBoot多模塊項目引入其他模塊時@Autowired無法注入

基于SpringBoot多模塊項目引入其他模塊時Autowired無法注入 一、問題描述1、解決方案 一、問題描述 啟動Spring Boot項目時報 Could not autowire. No beans of ‘xxxxxxxx’ type found. 沒有找到bean的實例&#xff0c;即spring沒有實例化對象&#xff0c;也就無法根據配置文…

【LeetCode-中等】209.長度最小的子數組-雙指針/滑動窗口

力扣題目鏈接 1. 暴力解法 這道題的暴力解法是兩層嵌套for循環&#xff0c;第一層循環從 i 0 開始遍歷至數組末尾&#xff0c;第二層循環從 j i 開始遍歷至找到總和大于等于 target 的連續子數組&#xff0c;并將該連續子數組的長度與之前找到的子數組長度相比較&#xff0…

傳輸層兩大戰將TCP、UDP的定位

傳輸層 定義一些傳輸數據的協議和端口&#xff0c;傳輸協議同時進行流量控制&#xff0c;根據接收方的數據吞入熟讀&#xff0c;規定適當的發送速率&#xff0c;解決傳輸效率及能力問題 什么是TCP TCP/IP即傳輸控制/網絡協議&#xff0c;是面向連接的協議&#xff0c;發送數…

什么是IP公網?

IP公網是指互聯網上可以公開訪問的IP地址。它是經過互聯網服務提供商&#xff08;ISP&#xff09;向用戶提供的公共網絡IP地址。與之相對的是內網IP地址&#xff0c;內網IP地址一般是由路由器或交換機分配給連接在局域網中的設備使用。 IP公網的作用非常廣泛&#xff0c;可以應…

C#解析JSON

https://blog.csdn.net/weixin_43046974/article/details/131449900 C#解析JSON 1. JSON定義2. JSON一般構成及解析方法3. 解析舉例子 1. JSON對象解析&#xff0c;只包含一層對象{}2. 嵌套JSON對象解析&#xff0c;包含多層對象{}3. JSON數組解析1&#xff08;數組循環遍歷&…

Web APIs知識點講解(階段二)

DOM-事件基礎 一.事件 1.事件 目標&#xff1a;能夠給 DOM元素添加事件監聽 事件:事件是在編程時系統內發生的動作或者發生的事情&#xff0c;比如用戶在網頁上單擊一個按鈕 事件監聽:就是讓程序檢測是否有事件產生&#xff0c;一旦有事件觸發&#xff0c;就立即調用一個函…

http工具類

public class HttpRequstUtil {/*** http請求方法** param message 查詢條件* param url 查詢地址* param token 身份驗證token* param socketTimeout socket 響應時間* param connectTimeout 超時時間* return 返回字符串*/Deprecatedpublic stat…

金仕達與 DolphinDB 建立深度合作,共筑 FICC 科技創新新篇章

從“關起門做交易”到“打開門做服務”&#xff0c;國內 FICC 業務正經歷從自營到市場化服務的轉變&#xff0c;借助數據分析、算法交易等技術的快速發展&#xff0c;交易團隊能夠更加主動地發現市場需求&#xff0c;為不同客群提供更好的做市業務&#xff0c;FICC 交易電子化已…

打造智能汽車微服務系統平臺:架構的設計與實現

隨著智能汽車技術的飛速發展&#xff0c;微服務架構在汽車行業中的應用越來越廣泛。采用微服務架構可以使汽車系統更加靈活、可擴展&#xff0c;并且有利于快速推出新功能和服務。本文將從設計原則、關鍵技術、數據安全等方面&#xff0c;介紹如何搭建智能汽車微服務系統平臺架…

網絡通信技術

?1.分組交換技術 在網絡通信中&#xff0c;數據通過網絡節點的某種轉發方式&#xff0c;實現從一個端系統到另一個端系統之間的數據傳輸技術稱為數據交換技術。數據交換技術有電路交換、報文交換和分組交換&#xff0c;計算機網絡采用分組交換技術。 分組就是源主機(如服務器…

【Python】FastAPI 項目創建 與 Docker 部署

文章目錄 前言&需求描述1. 本地FastAPI1.1 Python 環境準備1.2 本地 Pycharm 創建FastAPI項目 2. Python FastAPI 部署2.1 服務器配置Python環境2.2.1 下載與配置Git、Pyenv等工具2.2.2 下載與配置Python 2.2 FastAPI 打包成鏡像2.2.1 項目準備所需環境文件2.2.2 編寫Docke…

畢業設計——基于springboot的聊天系統設計與實現(服務端 + 客戶端 + web端)

整個工程包含三個部分&#xff1a; 1、聊天服務器 聊天服務器的職責一句話解釋&#xff1a;負責接收所有用戶發送的消息&#xff0c;并將消息轉發給目標用戶。 聊天服務器沒有任何界面&#xff0c;但是卻是IM中最重要的角色&#xff0c;為表達敬意&#xff0c;必須要給它放個…

入侵和攻擊模擬 (BAS) 技術應用實踐

文章目錄 前言一、實施BAS的必要性二、實施BAS的關鍵步驟1、識別網絡中的脆弱區域2、創建基線安全模型3、選擇合適的BAS工具4、進行模擬攻擊測試5、分析結果并改進三、BAS實施中的挑戰1、組織的專業知識和能力有限2、改變傳統工作流程3、安全預算不足4、難以與現有安全基礎設施…

C語言中的不同變量初始值:深度解析與實踐指南

在C語言編程領域&#xff0c;理解和掌握變量的初始化原理和過程是構建穩健、高效代碼的基礎。C語言對不同類型變量的初始化處理方式存在差異&#xff0c;這要求開發者明確理解并合理應用這些規則以避免潛在的運行時錯誤和未定義行為。本文將詳細解讀C語言中各類變量的初始狀態設…

AI智能分析網關V4車輛違停算法在園區場景中的應用及特點

隨著城市化進程的加速&#xff0c;車輛違停問題愈發嚴重&#xff0c;給城市交通帶來了極大的困擾。為了解決這一問題&#xff0c;AI技術逐漸被應用于車輛違停的檢測中。AI檢測算法在車輛違停方面的應用&#xff0c;主要是通過計算機視覺技術&#xff0c;對道路上的車輛進行實時…

智慧灌區項目案例(甘肅省蘭州市某重點灌區)

?甘肅省蘭州市某重點灌區自上個世紀80年代建成后,灌溉面積達到30萬畝,對推動當地農業發展發揮了重要作用。但長期以來,該灌區的水利管理仍主要依靠人工統計記錄,缺乏實時監測和精細化管理。為實現灌區管理的現代化升級,甘肅水利局委托星創易聯公司設計實施水利信息化項目。 項…

【Python筆記-設計模式】狀態模式

一、說明 狀態模式是一種行為設計模式&#xff0c;用于解決對象在不同狀態下具有不同行為 (一) 解決問題 在對象行為根據對象狀態而改變時&#xff0c;規避使用大量的條件語句來判斷對象的狀態&#xff0c;提高系統可維護性 (二) 使用場景 當對象的行為取決于其狀態&#…