web開發中,必需要了解的HTTP相關知識

本文已同步到github, web開發中,必需要了解的HTTP相關知識,歡迎收藏,歡迎star。javascript

本文主要記錄與HTTP相關的具體概念和知識,關於HTTP協議的誕生和歷史發展,很少作介紹,可是既然是寫HTTP,順帶說兩句,上下文也能銜接的上。html

CERN(歐洲核子研究組織)的蒂姆 • 伯納斯 - 李(Tim Berners - Lee)博士提出了一種能讓遠隔兩地的研究者們共享知識的設想,因而HTTP慢慢的誕生了。java

另外,HTTP協議是無狀態協議,因而爲了保存用戶的狀態,cookie誕生了。node

HTTP協議是創建在TCP鏈接之上的,當瀏覽器輸入URL進行訪問,瀏覽器衝URL中解析出主機名和端口,瀏覽器創建一條與web服務器的鏈接,而後才進行http請求。git

TCP鏈接的創建與終止

創建TCP鏈接(三次握手)

在客戶端與服務端進行http通訊以前,須要創建TCP鏈接,這時須要三次握手github

(1) 請求新的TCP鏈接,客戶端發送一個小的TCP分組,這個分組設置一個特殊的SYN標記,代表是一個客戶端請求。web

(2) 若是服務器接受這個鏈接,就會對一些鏈接參數進行計算,並向客戶端回送一個TCP分組,發送SY和ACK標記,代表鏈接請求已經被接受跨域

(3) 最後,客戶端向服務器回送一條確認消息,通知服務器鏈接已經創建。瀏覽器

HTTP-三次握手

斷開TCP鏈接(四次斷開)

創建一個鏈接須要三次握手,而終止一個鏈接要通過4次握手。這由TCP的半關閉(half-close)形成的。既然一個TCP鏈接是全雙工(即數據在兩個方向上能同時傳遞),所以每一個方
向必須單獨地進行關閉。這原則就是當一方完成它的數據發送任務後就能發送一個FIN來終止
這個方向鏈接。當一端收到一個FIN,它必須通知應用層另外一端幾經終止了那個方向的數據傳
送。發送FIN一般是應用層進行關閉的結果。

(1) 客戶端發送FIN標記到服務器,代表客戶端發起關閉鏈接緩存

(2) 服務器接收客戶端的FIN標記並,向客戶端發送FIN的ACK確認標記

(3) 服務器發送FIN到客戶端,服務器關閉鏈接

(4) 服務器端發送一個FIN的ACK確認標記,確認鏈接關閉

HTTP-四次分手

創建持久鏈接的請求和響應交互:

HTTP-持久鏈接的請求和響應交互

使用wireshark進行數據抓包:

這裏向你們推薦一款抓包軟件Wireshark,能夠用來分析TCP鏈接的創建和斷開過程,以及抓取HTTP請求和相應的信息等,下面是我進行一次客戶端和服務端通訊的抓包數據截圖:

HTTP-TCP鏈接的創建與斷開

HTTP報文

HTTP協議報文是應用程序之間發送的數據塊,也就是客戶端和服務端用於交互的信息。客戶端的報文叫作請求報文,服務器端的報文叫作響應報文。

HTTP報文組成

HTTP報文由起始行、首部和實體的主體(也稱報文主體或主體)組成。起始行和首部以一個回車符和換行符做爲結束,主體部分能夠是二進制數據,也能夠爲空。

HTTP報文組成

1. 起始行

請求報文起始行:

請求報文起始行說明了要作什麼,由請求方法 、請求URI和協議版本構成。

GET /index.html HTTP/1.1

響應報文起始行:

響應報文的起始行,由協議版本、狀態碼和緣由短語構成。

HTTP/1.1 200 OK   // OK就是緣由短語

HTTP-請求-響應報文

2. 首部

首部字段分類

  • 1.通用首部

    客戶端和服務端均可以使用的首部

    通用首部字段表:

    通用首部字段

  • 2.請求首部

    請求報文特有的首部,爲服務器提供了一些額外的信息,補充了請求的附加內容、客戶端信息、響應內容相關的優先級等信息。

    請求首部字段
    請求首部字段

  • 3.響應首部

    響應報文特有的字段

    響應首部字段表:

    響應首部字段

  • 4.實體首部

    用於針對請求報文和響應報文主體部分使用的首部

    實體首部字段

  • 5.擴展首部

    擴展首部是非標準的首部,由應用程序開發者建立,但還未添加到已批准的HTTP標準中去。

