天天看點

HTTP 協定入門HTTP 協定入門

HTTP 協定入門

HTTP 協定是網際網路的基礎協定,也是網頁開發的必備知識,最新版本 HTTP/2 更是讓它成為技術熱點。

本文介紹 HTTP 協定的曆史演變和設計思路。

HTTP 協定入門HTTP 協定入門

一、HTTP/0.9

HTTP 是基于 TCP/IP 協定的應用層協定。它不涉及資料包(packet)傳輸,主要規定了用戶端和伺服器之間的通信格式,預設使用80端口。

最早版本是1991年釋出的0.9版。該版本極其簡單,隻有一個指令

GET

GET /index.html
           

上面指令表示,TCP 連接配接(connection)建立後,用戶端向伺服器請求(request)網頁

index.html

協定規定,伺服器隻能回應HTML格式的字元串,不能回應别的格式。

<html>
  <body>Hello World</body>
</html>
           

伺服器發送完畢,就關閉TCP連接配接。

二、HTTP/1.0

2.1 簡介

1996年5月,HTTP/1.0 版本釋出,内容大大增加。

首先,任何格式的内容都可以發送。這使得網際網路不僅可以傳輸文字,還能傳輸圖像、視訊、二進制檔案(都是位元組流)。這為網際網路的大發展奠定了基礎。

其次,除了

GET

指令,還引入了

POST

指令和

HEAD

指令,豐富了浏覽器與伺服器的互動手段。

再次,HTTP請求和回應的格式也變了。除了資料部分,每次通信都必須包括頭資訊(HTTP header),用來描述一些中繼資料。

其他的新增功能還包括狀态碼(status code)、多字元集支援、多部分發送(multi-part type)、權限(authorization)、緩存(cache)、内容編碼(content encoding)等。

2.2 請求格式

下面是一個1.0版的HTTP請求的例子。

GET / HTTP/1.0
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_5)
Accept: */*
           

可以看到,這個格式與0.9版有很大變化。

第一行是請求指令,必須在尾部添加協定版本(

HTTP/1.0

)。後面就是多行頭資訊,描述用戶端的情況。

2.3 回應格式

伺服器的回應如下。

HTTP/1.0 200 OK 
Content-Type: text/plain
Content-Length: 137582
Expires: Thu, 05 Dec 1997 16:00:00 GMT
Last-Modified: Wed, 5 August 1996 15:55:28 GMT
Server: Apache 0.84

<html>
  <body>Hello World</body>
</html>
           

回應的格式是"頭資訊 + 一個空行(

\r\n

) + 資料"。其中,第一行是"協定版本 + 狀态碼(status code) + 狀态描述"。

2.4 Content-Type 字段

關于字元的編碼,1.0版規定,頭資訊必須是 ASCII 碼,後面的資料可以是任何格式。是以,伺服器回應的時候,必須告訴用戶端,資料是什麼格式,這就是

Content-Type

字段的作用。

下面是一些常見的

Content-Type

字段的值。

  • text/plain
  • text/html
  • text/css
  • image/jpeg
  • image/png
  • image/svg+xml
  • audio/mp4
  • video/mp4
  • application/javascript
  • application/pdf
  • application/zip
  • application/atom+xml

這些資料類型總稱為

MIME type

,每個值包括一級類型和二級類型,之間用斜杠分隔。

除了預定義的類型,廠商也可以自定義類型。

application/vnd.debian.binary-package
           

上面的類型表明,發送的是Debian系統的二進制資料包。

MIME type

還可以在尾部使用分号,添加參數。

Content-Type: text/html; charset=utf-8
           

上面的類型表明,發送的是網頁,而且編碼是UTF-8。

用戶端請求的時候,可以使用

Accept

字段聲明自己可以接受哪些資料格式。

Accept: */*
           

上面代碼中,用戶端聲明自己可以接受任何格式的資料。

MIME type

不僅用在HTTP協定,還可以用在其他地方,比如HTML網頁。

<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<!-- 等同于 -->
<meta charset="utf-8" /> 
           

2.5 Content-Encoding 字段

由于發送的資料可以是任何格式,是以可以把資料壓縮後再發送。

Content-Encoding

字段說明資料的壓縮方法。

Content-Encoding: gzip
Content-Encoding: compress
Content-Encoding: deflate
           

用戶端在請求時,用

Accept-Encoding

字段說明自己可以接受哪些壓縮方法。

