一.前言
還記得第一次去找工作的時候,來到杭州某某智慧醫療公司,面試的時候,那邊的技術總監就問了一個HTTP的OSI的模型有幾層。每每說到這個OSI模型,我就想起我大學的計算機網絡這門課是在睡夢中渡過的。依稀記得老師姓熊,除了老師的名字以外,整門課我現在都記不得,與其說把知識都還給老師了,還不如說是壓根就沒有學好。
"請問一下,HTTP的OSI模型有幾層?"面試官看了一下手中的簡歷問道。
"應該是5層吧!?"我心中忐忑,因爲我只知道OSI的模型是七層,但是對於HTTP的OSI有幾層我真的一頭霧水,"好像我的大學老師說是5層的。"
"那你的大學老師確實有點水。"面試官看了我一眼不知道說什麼好。
"我記得是5層的....應該沒錯吧?"我反駁道,聽到老師被別人說,心裏有一點點不甘心。
......
說來好玩,後面我和那個面試官爭的面紅耳赤。
最後的結果當然是沒有通過面試,但是我並沒有爲我回答了錯誤的答案而感到一點羞愧,因爲我並不是來應聘網絡工程師的,對於OSI什麼的究竟對於HTTP有什麼用,我當時真的不是很想去考究。
言歸正傳,我說上面的列子不是來噴那個面試官的,其實想說的就是我寫的這篇文章僅僅是HTTP在C#的一些皮毛,就像我當時面試的時候懵懂無知一樣,也許看完我這篇文章之後,小夥伴們可能已經會用HTTP了,但不代表真的是理解的透徹。如果想要學習,可能要找更多的資料和書籍進行翻閱學習。
二.HTTP簡介
(1)HTTP的由來
HTTP即爲HyperText Transfer Protocol的英文的縮寫,中文翻譯是超文本傳輸協議或者翻譯爲超文件傳輸協議,它是一種因特網上比較流行的傳輸協議。
HTTP最初用來發布和接收由文本組成的HTML網頁頁面的協議方法,後來才逐漸從只能傳輸文本數據,到可以傳輸圖片文件、影視頻文件,以及各種壓縮、程序文件等各種文件數據。
(2)HTTP協議版本
目前使用的基本上都是HTTP/1.1版本。如下圖,在訪問百度的時候發的GET請求用的HTTP版本爲HTTP/1.1:
(3)HTTP協議所在OSI層次
HTTP屬於應用層,位於TCP/IP的頂層。如下圖:
由上圖可知,HTTP是基於TCP的爲基礎的進行工作的,所以HTTP不會出現數據丟失和數據亂序的情況。客戶端通過TCP建立的鏈接後,用套接字發送HTTP,同理通過套接字接收HTTP的請求響應。
(4)HTTP發送請求過程
主要的過程如下:
1. 客戶端與服務器建立TCP鏈接;
2. 客戶端向服務器發送請求,如果服務器接收請求,則回送響應碼和所需的信息數據,響應的過程是一個異步於其他的過程的過程;
3. 重複第二步,直到客戶端將要和服務器斷開鏈接;
4. 客戶端與服務器斷開鏈接;
注意:HTTP/1.1支持持久鏈接,在客戶端發送一個請求直到收到響應之前,可以再次發送多個請求,即在完成一個請求和接收一個應答之後,還可以多次在這之後或者並列的完成多次"請求—應答";而對於HTTP/1.0則不支持持久鏈接。
(5)HTTP的特點
1. 以TCP的方式工作,上面已經分析;
2. HTTP是無狀態的,即服務器不會存儲關於客戶端的任何狀態信息,也不會保存客戶端的請求究竟是何種請求類型。如果一個客戶端請求兩次相同的內容,服務器也會進行兩次返回請求的對象內容,而不管原來是不是已經向客戶端發送過這個請求的對象;
3. HTTP使用元信息作爲標頭,即在請求的主體之前添加一部分信息,我們把這一部分數據稱之爲元信息(Metainfotmation),對於源信息所在的部分,我們稱之爲標頭(Header),上面第一個圖中的Request Header即爲我們請求百度頁面的標頭,其中的"Host: www.baidu.com"和"Connection: keep-Alive"等信息都屬於元信息;
(6)HTTP/1.0和HTTP/1.1之間的區別
HTTP/1.0 | HTTP/1.1 | |
請求類型 | 請求類型比1.1的版本少的多,定義了基本的請求類型:GET請求、POST請求和HEAD請求 | 提供了8種請求類型:GET請求、POST請求、PUT請求、HEAD請求、DELETE請求、OPTIONS請求、TRACE請求和CONNECT請求。 |
是否支持長鏈接 | 不支持 | 支持 |
標頭結構 | 無要求,標頭不需要Host部分 | 標頭部分必須有Host部分,其它都是可選部分 |
現使用情況 | 幾乎不再使用 | 絕大部分瀏覽器和服務器都使用1.1版本 |
三.HTTP的請求與相應簡述(重要)
(1)Web頁面的構成
Web頁面即網頁頁面由多個頁面元素構成,我們把這些元素稱之爲對象或者是資源,這些對象或者資源在Web頁面上由單個URL所引用,我們可以通過URL來找到訪問這些對象的地址路徑,進而來訪問這些對象。特別的,我們把Web頁面的源HTML文件也看作一個對象,即一個Web頁面由其自身的源HTML文件和其頁面上引用的其他對象資源文件所構成。如果一個頁面中包含1個視頻,1個字幕,3張圖片,那麼這個頁面由6個對象構成。
(2)HTTP/1.1請求的種類
我們將GET、POST等的請求類型也稱之爲請求的方法。HTTP 1.1提供瞭如下表8種類型的請求方法:
請求的方法名 | 說明 |
GET |
請求獲取特定的資源,例如,請求一個Web頁面 |
POST | 請求向指定資源提交數據進行處理(例如,提交表單或者上傳文件),請求的數據被包含在請求體中 |
PUT | 向指定資源位置上傳其最新內容,例如,請求儲存一個Web頁面 |
HEAD | 向服務器請求獲取與GET請求相一致的響應,只不過響應體將不會被返回。這一方法可以在不必傳輸整個響應內容的情況下,就可以獲取包含在響應消息頭中的源信息。 |
DELETE | 請求刪除指定的資源 |
OPTIONS | 返回服務器正對特定資源所支持的HTTP請求方法 |
TRACE | 回顯服務器收到的請求 |
CONNECT | 預留給能夠將連接改爲管道方式的代理服務器 |
我們在上表中最常用的方法就是GET方法和POST方法,也稱之爲GET請求和POST請求。
注意:1. 如果服務器不支持或者客戶端發送了錯誤的請求方法,服務器會返回錯誤並立即關閉連接;
2. 請求的名稱是區分大小寫的,一般以大寫的方式表示;
3. 對於HTTP/1.1服務器至少應該事先GET和HEAD方法,而其它的方法都是可選的;
(3)HTTP請求的基本格式(重要)
格式如下:
< request-line >
< headers >
< blank line >
{ < request-body > }
格式代碼說明見如下表格:
請求內容 | 說明解析 |
<request-line> |
第一行必須是一個請求行(request line),說明請求的類型、要訪問的資源以及使用的HTTP版本,這些內容之間用空格來分隔。例如: GET / HTTP/1.1 |
<headers> |
接着的這個是標頭的部分,說明服務器要使用的附加信息,這部分一般是由多行組成。 |
<blank line> | 然後在標頭之後是一個空行(blank line),它表示的標頭的結束位置。而這個空行是必須要存在的無論前後的內容如何,即使不存在請求體的部分,這個空行也是必須要存在的。 |
<request-body> | 空行之後是請求的主體(request-body),主體中可以包含任意的數據。 |
注意:1. 請求行和標頭必須以回車換行(<CR><LF>)作爲結尾。空行內必須只有<CR><LF>而無其他空格;
2. 在HTTP/1.1中,標頭中必須有Host部分,其他都是可選的;
3. 即使不存在請求體的部分,這個空行也是必須要存在的;
※這裏稍微提一下<CR><LF>的含義和來源:
CR是Carriage Return的英文的縮寫,意思是"回車",我們通常用" \r "來表示。
LF是Line Feed的英文縮寫,意思是"換行",我們通常用" \n "來表示。
在Windows系統中用" \r\n "表示下一行,而在MAC系統中則是用" \r "表示下一行,在 Unix系統中是用" \n "表示下一行,所以上面說的是在Windows下用回車換行(<CR><LF>)作爲結尾。
這裏我們平時些程序的時候,最常用到的是GET請求和POST請求,我們就以這兩個請求爲列子,探究一下請求體的格式:
(4)GET請求的請求體舉例
我們在百度的頁面中輸入1,然後點擊"百度一下"。在Chrome的控制檯中可以看到如下的內容,如圖:
GET /s?ie=utf-8&f=8&...&inputT=533 HTTP/1.1
Host: www.baidu.com
Connection: keep-alive
Upgrade-Insecure-Requests: 1
User-Agent: <...>
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8
Referer: https://www.baidu.com/?tn=02003390_15_hao_pg
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9
Cookie: <...>
我把一些不重要的內容進行了省略化,我們重點分析一下重要的幾行,如下分析表格:
重要行 | 解析 |
第一行 (GET起始行) |
這一行是由3個部分組成的,分別是"GET"、"/s?name1=value1&...&nameN=valueN"和"HTTP/1.1"組成。 【1】其中"GET"是說明請求的類型是GET請求; 【2】而" /s?name1=value1&...&nameN=valueN "部分,在" ? "之前的部分是URL地址,即Host加上"?"之前的部分,即爲 " www,baidu.com/s ",如果是單純一個" / "就表示的是網址下的根目錄" www.baidu.com/ ",對於" ? "之後的部分,則是GET請求的參數,每一個參數之間通過" & "分隔,其格式如下: URL?name1=value1&name2=value2&...&nameN=valueN 這一條信息被稱爲查詢字符串(Query String); 【3】最後的" HTTP/1.1 "說明使用的HTTP協議版本是1.1版本; |
第二行 (Host起始行) |
第二行是Host標頭,指出請求的目的地址。結合上面第一行的第二部分使用。 注意:上面已經說過HTTP1.1才需要使用標頭Host,而1.0的版本是不需要的; |
第三行(Connenction起始行) | 第三行是Connection標頭,通常將瀏覽器操作設置爲Keep-Alive(當然也可以是其他的值)。 |
第四行(User-Agent起始行) | 第四行是User-Agent標頭,服務器和客戶端腳本都能夠訪問它,它是瀏覽器類型檢測邏輯的重要基礎。該信息由使用的瀏覽器來定義,並且在每個請求中都會自動發送。 |
最後空行 |
這一行在上面的圖中並沒有標出,但是按照上面的格式,這一行是存在的,表示標頭的結束。 注意:上面已經說過即使不存在請求體的部分,這個空行也是必須要存在的。 |
(5)POST請求體舉例
我們來使用一下百度翻譯,翻譯"烏龍茶",獲得請求體如下圖所示:
可以看到其中不但有請求的標頭,還有表單數據。我將上面的數據整理如下:
Request Header:
POST /langdetect HTTP/1.1
Host: fanyi.baidu.com
Connection: keep-alive
Content-Length: 33
Accept: */*
Origin: https://fanyi.baidu.com
X-Requested-With: XMLHttpRequest
User-Agent: Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.81 Safari/537.36
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
Referer: https://fanyi.baidu.com/?aldtype=16047
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9
Cookie: <...>
Form Data:
query=%E4%B9%8C%E9%BE%99%E8%8C%B6
我把一些不重要的內容進行了省略化,我們重點分析一下重要的幾行,如下分析表格:
重要行 | 解析 |
第一行(GET起始行) |
【1】第一行的第一部分請求類型修改爲了POST請求; 【2】第二部分僅僅只含有URL,而不再含有" ? ",以及" ? "之後的參數部分,這部分消失的參數將會出現的請求體的表單部分中; 我們可以看到請求的地址爲 "fanyi.baidu.com/langdetect " |
第二行(Host起始行)、第三行(Connection起始行)、第八行(User-Agent起始行) | 和上面的GET請求的標頭作用一致 |
第四行(Content-Length起始行) | 這一行的Content-Length標頭說明了請求主體的字節數 |
第九行(Content-Type起始行) | 第九航的Content-Type標頭 |
(6)HTTP響應的基本格式(重要)
格式如下:
< status-line >
< headers >
< blank line >
{ < response-body > }
格式代碼說明見如下表格:
響應內容 | 解析說明 |
< status-line > | 第一行都會是一個狀態行,該行的內容一次是當前的HTTP版本號、3位數字組成的狀態碼以及描述這個狀態的短語組成,每個組成部分用空格分隔 |
< headers >、< blank line >和{ < response-body > } | 之後的部分個請求體格式中的差別不大 |
(7)HTTP狀態碼分類及常用狀態碼(重要)
【1】狀態碼以第一個數字代表當前響應的類型,具體的規定如下表:
狀態碼 | 大類含義 | 解釋 |
1XX | 消息 | 請求已經被服務器接收,繼續處理 |
2XX | 成功 | 請求已經成功被服務器接收、理解並接收 |
3XX | 重定向 | 需要後續操作才能完成這一請求 |
4XX | 請求錯誤 | 請求含有詞法錯誤或者無法被執行 |
5XX | 服務器錯誤 | 服務器在處理某個正確請求的時候發生錯誤 |
【2】HTTP常用的狀態碼
狀態碼 | 含義 | 說明 |
200 | OK | 找到了資源,並且一切正常 |
304 | NOT MODIFIED | 該資源在上次請求之後沒有任何修改(這通常用於瀏覽器的緩存機制) |
401 | UNAUTHORIZED | 客戶端無權訪問該資源(這通常會使得瀏覽器要求用戶輸入用戶名和密碼,以登錄到服務器) |
403 | FORBIDDEN | 客戶端未能獲得授權(這通常是在401之後輸入了不正確的用戶名或密碼) |
404 | NOT FOUND | 在指定的位置不存在所申請的資源 |
405 | METHOD NOT ALLOWED | 不支持對應的請求方法 |
501 | NOT IMPLEMEMTED | 服務器不能識別請求或者未實現指定的請求 |