http狀態碼

狀態碼的職責是當客戶端向服務器端發送請求時,描述返回的請求結果。藉助狀態碼,用戶能夠知道服務器端是正常處理了請求,仍是出現了錯誤。

狀態碼分類:

狀態碼區間 類別
100~199 信息性狀態碼
200~299 成功狀態碼
300~399 重定向狀態碼
400~499 客戶端錯誤狀態碼
500~599 服務器錯誤狀態碼

經常使用狀態碼列表:

狀態碼 緣由短語 含義
200 OK 表示從客戶端發來的請求在服務器端被正常處理了
204 No Content 該狀態碼錶明服務器接收的請求已成功處理,但在返回的響應報文中不含實體的主體部分。另外,也不容許返回任何實體的主體。
301 Moved Permanently 永久重定向,該狀態碼錶示請求的資源已被分配了新的 URI,之後應使用資源如今所指的 URI
302 Found 臨時性重定向,該狀態碼錶示請求的資源已被分配了新的 URI,但願用戶(本次)能使用新的 URI 訪問
303 See Other 303 狀態碼和 302 Found 狀態碼有着相同的功能,但 303 狀態碼明確表示客戶端應當採用 GET 方法獲取資源,這點與 302 狀態碼有區別
304 Not Modified 緩存
307 Temporary Redirect 臨時重定向,和302同樣
400 Bad Request 該狀態碼錶示請求報文中存在語法錯誤。當錯誤發生時,需修改請求的內容後再次發送請求。另外,瀏覽器會像 200 OK 同樣對待該狀態碼
401 Unauthorized 該狀態碼錶示發送的請求須要有經過 HTTP 認證(BASIC 認證、DIGEST 認證)的認證信息
403 Forbidden 該狀態碼代表對請求資源的訪問被服務器拒絕了
404 Not Found 該狀態碼代表服務器上沒法找到請求的資源
500 Internal Server Error 該狀態碼代表服務器端在執行請求時發生了錯誤。也有多是 Web應用存在的 bug 或某些臨時的故障
502 Bad Gateway 網關錯誤
503 Service Unavailable 該狀態碼代表服務器暫時處於超負載或正在進行停機維護,如今沒法處理請求。若是事先得知解除以上情況須要的時間,最好寫入RetryAfter 首部字段再返回給客戶端

HTTP中不一樣場景下,首部字段的做用

1. CORS 跨域資源共享

跨域資源共享( CORS) 是一種機制,它使用額外的 HTTP 頭來告訴瀏覽器 讓運行在一個 origin (domain) 上的Web應用被准許訪問來自不一樣源服務器上的指定的資源。當一個資源從與該資源自己所在的服務器不一樣的域、協議或端口請求一個資源時,資源會發起一個跨域 HTTP 請求。 --MDN

下面使用nodejs來搭建一個簡單的服務器,來介紹一個跨域問題的解決方法

// index.html

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <title>CORS</title>
</head>
<body>
    Hello World
<script>
    fetch('http://127.0.0.1:8081')
</script>
</body>
</html>
// server.js

const http = require('http')

http.createServer(function(req, res) {
    res.writeHead('200', {   
        'Access-Control-Allow-Origin': 'http://localhost:8082'
    })
}).listen(8081)

在源地址爲 http://localhost:8082 下,請求http://localhost:8081,是跨域請求,瀏覽器會自動在request Header中發送Origin首部字段,並把值設置爲來自哪一個源,本例爲http://localhost:8082。服務器須要在響應頭中設置Access-Control-Allow-Origin,來告知瀏覽器能夠處理返回的數據。若是響應頭中不設置Access-Control-Allow-Origin則會報錯,可是返回狀態碼爲200,跨域其實是瀏覽器自己的一個安全機制。

http-跨域-報錯

http-跨域-狀態碼200

// server2.js
// 啓動8082端口服務,在瀏覽器中訪問http://127.0.0.1:8082,會返回index.html內容

const http = require('http')
const fs = require('fs')
http.createServer(function(req, res) {
    var page = fs.readFileSync('index.html', 'utf-8')
    res.writeHead(200, {
        'Content-Type': 'text/html'
    })
    res.end(page)
}).listen(8082)

關於CORS跨域請求的分類:

1.簡單請求:

須要同時知足如下的條件就是簡單請求

(1)請求方法:

