一、HTTP定義
HTTP是Hyper Text Transfer Protocol 超文本傳輸協議 的縮寫。用於從WWW服務器傳輸超文本到本地瀏覽器的傳送協議,是一個無狀態的協議。

通常承載於TCP協議之上,有時也承載於TLS或SSL協議層之上,這個時候,就成了我們常說的HTTPS。
默認HTTP的端口號為80,HTTPS的端口號為443。
一般情況:
- HTTP協議永遠都是客戶端發起請求,服務器回送響應,客戶端將內容拿到之后就和服務器斷開來連接。這就決定了,如果客戶端沒有發起請求,服務器是沒法推送數據給客戶端的。
- 另外,因為是無狀態的協議,所以上一次的連接和下一次的連接沒有關系。
問題1:為什么說http協議是無狀態的,影響?
Http協議本身就是一個 傳輸 協議,理解這個意思,更加廣義一些就是支持資源的傳輸,那么在客戶端瀏覽器向HTTP服務器發送請求,繼而HTTP服務器將相應的資源發回給客戶端這樣一個過程中因為每一次請求和響應都是相對獨立的。但是隨着時間推移,HTML的語法在不斷膨脹,其中最重要的是增加了表單,就有了很多帶着數據的請求,有來有回的這么些動態交互的程序,HTTP無狀態的特性嚴重阻礙了這些應用程序的實現,畢竟交互是需要承前啟后的,簡單的購物車程序也要知道用戶到底在之前選擇了什么商品。於是,兩種用於保持HTTP連接狀態的技術就應運而生了,一個是Cookie,而另一個則是Session。
問題2:http和TCP的區別,如何基於tcp實現http呢?
區別:
從上面的圖中不難看出區別,TCP協議對應於運輸層,而HTTP協議對應於應用層,從本質上來說,二者沒有可比性。但是可以看到,http是基於tcp實現的,那么既然tcp是可靠的長連接,為什么http還存在短連接這一說呢?答案是,Http就是在每次請求完成后就把TCP連接關了,所以是短連接。而如果直接通過Socket編程使用TCP協議的時候,通過代碼區控制什么時候打開連接、什么時候關閉,只要我們不通過代碼把連接關閉,這個連接就會在客戶端和服務端的進程中一直存在。
如何實現:
可以看到,其實http就是在tcp的傳輸之上加了一些內容,我們模擬這些思路就可以。
設定一個規則之后,開始處理:
1、實現兩個主機不同進程之間的TCP通信。
2、分析請求方法(比如GET和POST方法)。
3、方法確定后,應該拿到請求的URL,
4、判斷資源是否存在,如果存在,GET方法:如果沒有參數,就直接將請求的資源返回(即進入非cgi模式運行);否則,進行對應的處理。
二、http報文格式
瀏覽器輸入baidu.com,可以檢查看到發出的請求報文頭部格式

2.1 客戶端請求消息格式
客戶端發送一個HTTP請求到服務器的請求消息包括以下格式:
- 請求行(request line)
- 請求頭部(header)
- 空行
- 請求數據

四個部分組成。
- 請求行 包括請求方法(常用的有GET和POST),請求的URL,和協議版本(例如HTTP1.1);
- 請求頭部 的格式就是字段名+值組成,一般有:
- User-Agent(用戶代理):產生請求的瀏覽器類型
- Accept:客戶端希望接受的數據類型,比如text/xml;
- Content-Type:發送端發送的實體數據的類型,比如text/html;
- Host:請求的主機名,允許多個域名同處一個IP地址,即虛擬主機。
- 空行;
- 請求體(數據):GET沒有請求數據,POST有。
其中,User-Agent用戶代理,User-Agent會告訴網站服務器,訪問者是通過什么工具來請求的,如果是爬蟲請求,一般會拒絕,如果是用戶瀏覽器,就會應答,另外服務器可以通過判斷User-Agent來給不同的操作系統、不同的瀏覽器發送不同的頁面。
他的格式一般是:Mozilla/5.0+(平台)+引擎版本+瀏覽器版本號。
更詳細的字段解釋可以看看這篇博客:https://www.cnblogs.com/xy51/p/12974945.html
比如上面我截圖的部分:

- 向百度首頁發送的請求,請求方法是get,協議版本是1.1,請求的URL沒有寫;
- 請求頭部里面的User-Agent就說明了,平台是Windows、引擎版本就是從AppleWebKit到Safari那么長一段,瀏覽器版本號就是Chrome后面跟的那一串;
- Content-Type是text/html(谷歌瀏覽器的檢查里沒有顯示這個);
- Accept就是很多數據類型;
- Host就是百度官網的域名。
2.2 服務端響應報文格式

