1. 程式人生 > >深入理解HTTP【轉載】

深入理解HTTP【轉載】

出處:深入理解HTTP—Joanna.Yan

先看一個簡單的http請求和響應:

1.HTTP協議是什麼**?**

圖片名稱 圖片名稱

我們瀏覽的每一個網頁都是基於HTTP協議呈現的,HTTP協議是網際網路應用中,客戶端(瀏覽器)

與伺服器之間進行資料通訊的一種協議。協議中規定了客戶端應該按照什麼格式給伺服器傳送請求,同時也約定了服務端返回的響應結果應該是什麼格式。

只要大家都按照協議規定方式發起請求和返回響應結果,任何人都可以基於HTTP協議實現自己的Web客戶端(瀏覽器、爬蟲)和Web伺服器(Nginx、Apache等)。

HTTP協議本身是非常簡單的。它規定,只能由客戶端主動發起請求,伺服器接收請求處理後返回響應結果,同時HTTP是一種無狀態的協議,協議本身不記錄客戶端的歷史請求記錄。

HTTP(超文字傳輸協議)是一個基於請求與響應模式的、無狀態的、應用層的協議,常基於TCP的連線方式。
HTTP協議的主要特點:

1.支援客戶端/伺服器模式;

2.簡單快速:客戶向伺服器請求服務時,只需傳送請求方法和路徑。由於HTTP協議簡單,通訊速度很快。

3.靈活:HTTP允許傳輸任意型別的資料物件。型別由Content-Type加以標記。

4.無連線:即每次連線只處理一個請求,處理完畢客戶的請求,並收到客戶的應答後,即斷開連線。採用這種方式可以節省傳輸時間。

5.無狀態:無狀態是指協議對於事務處理沒有記憶能力。

HTTP1.0協議預設的是非持久連線,HTTP1.1預設的連線方式為持久連線。

非持久連線:每次伺服器發出一個物件後,相應的TCP連線就被關閉,也就是說每個連線都沒有持續到可用於傳送其他物件。每個TCP連線只用於傳輸一個請求和一個響應訊息。

持久連線:伺服器在發出響應後讓TCP連線繼續開啟著。同一對客戶/伺服器之間的後續請求和響應可以通過這個連線傳送。HTTP/1.1的預設模式使用帶流水線的持久連線。

HTTP協議是如何規定請求格式和響應格式的呢?換言之,客戶端按照什麼格式才能正確發起HTTP請求呢?服務端按照什麼格式返回響應結果客戶端才能正確解析?

2.HTTP請求

HTTP請求由3部分組成,分別是請求行、請求首部、請求體,首部和請求體是可選的,並不是每個請求都需要的。

請求行

請求行是每個請求必不可少的部分,它由3部分組成,分別是請求方法(method)、請求URL(URI)、HTTP協議版本,以空格隔開。

HTTP協議中最常用的請求方法有:GET、POST、PUT、DELETE。GET方法用於從伺服器獲取資源,90%的爬蟲都是基於GET請求抓取資料。

請求URL是指資源所在伺服器的路徑地址,比如上圖例子表示客戶端想獲取index.html這個資源,它的路徑在伺服器foofish.net的根目錄(/)下面。

請求首部

因為請求行所攜帶的資訊量非常有限,以至於客戶端還有很多想向伺服器要所的事情不得不放在請求首部(Header),請求首部用於給伺服器提供一些額外的資訊,比如User-Agent用來表示客戶端的身份,讓伺服器知道你是來自瀏覽器的請求還是爬蟲,是來自Chrome瀏覽器還是FireFox。HTTP/1.1規定了47種首部欄位型別。HTTP首部欄位的格式很像Python中的字典型別,由鍵值對組成,中間用冒號隔開。比如:

User-Agent: Mozilla/5.0

因為客戶端傳送請求時,傳送的資料(報文)是由字串構成的,為了區分請求首部的結尾和請求體的開始,使用一個空行來表示,遇到空行時,就表示這個是首部的結尾,請求體的開始。

請求體