Accept-Encoding: gzip, deflate
           

2.6 缺點

HTTP/1.0 版的主要缺點是,每個TCP連接配接隻能發送一個請求。發送資料完畢,連接配接就關閉,如果還要請求其他資源,就必須再建立一個連接配接。

TCP連接配接的建立成本很高,因為需要用戶端和伺服器三次握手,并且開始時發送速率較慢(slow start)。是以,HTTP 1.0版本的性能比較差。随着網頁加載的外部資源越來越多,這個問題就愈發突出了。

為了解決這個問題,有些浏覽器在請求時,用了一個非标準的

Connection

字段。

Connection: keep-alive
           

這個字段要求伺服器不要關閉TCP連接配接,以便其他請求複用。伺服器同樣回應這個字段。

Connection: keep-alive
           

一個可以複用的TCP連接配接就建立了,直到用戶端或伺服器主動關閉連接配接。但是,這不是标準字段,不同實作的行為可能不一緻,是以不是根本的解決辦法。

三、HTTP/1.1

1997年1月,HTTP/1.1 版本釋出,隻比 1.0 版本晚了半年。它進一步完善了 HTTP 協定,一直用到了20年後的今天,直到現在還是最流行的版本。

3.1 持久連接配接

1.1 版的最大變化,就是引入了持久連接配接(persistent connection),即TCP連接配接預設不關閉,可以被多個請求複用,不用聲明

Connection: keep-alive

用戶端和伺服器發現對方一段時間沒有活動,就可以主動關閉連接配接。不過,規範的做法是,用戶端在最後一個請求時,發送

Connection: close

,明确要求伺服器關閉TCP連接配接。

Connection: close
           

目前,對于同一個域名,大多數浏覽器允許同時建立6個持久連接配接。

3.2 管道機制

1.1 版還引入了管道機制(pipelining),即在同一個TCP連接配接裡面,用戶端可以同時發送多個請求。這樣就進一步改進了HTTP協定的效率。

舉例來說,用戶端需要請求兩個資源。以前的做法是,在同一個TCP連接配接裡面,先發送A請求,然後等待伺服器做出回應,收到後再發出B請求。管道機制則是允許浏覽器同時發出A請求和B請求,但是伺服器還是按照順序,先回應A請求,完成後再回應B請求。

3.3 Content-Length 字段

一個TCP連接配接現在可以傳送多個回應,勢必就要有一種機制,區分資料包是屬于哪一個回應的。這就是

Content-length

字段的作用,聲明本次回應的資料長度。

Content-Length: 3495
           

上面代碼告訴浏覽器,本次回應的長度是3495個位元組,後面的位元組就屬于下一個回應了。

在1.0版中,

Content-Length

字段不是必需的,因為浏覽器發現伺服器關閉了TCP連接配接,就表明收到的資料包已經全了。

3.4 分塊傳輸編碼

使用

Content-Length

字段的前提條件是,伺服器發送回應之前,必須知道回應的資料長度。

對于一些很耗時的動态操作來說,這意味着,伺服器要等到所有操作完成,才能發送資料,顯然這樣的效率不高。更好的處理方法是,産生一塊資料,就發送一塊,采用"流模式"(stream)取代"緩存模式"(buffer)。

是以,1.1版規定可以不使用

Content-Length

字段,而使用"分塊傳輸編碼"(chunked transfer encoding)。隻要請求或回應的頭資訊有

Transfer-Encoding

字段,就表明回應将由數量未定的資料塊組成。

Transfer-Encoding: chunked
           

每個非空的資料塊之前,會有一個16進制的數值,表示這個塊的長度。最後是一個大小為0的塊,就表示本次回應的資料發送完了。下面是一個例子。

HTTP/1.1 200 OK
Content-Type: text/plain
Transfer-Encoding: chunked

25
This is the data in the first chunk

1C
and this is the second one

3
con

8
sequence

0

           

3.5 其他功能

1.1版還新增了許多動詞方法:

PUT

PATCH

HEAD

、 

OPTIONS

DELETE

另外,用戶端請求的頭資訊新增了

Host

字段,用來指定伺服器的域名。

Host: www.example.com
           

有了

Host

字段,就可以将請求發往同一台伺服器上的不同網站,為虛拟主機的興起打下了基礎。

3.6 缺點