HTTP響應報文和請求報文的結構類似,也是由四個部分組成:狀態行、消息報頭、空行、響應體。
- 狀態行也由三部分組成:服務器HTTP協議版本,響應狀態碼,狀態碼的文本描述;
- 消息報頭:Date時間,Content-Type,Content-Encoding編碼格式,Content-Length內容長度等等,和請求報文的請求頭部類似,也是字段+值的形式;
- 空行;
- 響應體:響應體就是對應的html加上別的各類資源數據的內容了。
三、HTTP請求方法類型
上面我們提到,http請求報文的請求行里會先寫上請求方法。
HTTP 協議中共定義了八種方法或者叫“動作”:
- OPTIONS:返回服務器針對特定資源所支持的HTTP請求方法。也可以利用向Web服務器發送 '*' 的請求來測試服務器的功能性。
- HEAD:向服務器索要與GET請求相一致的響應,只不過響應體將不會被返回。這一方法可以在不必傳輸整個響應內容的情況下,就可以獲取包含在響應消息頭中的元信息。
- GET:向特定的資源發出請求。
- POST:向指定資源提交數據進行處理請求(例如提交表單或者上傳文件)。數據被包含在請求體中。POST請求可能會導致新的資源的創建和/或已有資源的修改。
- PUT:向指定資源位置上傳其最新內容。
- DELETE:請求服務器刪除 Request-URI 所標識的資源。
- TRACE:回顯服務器收到的請求,主要用於測試或診斷。
- CONNECT:HTTP/1.1 協議中預留給能夠將連接改為管道方式的代理服務器。
其中:OPTIONS, PUT, DELETE, TRACE, CONNECT這些方法都是http1.1新增的,在http1.0是沒有的雖然 HTTP 的請求方式有 8 種,但是我們在實際應用中常用的也就是 get 和 post,其他請求方式也都可以通過這兩種方式間接的來實現。
問題:常用的GET和POST請求的區別是什么?
GET和POST本質上就是TCP鏈接,並無差別。但是由於HTTP的規定和瀏覽器/服務器的限制,導致他們在應用過程中體現出一些不同。
- 請求次數不同(因而POST比GET更慢):
- GET產生一個TCP數據包;
- POST產生兩個TCP數據包。
- 對於GET方式的請求,瀏覽器會把http header和data一並發送出去,服務器響應200(返回數據);而對於POST,瀏覽器先發送header,服務器響應100 continue,瀏覽器再發送data,服務器響應200 ok(返回數據)。
- 參數編碼不同:
- GET僅支持URL編碼,參數需要編碼和解碼;
- POST支持多種編碼方式。
- 報文不同(因而POST能發送的數據更大):
- GET的參數放在了URL里面;
- POST參數在請求體中。
- 從這個層面來說,get請求更不安全,因為他把參數放在了url里(有了ssl,http變成https,就解決了這個問題)
- 緩存:
- GET會被瀏覽器緩存;
- POST不會。
- 以上幾點帶來的應用場景不同:
- post的目的是修改和寫入數據;
- get一般用於搜索排序和篩選之類的操作(淘寶,支付寶的搜索查詢都是get提交),目的是資源的獲取,讀取數據。
- 從5的層面總結來說,GET符合冪等性和安全性,POST不符合。
- 冪等性:對數據庫的一次操作和多次操作獲得的結果是一致的。
- GET操作是查詢操作,不會改變數據庫中原有的數據,只是類似於數據庫的查詢,大致認為符合冪等性和安全性。
- POST會往數據庫中提交數據,會改變數據庫中的數據;POST請求每次獲得的結果都有可能不一樣,因為POST作用在上一級的URL,每一次請求都會添加一份新資源,所以既不冪等也不安全。
注意事項:
- http協議從未規定get/post的請求長度限制是多少,所謂的請求長度限制是由瀏覽器和web服務器決定和設置的。
- 並不是所有瀏覽器都會在POST中發送兩次包,Firefox就只發送一次。
四、HTTP狀態碼常見的狀態碼:
- 200 - 請求成功
- 301 - 資源(網頁等)被永久轉移到其它URL
- 404NotFound - 請求的資源(網頁等)不存在
- 500 - 內部服務器錯誤
- 400 Bad Request - 客戶端請求有語法錯誤,不能被服務器所理解。此時需要分析客戶端的代碼,去看看請求為什么出現服務器無法理解的錯誤。
http的狀態碼三個十進制數字組成,第一個十進制數字定義了狀態碼的類型,后兩個數字沒有分類的作用。
狀態碼 | 含義 |
---|---|
1** | 信息。服務器收到請求,需要請求者繼續執行操作 |
2** | 成功。操作被成功接收並處理 |
3** | 重定向。需要進一步的操作以完成請求 |
4** | 客戶端錯誤。請求包含語法錯誤或無法完成請求 |
5** | 服務器錯誤。服務器在處理請求的過程中發生了錯誤 |
五、HTTP1.0, 1.1, 2.0的區別
版本 | 變化 |
---|---|
HTTP/0.9 | 僅支持GET請求,不支持請求頭, 只能傳輸純文本內容, 典型的無狀態連接 |
HTTP/1.0 | 默認短連接(一次請求建議一次TCP連接,請求完就斷開),但是增加了keep-alive關鍵字來由短鏈接變成長連接,就是請求報文里的字段指定Connection:keep-alive;支持GET、POST、 HEAD請求。 |
HTTP/1.1 | 默認長連接(一次TCP連接可以多次請求),同時也可以用請求報文Connection:close來把長連接變成短連接;新增了5種請求類型;請求頭部增加了Host字段,在HTTP1.0中認為每台服務器都綁定一個唯一的ip地址,因此在URL中並沒有傳遞主機名,但是隨着虛擬機技術的發展,可能在一台物理機器上存在多個虛擬主機,並且他們共享了一個ip地址,http1.1中請求消息和響應消息都支持host頭域;增加了100在內的一些狀態響應碼。 |
HTTP/2 | 多路復用,降低開銷(一次TCP連接可以處理多個請求), 一個連接里面並發處理請求,不像http1.1在一個tcp連接中各個請求是串行的;解析基於二進制,解析錯誤少,更高效(HTTP/1.X解析基於文本);在1.0版本后增加了header頭信息,2.0版本通過算法把header進行了壓縮這樣數據體積就更小,在網絡上傳輸就更快。 |
其中,1.0和1.1最常用,0.9幾乎不用(舊),2.0比較少用(更新代價大)
六、HTTP和HTTPS的區別
回到前面那張圖:

前面說過,當http承載於TLS、SSL之上的時候,就變成https,那么到底有哪些區別,SSL又是干嘛的?
定義:(Hypertext Transfer Protocol Secure:超文本傳輸安全協議)
HTTPS協議可以理解為HTTP協議的升級,就是在HTTP的基礎上增加了數據加密。在數據進行傳輸之前,對數據進行加密,然后再發送到服務器。這樣,經由HTTP進行通信,利用SSL/TLS建立全信道,加密數據包。就算數據被第三者所截獲,但是由於數據是加密的,所以你的個人信息讓然是安全的。
TLS是傳輸層加密協議,前身是SSL協議,由網景公司1995年發布,有時候兩者不區分。
SSL(Secure Socket Layer安全套接層)是基於HTTPS下的一個協議加密層,屬於應用層和運輸層之間,保障TCP-based的這些應用層協議的安全,所以不是http專用的。
TSL(Transport Layer Security 安全傳輸層協議)就是ssl的版本更新到后期換了一個名字。
所以對比兩個協議的區別就是:
HTTP | HTTPS |
---|---|
明文傳輸,數據未加密,安全性較差 | 數據傳輸過程是加密的,安全性較好 |
不需要CA | 需要到 CA(Certificate Authority,數字證書認證機構) 申請證書,要錢 |
三次握手,簡單,速度快 | 除了 TCP 的三個包,還要加上 ssl 握手需要的很多個包,速度慢,更耗資源 |
有了加密機制之后,輸入url到顯示內容的過程就更加復雜了,因為傳輸數據的過程還加上了加密的過程,不是簡單的請求、響應這么幾個來回。
七、HTTPS的通信過程
首先,我們回顧http的通信過程,從發起一個請求,到傳輸數據,到最后斷開連接。由於http是基於tcp協議,先建立連接,然后開始傳輸數據,大概過程可以分為如下步驟:
- http客戶端(瀏覽器)與web服務器的http端口建立連接,這個過程是基於tcp的三次握手,完成后連接建立成功;
- 客戶端發request報文,就包括前面說過的請求頭、請求行、請求體、數據;
- 服務端回復response報文,同樣包括狀態行,消息頭、響應體。
- 服務端主動發起四次揮手(tcp),結束連接。(這是一般情況下,如果說http報文指定了connection:keepalive之類的,那就不釋放。
而HTTPS通信加了安全套接層,所以在建立了tcp連接之后,首先會進行ssl安全套接層的數據傳輸,來確定加密的一些東西。
- http客戶端(瀏覽器)與web服務器的http端口建立連接,這個過程是基於tcp的三次握手,完成后連接建立成功(和http的第一步是一樣的);
- 客戶端以明文傳輸請求信息,包含版本信息,加密套件候選列表,壓縮算法候選列表,隨機數,擴展字段等信息;這一步的核心就是,給服務器一個”我支持哪些加密規則“;
- 服務器從中選出一組加密算法與HASH算法,並將自己的身份信息以證書的形式發回給瀏覽器。證書里面包含了網站地址,加密公鑰,以及證書的頒發機構等信息;
- 客戶端收到后,通過CA驗證機構校驗這個服務端的真偽,如果有問題會提示一個”證書過期“的危險,如果沒有問題,那么客戶端使用這個公鑰進行加密,然后用約定的hash算法計算出隨機數,用隨機數對消息加密,將消息再發給服務端;
- 服務端用私鑰解密,解密客戶端的消息,看hash碼是不是一致,然后再用密碼加密一段握手消息,發給客戶端;
- 客戶端解密並看hash碼是不是一致,如果一致,說明這個完整的驗證過程正常了,接下來就可以正常通信了;
- 雙方正常通信,和http的2、3步驟一樣,不過每一次都要用隨機密碼並用對稱加密算法進行加密再發送、解密再接收;
- 通信結束,和http的4一樣。
可以總結一下,整個過程中,最開始基於tcp的連接不變,最后揮手不變。正式通信之前,先進行握手,確定和測試加密的規則,然后數據傳輸的時候都要經過加密再解密。
更詳細的提前握手的過程如下:
- client_hello
- 第一次C -> S 客戶端發起請求,以明文傳輸請求信息,包含版本信息,加密套件候選列表,壓縮算法候選列表,隨機數,擴展字段等信息,相關信息如下:
- 支持的最高TSL協議版本version,從低到高依次 SSLv2 SSLv3 TLSv1 TLSv1.1 TLSv1.2, 基本不再使用低於 TLSv1 的版本;
- 客戶端支持的加密套件 cipher suites 列表, 每個加密套件對應前面 TLS 原理中的四個功能的組合:認證算法 Au (身份驗證)、密鑰交換算法 KeyExchange(密鑰協商)、對稱加密算法 Enc (信息加密)和信息摘要 Mac(完整性校驗);
- 支持的壓縮算法列表,用於后續的信息壓縮傳輸;
- 隨機數 ,用於后續的密鑰的生成;
- 擴展字段。
- server_hello+server_certificate+sever_hello_done
- 第一次S -> C
- server_hello, 服務端返回協商的信息結果,包括選擇使用的協議版本 version,選擇的加密套件,選擇的壓縮算法、隨機數等,其中隨機數用於后續的密鑰協商;
- server_certificates, 服務器端配置對應的證書鏈,用於身份驗證與密鑰交換;
- server_hello_done,通知客戶端 server_hello 信息發送結束;
- 證書校驗
- 客戶端驗證證書的合法性,如果驗證通過才會進行后續通信,否則根據錯誤情況不同做出提示和操作
- client_key_exchange+change_cipher_spec+encrypted_handshake_message
- 第二次C -> S
- client_key_exchange,合法性驗證通過之后,客戶端計算產生隨機數字並用證書公鑰加密,發送給服務器。此時客戶端已經獲取全部的計算協商密鑰需要的信息:兩個明文隨機數與自己計算產生的加密隨機數字計算得到協商密鑰。
- change_cipher_spec,客戶端通知服務器后續的通信都采用協商的通信密鑰和加密算法進行加密通信;
- encrypted_handshake_message,結合之前所有通信參數的 hash 值與其它相關信息生成一段數據,采用協商密鑰與算法進行加密,然后發送給服務器用於數據與握手驗證;
- change_cipher_spec+encrypted_handshake_message
- 第二次S -> C
- 服務器用私鑰解密加密的數據,基於之前交換的兩個明文隨機數,計算得到協商密鑰,計算之前所有接收信息的 hash 值,然后解密客戶端發送的encrypted_handshake_message,驗證數據和密鑰正確性;
- change_cipher_spec, 驗證通過之后,服務器同樣發送 change_cipher_spec 以告知客戶端后續的通信都采用協商的密鑰與算法進行加密通信;
- encrypted_handshake_message, 服務器也結合所有當前的通信參數信息生成一段數據並采用協商密鑰與算法加密並發送到客戶端;
- 握手結束
- 客戶端計算所有接收信息的 hash 值,並采用協商密鑰解密 encrypted_handshake_message,驗證服務器發送的數據和密鑰,驗證通過則握手完成;
- 加密通信
開始使用協商密鑰與算法進行加密通信。

(Ref:https://www.cnblogs.com/fengting0913/p/13291326.html )
有些東西太詳細,記起來也不容易,后來看到了一篇講的更簡單的過程:
- 客戶端(通常是瀏覽器)先向服務器發出加密通信的請求,叫ClientHello請求;
(1) 支持的協議版本,比如TLS 1.0版。
(2) 一個隨機數A,稍后用於生成"對話密鑰"。
(3) 支持的加密方法,比如RSA公鑰加密。
(4) 支持的壓縮方法。 - 服務器回應(SeverHello)
(1) 確認加密通信協議版本,比如TLS 1.0版本。
(2) 一個隨機數B,稍后用於生成"對話密鑰"。
(3) 確認使用的加密方法,比如RSA公鑰加密。
(4) 服務器證書。 - 客戶端收到服務器回應以后,首先驗證服務器證書。
如果證書不是可信機構頒布、或者證書中的域名與實際域名不一致、或者證書已經過期,就會向訪問者顯示一個警告,由其選擇是否還要繼續通信。如果證書沒有問題,客戶端就會從證書中取出服務器的公鑰,然后進行 4 。 - 客戶端回應
(1) 一個隨機數C。該隨機數用服務器公鑰加密,防止被竊聽。
(2) 編碼改變通知,表示隨后的信息都將用雙方商定的加密方法和密鑰發送。
(3) 客戶端握手結束通知,表示客戶端的握手階段已經結束。這一項同時也是前面發送的所有內容的hash值,用來供服務器校驗。(1)的隨機數,是整個握手階段出現的第三個隨機數,又稱"pre-master key"。有了它以后,客戶端和服務器就同時有了三個隨機數ABC,接着雙方就用事先商定的加密方法,各自生成本次會話所用的同一把"會話密鑰"。 - 服務器的最后回應
(1)編碼改變通知,表示隨后的信息都將用雙方商定的加密方法和密鑰發送。
(2)服務器握手結束通知,表示服務器的握手階段已經結束。這一項同時也是前面發送的所有內容的hash值,用來供客戶端校驗。
至此,整個握手階段全部結束。接下來,客戶端與服務器進入加密通信,就完全是使用普通的HTTP協議,只不過用"會話密鑰"加密內容。
八、對稱加密和非對稱加密的區別
上面提到了對稱加密和非對稱加密:
在對稱加密算法中,加密使用的密鑰和解密使用的密鑰是相同的。也就是說,加密和解密都是使用的同一個密鑰。因此對稱加密算法要保證安全性的話,密鑰要做好保密,只能讓使用的人知道,不能對外公開。
在非對稱加密算法中,加密使用的密鑰和解密使用的密鑰是不相同的。前面所說的https的公鑰密碼體制就是一種非對稱加密算法,他的公鑰和是私鑰是不能相同的,也就是說加密使用的密鑰和解密使用的密鑰不同,因此它是一個非對稱加密算法。( RSA就是一種公鑰密碼體制,現在使用得很廣泛。)
九、cookie和session
既然http是無狀態的協議,前面也說了,需要他有狀態。引出了cookie和session,二者的區別是什么?
1.cookie:Cookie就是由服務器發給客戶端的特殊信息,存放在客戶端,客戶端每次向服務器發送請求的時候都會帶上這些特殊的信息。這些信息並不是存放在HTTP響應體中的,而是存放於HTTP響應頭。
2.Session:是通過服務器來保持狀態的。在Java中是通過調用HttpServletRequest的getSession方法(使用true作為參數)創建,創建的同時,服務器會為該Session生成唯一的id,而這個id在隨后的請求中會被用來重新獲得已經創建的Session,在Session被創建之后,可以在其中增加內容,而這些內容只會保存在服務器中,發到客戶端的只有id;當客戶端再次發送請求的時候,會將這個id帶上,顯然id是在cookie里的,服務器根據id找到相應的Session。
區別:
session保存在服務器,cookie保存在客戶端;
session中保存的時對象,cookie保存的是字符串;
session不能區分路徑,同一個用戶訪問一個網站期間,所有的session在任何一個地方都可以訪問,cookie如果設置路徑,則在某些地方不能訪問;
session需要借助cookie才能正常工作,如果禁用cookie,session則失效;
客戶端會在發送請求的時候,自動將本地存活的cookie封裝在信息頭發送給服務器。
應用場景:
重要狀態走session,不重要走cookie,登陸信息用session,購物車用cookie