GET、POST、HEAD

(2)請求頭不能爲如下其餘字段以外

Accept
Accept-Language
Content-Language
Content-Type的值必須爲application/x-www-form-urlencoded、multipart/form-data、text/plain之一

2.非簡單請求:

非簡單請求是當請求信息不知足簡單請求的條件,瀏覽器就發送方法爲OPTIONS的預請求,包含本身請求的方法及須要使用的請求頭字段,在獲得服務器響應容許以後,瀏覽器會按照想要使用的請求方法及頭信息再發一次請求。

如今修改如下上面的例子:

// index.html

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <title>CORS</title>
</head>
<body>
    Hello World
<script>
    fetch('http://127.0.0.1:8081', {
        method: 'PUT',
        headers: {
            X-Coustom-Head: 'abc'
        }
    })
</script>
</body>
</html>
// server.js

const http = require('http')

http.createServer(function(req, res) {
    res.writeHead('200', {   
        'Access-Control-Allow-Origin': 'http://localhost:8082'
    })
}).listen(8081)

若是服務端不進行相應的設置告訴瀏覽器容許跨域訪問則會報錯

HTTP-cors-methodError

可是預請求返回狀態碼爲200
HTTP-cors-options-狀態碼200

// server2.js
// 啓動8082端口服務,在瀏覽器中訪問http://127.0.0.1:8082,會返回index.html內容

const http = require('http')
const fs = require('fs')
http.createServer(function(req, res) {
    var page = fs.readFileSync('index.html', 'utf-8')
    res.writeHead(200, {
        'Content-Type': 'text/html'
    })
    res.end(page)
}).listen(8082)

如今咱們修改如下 server.js

// server.js

const http = require('http')

http.createServer(function(req, res) {
    res.writeHead('200', {   
        'Access-Control-Allow-Origin': 'http://localhost:8082',
        'Access-Control-Allow-Headers': 'X-Coustom-Head',
        'Access-Control-Allow-Methods': 'PUT'
    })
}).listen(8081)

從新啓動node服務,訪問http://locaohost:8082,能夠看到在發送預請求後,瀏覽器會繼續發送PUT請求

HTTP-cors-options

HTTP-cors-allow.png

關於CORS的其餘設置這裏就很少作介紹了,這裏主要是用一個例子來講明如下http不一樣字段在跨域場景下的做用。

2. 緩存 (Cache-Control的做用)

本例依舊用node服務來說解一下Cache-Control的做用,新建三個文件

// index.html
<!DOCTYPE html>
<html>

<head>
    <meta charset="utf-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <title>Cache-Control</title>
    <meta name="viewport" content="width=device-width, initial-scale=1">
</head>

<body>
    <script src="/script.js"></script>
</body>

</html>
// script.js
console.log('script.js')
// server.js
const http = require('http')
const fs = require('fs')


http.createServer(function(req, res) {
    if (req.url === '/') {
        let page = fs.readFileSync('index2.html', 'utf-8')
        res.writeHead(200, {
            'Content-Type': 'text/html'
        })
        res.end(page)
    }

    if (req.url === '/script.js') {
        let page = fs.readFileSync('script.js', 'utf-8')
        res.writeHead(200, {
            'Content-Type': 'text/javascript',
            'Cache-Control': 'max-age=10'
        })
        res.end(page)
    }
}).listen(8082)

在第一次請求script.js資源時,向服務器發送請求

HTTP-cache-control-1
HTTP-cache-control-3

因爲服務器返回響應時,設置Cache-Control: 'max-age=10'時,修改script.js後,在10秒內繼續請求script.js資源,則從緩存中讀取,而打印信息依舊是'script.js'

// script.js
console.log('script-modify.js')

HTTP-cache-control-1
HTTP-cache-control-3

更多關於緩存的知識在這裏也很少介紹了,貼兩張cache-control字段在請求和響應時能夠設置的值和其表示含義:

1. Cache-Control 緩存請求指令:

HTTP-cache-control-緩存請求指令

2. Cache-Control 緩存響應指令:

HTTP-cache-control-緩存響應指令

3. cookie

指某些網站爲了辨別用戶身份、進行 session 跟蹤而儲存在用戶本地終端上的數據(一般通過加密),當下次再訪問時瀏覽器會將該網站的cookie發回給服務器端。

cookie若是不設置過時時間,隨瀏覽器關閉而失效,若是有須要能夠設置過時時間,繼續上代碼例子🌰,新建兩個文件以下