雖然1.1版允許複用TCP連接配接,但是同一個TCP連接配接裡面,所有的資料通信是按次序進行的。伺服器隻有處理完一個回應,才會進行下一個回應。要是前面的回應特别慢,後面就會有許多請求排隊等着。這稱為"隊頭堵塞"(Head-of-line blocking)。

為了避免這個問題,隻有兩種方法:一是減少請求數,二是同時多開持久連接配接。這導緻了很多的網頁優化技巧,比如合并腳本和樣式表、将圖檔嵌入CSS代碼、域名分片(domain sharding)等等。如果HTTP協定設計得更好一些,這些額外的工作是可以避免的。

四、SPDY 協定

2009年,谷歌公開了自行研發的 SPDY 協定,主要解決 HTTP/1.1 效率不高的問題。

這個協定在Chrome浏覽器上證明可行以後,就被當作 HTTP/2 的基礎,主要特性都在 HTTP/2 之中得到繼承。

五、HTTP/2

2015年,HTTP/2 釋出。它不叫 HTTP/2.0,是因為标準委員會不打算再釋出子版本了,下一個新版本将是 HTTP/3。

5.1 二進制協定

HTTP/1.1 版的頭資訊肯定是文本(ASCII編碼),資料體可以是文本,也可以是二進制。HTTP/2 則是一個徹底的二進制協定,頭資訊和資料體都是二進制,并且統稱為"幀"(frame):頭資訊幀和資料幀。

二進制協定的一個好處是,可以定義額外的幀。HTTP/2 定義了近十種幀,為将來的進階應用打好了基礎。如果使用文本實作這種功能,解析資料将會變得非常麻煩,二進制解析則友善得多。

5.2 多工

HTTP/2 複用TCP連接配接,在一個連接配接裡,用戶端和浏覽器都可以同時發送多個請求或回應,而且不用按照順序一一對應,這樣就避免了"隊頭堵塞"。

舉例來說,在一個TCP連接配接裡面,伺服器同時收到了A請求和B請求,于是先回應A請求,結果發現處理過程非常耗時,于是就發送A請求已經處理好的部分, 接着回應B請求,完成後,再發送A請求剩下的部分。

這樣雙向的、實時的通信,就叫做多工(Multiplexing)。

5.3 資料流

因為 HTTP/2 的資料包是不按順序發送的,同一個連接配接裡面連續的資料包,可能屬于不同的回應。是以,必須要對資料包做标記,指出它屬于哪個回應。

HTTP/2 将每個請求或回應的所有資料包,稱為一個資料流(stream)。每個資料流都有一個獨一無二的編号。資料包發送的時候,都必須标記資料流ID,用來區分它屬于哪個資料流。另外還規定,用戶端發出的資料流,ID一律為奇數,伺服器發出的,ID為偶數。

資料流發送到一半的時候,用戶端和伺服器都可以發送信号(

RST_STREAM

幀),取消這個資料流。1.1版取消資料流的唯一方法,就是關閉TCP連接配接。這就是說,HTTP/2 可以取消某一次請求,同時保證TCP連接配接還打開着,可以被其他請求使用。

用戶端還可以指定資料流的優先級。優先級越高,伺服器就會越早回應。

5.4 頭資訊壓縮

HTTP 協定不帶有狀态,每次請求都必須附上所有資訊。是以,請求的很多字段都是重複的,比如

Cookie

User Agent

,一模一樣的内容,每次請求都必須附帶,這會浪費很多帶寬,也影響速度。

HTTP/2 對這一點做了優化,引入了頭資訊壓縮機制(header compression)。一方面,頭資訊使用

gzip

compress

壓縮後再發送;另一方面,用戶端和伺服器同時維護一張頭資訊表,所有字段都會存入這個表,生成一個索引号,以後就不發送同樣字段了,隻發送索引号,這樣就提高速度了。

5.5 伺服器推送

HTTP/2 允許伺服器未經請求,主動向用戶端發送資源,這叫做伺服器推送(server push)。

常見場景是用戶端請求一個網頁,這個網頁裡面包含很多靜态資源。正常情況下,用戶端必須收到網頁後,解析HTML源碼,發現有靜态資源,再發出靜态資源請求。其實,伺服器可以預期到用戶端請求網頁後,很可能會再請求靜态資源,是以就主動把這些靜态資源随着網頁一起發給用戶端了。

六、參考連結

  • Journey to HTTP/2, by Kamran Ahmed
  • HTTP, by Wikipedia
  • HTTP/1.0 Specification
  • HTTP/2 Specification

繼續閱讀