請求體是客戶端提交給伺服器的真正內容,比如使用者登入時的需要用的使用者名稱和密碼,比如檔案上傳的資料,比如註冊使用者資訊時提交的表單資訊。

現在我們用Python提供的最原始API socket模組來模擬向伺服器發起一個HTTP請求。

with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
    # 1. 與伺服器建立連線
    s.connect(("www.seriot.ch", 80))
    # 2. 構建請求行,請求資源是 index.php
    request_line = b"GET /index.php HTTP/1.1"
    # 3. 構建請求首部,指定主機名
    headers = b"Host: seriot.ch"
    # 4. 用空行標記請求首部的結束位置
    blank_line = b"\r\n"

    # 請求行、首部、空行這3部分內容用換行符分隔,組成一個請求報文字串
    # 傳送給伺服器
    message = b"\r\n".join([request_line, headers, blank_line])
    s.send(message)

    # 伺服器返回的響應內容稍後進行分析
    response = s.recv(1024)
    print(response)

3.HTTP響應

服務端接收請求並處理後,返回響應內容給客戶端,同樣地,響應內容也必須遵循固定的格式瀏覽器才能正確解析。HTTP響應也由3部分組成,分別是:響應行、響應首部、響應體,與HTTP的請求格式是想物件的。

響應行

響應行同意也是3部分組成,由服務端支援的HTTP協議版本號、狀態碼以及對狀態嗎的簡短原因描述組成。

狀態碼是響應行中很重要的一個欄位。通過狀態碼,客戶端可以知道伺服器是否正常處理的請求。如果狀態碼是200,說明客戶端的請求處理成功,如果是500,說明伺服器處理的時候出現了異常。404表示請求的資源在伺服器找不到。除此之外,HTTP協議還定義了很多其他的狀態碼。

響應首部

響應首部金額請求首部類似,用於響應內容的補充,在受不裡面可以告知客戶端響應體的資料型別是什麼?響應內容返回的時候是什麼時候,響應體是否壓縮了,響應體最後一次修改的時間。

響應體

響應體(body)是伺服器返回的真正內容。它可以是一個HTML頁面,或者是一張圖片、一段視訊等等。

我們繼續沿用前面那個例子來看看伺服器返回的響應結果是什麼?因為這裡只接收了前1024個位元組,所以有一部分響應內容是看不到的。
複製程式碼

b'HTTP/1.1 200 OK\r\n
Date: Tue, 04 Apr 2017 16:22:35 GMT\r\n
Server: Apache\r\n
Expires: Thu, 19 Nov 1981 08:52:00 GMT\r\n
Set-Cookie: PHPSESSID=66bea0a1f7cb572584745f9ce6984b7e; path=/\r\n
Transfer-Encoding: chunked\r\n
Content-Type: text/html; charset=UTF-8\r\n\r\n118d\r\n

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">\n\n
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">\n
<head>\n\t
    <meta http-equiv="Content-Type" content="text/html;charset=iso-8859-1" />    \n\t
    <meta http-equiv="content-language" content="en" />\n\t
...
</html>

從結果來看,它與協議中規範的格式是一樣的,第一行是響應行,狀態碼是200,表明請求成功。第二部分是響應首部資訊,由多個首部組成,有伺服器返回響應的時間,Cookie資訊等等。第三部分就是真正的響應體HTML文字。

Java中平時我們訪問網路經常會用到:
4.HttpURLConnection和HttpClient

在JDK的java.NET包中已經提供了訪問HTTP協議的基本功能:HttpURLConnection。

但是對於大部分應用程式來說,JDK庫本身提供的功能還不夠豐富和靈活。

除此之外,在Android中,androidSDK中集成了Apache的HttpClient模組,用來提供高效的、最新的、功能豐富的支援HTTP協議工具包,並且它支援HTTP協議最新的版本和建議。使用HttpClient可以快速開發出功能強大的Http程式。

區別:

HttpClient是個很不錯的開源框架,封裝了訪問http的請求頭、引數、內容體、響應等等。

HttpURLConnection是Java的標準類,什麼都沒封裝,用起來太原始,不方便,比如重訪問的自定義以及一些高階功能等。