// index.html

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <title>Cookie</title>
</head>
<body>
    Cookie
<script>
    console.log(document.cookie)
</script>
</body>
</html>
// server.js

const http = require('http')
const fs = require('fs')

http.createServer(function(req, res) {
    if (req.url === '/') {
        let page = fs.readFileSync('index.html', 'utf-8')
        res.writeHead(200, {
            'Content-Type': 'text/html',
            'Set-Cookie': ['a=1;max-age:5', 'b=2;HTTPOnly']
        })
        res.end(page)
    }
}).listen(8082)

啓動node服務,訪問localhost:8082,能夠看到成功設置了cookie

HTTP-cookie-application

並在響應頭信息中設置了Set-Cookie字段

HTTP-cookie-init

另外關注如下打印信息,發現只有a=1,由於給b=2設置了HttpOnly屬性,不容許JavaScript經過腳原本獲取到cookie信息

HTTP-cookie-console

因爲當再次請求時,cookie會在請求頭中發送到服務器,因爲cookie a=1設置了5秒後過時,在5秒後刷新頁面,請求頭中的cookie只有a=1

    HTTP-cookie-againRequest

在5秒內發送二次請求,cookie a=1沒有失效,在請求頭中cookie a=1;b=2都會發送到服務器

    HTTP-cookie-max-age

另外對於cookie的其餘設置如expires、domain等在這裏也很少作介紹了

4. 重定向

當服務端返回30一、30二、307等狀態碼都表明資源已經被重定向到其餘位置,301表示永久改變URI,302和307表示臨時重定向到某個URI

本例舉一個服務器返回302狀態碼的例子,直接上代碼:

// server.js

const http = require('http');
const fs = require('fs')

http.createServer((req, res) => {
    if (req.url === '/') {
        res.writeHead(302, {
            'Location': '/redirect'
        })
        res.end()
    }

    if (req.url === '/redirect') {
        res.end('redirect')
    }
}).listen(8082);

訪問localhost:8082, 服務器返回302狀態碼時,在相應頭中設置Location首部字段,瀏覽器會繼續發送請求到重定向的地址

HTTP-重定向-302-1

HTTP-重定向-302-2

HTTP與HTTPS的區別

首先說一下什麼是HTTPS

HTTPS(全稱:Hyper Text Transfer Protocol over Secure Socket Layer 或 Hypertext Transfer Protocol Secure,超文本傳輸安全協議),是以安全爲目標的HTTP通道,簡單講是HTTP的安全版。即HTTP下加入SSL層,HTTPS的安全基礎是SSL,所以加密的詳細內容就須要SSL。 --百度百科

HTTPS = HTTP+ 加密 + 認證 + 完整性保護

最主要是在應用層和傳輸層中間加了一個SSL(安全套階層),一般,HTTP 直接和 TCP 通訊。當使用 SSL 時,則演變成先和 SSL 通訊,再由 SSL 和 TCP 通訊。

HTTP-http與https對比

HTTP與HTTPS的區別:

  • (1) HTTP是明文傳輸,HTTPS是通過SSL加密後進行傳輸,只有客戶端和服務端根據公鑰和私鑰進行加密和解密能看到,中間任何傳輸環節沒法獲取傳輸信息,因此HTTPS比HTTP安全
  • (2) HTTPS須要到數字證書認證機構進行購買
  • (3) HTTP服務器默認端口是80,HTTPS服務器默認端口是443

本文主要介紹HTTP,關於HTTPS主要就介紹這麼多吧。

HTTP2

本想說點HTTP2的知識,奈何本身是小白,放個百度百科的連接吧 HTTP2

等後續隨着不斷的學習,再回來更新本文。

另外放一個HTTP1.1與HTTP2請求與相應對比的demo的連接HTTP/2 is the future of the Web, and it is here!

最後,本文主要介紹了一些HTTP在web開發中的基礎知識,關於概念和圖解流程的截圖基本上都是來自《TCP/IP詳解 卷1:協議》《圖解HTTP》《HTTP權威指南》,可放心參考。筆者功力實在有限,若有問題,請你們多多指出,相互學習和進步,也但願經過個人學習與實踐過程,整理出的筆記能對你們有所幫助,謝謝。

本文參考連接:

TCP/IP詳解 卷1:協議
圖解HTTP
HTTP權威指南
跨域資源共享 CORS 詳解--阮一峯

相關文章
相關標籤/搜索