HTTP是超文本傳輸協議,是一種用於在萬維網,傳輸文件,不管是HTML文件,仍是圖片,請求等數據的網絡協議。一般HTTP協議是創建在TCP/IP socket通訊基礎之上的。css
HTTP協議是客戶端(client)和服務器(servers)通訊的協議。瀏覽器就是一個客戶端,由於它發送請求給HTTP服務器(也就是web服務器),web服務器返回響應給客戶端。符合HTTP協議的服務器,默認監聽80端口,固然也能夠從新指定任何一個端口。html
HTTP協議是用來傳播資源,而不只僅只是文件。資源就是一個URL連接所對應的一些信息。咱們廣泛見到的資源就是文件,同時資源也多是:經過不一樣編程語言寫的CGI腳本,動態生成,並輸出。返回請求結果的文件。web
學習HTTP,有助於理解資源相似於文件的概念。實際場景中,HTTP資源不是靜態文件,就是服務器端腳本動態生成的結果。django
就像大多數的網絡協議,HTTP也是C/S模式:客戶端向服務器發送請求鏈接和請求的信息內容,服務器返回響應信息。一般包含請求的資源。服務器發送完響應後,關閉鏈接。(HTTP是一個無狀態的鏈接)編程
請求和響應的格式長得差很少,它們都是由:瀏覽器
<initial line, different for request vs. response> Header1: value1 Header2: value2 Header3: value3 <optional message body goes here, like file contents or query data; it can be many lines long, or even binary data $&*%@!^$@>
initial line
和headers
必須由回車
結尾。緩存
請求的第一行和響應的第一行不同!請求的第一行有三個部分:方法名,請求資源的路徑(也就是/分隔的路徑),使用的HTTP協議版本。訪問個人博客首頁時,請求頭以下:安全
GET / HTTP/1.1
注意:服務器
響應的初始行,稱做‘狀態行’。也是由三個部分組成的:HTTP協議版本,狀態碼,狀態碼描述。一樣以我博客爲例子,狀態行以下:網絡
HTTP/1.1 304 Not Modified
注意:
常見的狀態碼:
200 OK:請求成功,接收到資源。
404 Not Found:請求失敗,未找到資源。
301 Moved Permanently:永久性轉移。
302 Moved Temporarily:暫時性轉移。
303 See Other:請求的資源已經移到了另一個URL上了,客戶端會自動跳轉。這個一般是CGI腳本使用redirect
,使得客戶端,重定向到另一個URL。
500 Server Error:一個未知的服務器錯誤。
請求頭提供了請求或響應的信息,或者是關於發送的消息體(message body)的信息。
請求頭的格式爲:每條頭信息佔一行例如「Header-Name: value」,以回車結尾。這個格式也被用於郵件等,更加詳細的說明:
下面的兩種格式,效果是同樣的:
Header1: some-long-value-1a, some-long-value-1b HEADER1: some-long-value-1a, some-long-value-1b
HTTP1.0定義了16種頭,沒有強制要帶的。而HTTP1.1定義了46種頭,而且請求時必須帶(Host:)。請求時,一些約定俗稱的規定(不遵照沒問題,可是最好遵照)。
User-Agent:Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/49.0.2623.87 Safari/537.36
上面說的這些頭,幫助網絡管理員分析問題。這些信息也提供了用戶的身份(這些信息能夠僞造)。
若是你在寫一個‘servers’,考慮返回響應時把下面這些頭加上:
Last-Modified: Fri, 31 Dec 1999 23:59:59 GMT
一個HTTP消息,頭信息後面可能有一個消息實體。響應的時候,這個消息實體就是:客戶端請求的資源,或者是提示的錯誤信息。請求時,消息實體就是:用戶輸入的數據,或者上傳的文件。
若是一個HTTP消息包含消息實體,那麼一般就會下面的幾個頭,用來描述消息實體,例如:
例如請求一個文件:http://www.somehost.com/path/file.html
首先與目標網站:'www.somehost.com'的80端口創建起socket鏈接。以後經過socket鏈接發送相似:
GET /path/file.html HTTP/1.0 From: someuser@jmarshall.com User-Agent: HTTPTool/1.0 [blank line here]
這時服務器回返迴響應,內容格式以下:
HTTP/1.0 200 OK Date: Fri, 31 Dec 1999 23:59:59 GMT Content-Type: text/html Content-Length: 1354 <html> <body> <h1>Happy New Millennium!</h1> (more file contents) . . . </body> </html>
發送完響應以後,服務器會關閉這個socket鏈接。
HTTP代理就是服務器和客戶端的一箇中間程序。它從客戶端接受請求,而後把這些請求再發送給服務器。響應返回的時候也是同理,須要經過代理。
代理一般用於防火牆,局域網的安全等。
當客戶端使用代理,它就會把全部的請求發送給代理,而不是發給服務器。經過代理請求和普通的請求有一點不一樣:第一行,代理請求使用完整的URL,而不是隻有path。例如:
經過代理請求:GET http://www.somehost.com/path/file.html HTTP/1.0 普通請求:GET /path/file.html HTTP/1.0
經過這種方式,代理就知道請求的服務器地址了。
就像常說的:「嚴格的發送,寬容的接收。」你交互信息過程當中,其它的客戶端和服務器,它們發送的信息都有可能有瑕疵。可是,你應該嘗試預料到這些問題,從而使一切正常的運行。下面有些建議:
固然還有一些其餘的狀況,總之要多兼容。
這是HTTP的基本知識。若是你想知道更多,你須要查看官方的資料。
到此爲止只講理HTTP1.0的知識,下面會講HTTP1.1的知識,因此休息一下,讓咱們升級一下!
想不少的協議同樣,HTTP是不斷升級的。HTTP1.1完善了HTTP1.0的一些缺點。總的來講,改善的地方包括:
HTTP1.1須要再服務器和客戶端增長一些額外的東西。接下來的兩個部分,分別講:如何編寫遵循HTTP1.1協議的客戶端和服務器。固然,你若是隻寫客戶端,只須要看客戶端的部分。能夠根據本身的需求選擇閱讀。
爲了符合HTTP1.1,客戶端必須:
HTTP1.1開始,支持一個IP對應多個虛擬主機。好比:「www.host1.com」和「www.host2.com」能夠是同一個服務器(同一IP)。
一個服務器,有多個域名就像:不一樣的人,共享一個手機。打電話的人知道他們要找誰,可是接電話的人不知道!因此,打來電話的人須要明確的指出他要找誰。同理,每個HTTP請求必須在Host頭中,明確地指出請求的host。例如:
GET /path/file.html HTTP/1.1 Host: www.host1.com:80 [blank line here]
其中:80
不需特別指出,由於默認就是訪問80端口。
HTTP1.1協議下的請求,請求頭中必須包含Host
。沒有它,每個域名須要一個獨一無二的IP地址,IP地址的數據量正在急劇減小,而網站(域名)卻以爆炸的速度增加。Host能夠有效的緩解IP地址緊張的現狀。
若是服務器想要在知道響應數據總量以前,就發送響應(好比特別長的響應內容,這樣計算數據總量會耗費很長的時間),那麼就會用到‘分塊傳輸編碼’。它把完整的響應數據,分紅不少個大小相同的數據塊,而後發送。你能夠一樣接收這樣的數據,由於頭已經包含了‘Transfer-Encoding: chunked’。全部的HTTP1.1客戶端必須能夠接收分塊的信息。
分塊的消息內容須要包含:有一行是‘0’,用於表示內容的結束。有'footers',和一個空行。必須包含兩個部分:
例如:
HTTP/1.1 200 OK Date: Fri, 31 Dec 1999 23:59:59 GMT Content-Type: text/plain Transfer-Encoding: chunked a; ignore-stuff-here abcdefghijklmnopqrstuvwxyz 10 234567890abcdef 0 some-footer: some-value another-footer: another-value [blank line here]
不要忘記,最後有一行空行。文本內容的大小是42bytes(1a+10=16+10+16=42),內容是:‘abcdefghijklmnopqrstuvwxyz1234567890abcdef’。
分塊數據能夠包含任意的二進制數據。下面內容是同樣的,可是沒有使用‘分塊傳輸編碼’的響應。以下:
HTTP/1.1 200 OK Date: Fri, 31 Dec 1999 23:59:59 GMT Content-Type: text/plain Content-Length: 42 some-footer: some-value another-footer: another-value abcdefghijklmnopqrstuvwxyz1234567890abcdef
在HTTP1.0以前,每一個請求和響應完成後都會關閉TCP鏈接,因此每次獲取都是一個單獨,獨立的���接。建立和關閉TCP鏈接花費大量的CPU資源,帶寬和內存。實際操做中,組成一個網頁的多個文件都是在一臺服務器上。因此,多個請求和響應均可以經過一個持久鏈接進行傳輸。
HTTP1.1默認是持久鏈接,因此若是沒有特殊的需求使用的就是持久鏈接。只須要創建一個鏈接,就是能夠發送多個請求和讀取返回的響應。若是你這麼作,必定要注意讀取響應返回的長度,以確保正確的區別他們。
若是一個客戶端在請求頭中聲明瞭「Connection: close」的話,鏈接會在響應送達後關閉。好比,這種操做的場景:若是你知道這是這個鏈接的,最後一個請求。一樣的,若是響應頭包含這個聲明,服務器會在發送完響應以後,關閉鏈接。因此,客戶端不能經過這個連接,發送任何請求。
服務器可能在發送任何一個響應以前關閉鏈接。因此,客戶端必須保持時刻檢查Persistent Connections頭的值。以確保選擇的鏈接是通路。
客戶端使用HTTP1.1協議向服務器發送請求,服務器可能返回臨時響應:‘100 Coninue’。它表示服務器接收到了請求的第一部分,後面還有一些緩慢的數據傳輸。因此,不管如何HTTP1.1的客戶端必須能正確處理‘100’狀態碼的響應。
返回的‘100 Continue’狀態碼,和咱們前面說的‘200 OK’都是同樣的,符合正常的響應的格式。惟一不一樣的是,響應的內容。以下:
HTTP/1.1 100 Continue #沒有過完整的響應內容。 HTTP/1.1 200 OK Date: Fri, 31 Dec 1999 23:59:59 GMT Content-Type: text/plain Content-Length: 42 some-footer: some-value another-footer: another-value abcdefghijklmnoprstuvwxyz1234567890abcdef
爲了解決上面的這種狀況(100 狀態碼沒有數據),一個簡單的HTTP1.1客戶端能夠經過socket讀取響應;若是狀態碼是100,就忽略此次響應,轉而讀取下個響應。
爲了聽從HTTP1.1,服務器必須:
每一個請求,必須包含Host頭,不然就會返回‘400 Bad Request’響應,以下:
HTTP/1.1 400 Bad Request Content-Type: text/html Content-Length: 111 <html><body> <h2>No Host: header received</h2> HTTP 1.1 requests must include the Host: header. </body></html>
Host頭其實是一個過渡的解決區別host的辦法。之後的HTTP版本,請求將要使用絕對地址代替路徑,好比:GET http://www.somehost.com/path/file.html HTTP/1.2
HTTP1.1服務器必須接受這種格式的請求,儘管HTTP1.1客戶端不發送這樣的請求。若是客戶端沒有host頭,服務器還必需要報告錯誤。
就像HTTP1.1客戶必須接受分塊的響應,服務器必須接受分塊的請求。服務器不須要生成,分塊的信息。只要可以接受分塊請求就能夠了。
若是HTTP1.1客戶端經過一個鏈接,發送了多個請求。爲了支持持久鏈接,服務器返回響應的順序應該和請求的順序是同樣的。
若是過一個請求包含‘Connection: close’頭,表示這是這個鏈接的最後一個請求,服務器須要在返回響應後關閉鏈接。服務器也會關閉超時閒置的鏈接。(一般設置10s超時)
若是你不想支持持久鏈接,響應頭中包含‘Connection: close’。這就是表示:返回當前這個響應以後,鏈接就會關閉。正確的支持HTTP1.1客戶端能正確的接受這個頭信息。
正如HTTP1.1客戶端那段描述的那樣,這個響應是爲了處理反應慢的鏈接的。
當一個HTTP1.1服務器收到一個HTTP1.1請求,若是不是返回‘100 Continue’就是錯誤代碼。若是它發送‘100 Continue’響應,那麼接下來服務還會發送另一個響應。‘100 Continue’不須要頭,可是必須含有空行。以下:
HTTP/1.1 100 Continue [blank line here] [another HTTP response will go here]
不要向HTTP1.0客戶端發送‘100 Continue’
緩存是HTTP1.1的一個重大改善,同時離不開響應的時間戳。因此,服務器返回的每一個響應,必須包含Date頭,表示當前時間。格式以下:Date: Fri, 31 Dec 1999 23:59:59 GMT
除了‘1xx’的狀態碼的響應,全部的響應必須包括Date頭。時間同一用:格林威治標準時間表示。
避免發送沒必要要的資源,這樣就節省了帶寬。HTTP1.1定義了‘If-Modified-Since’和‘If-Unmodified-Since’請求頭。用於表示:「只有在這個時間以後修改過的才發送」;客戶端不需這些,可是HTTP1.1須要這些信息。
不幸的是,早期的HTTP版本,時間值的格式不統一,例如:
If-Modified-Since: Fri, 31 Dec 1999 23:59:59 GMT If-Modified-Since: Friday, 31-Dec-99 23:59:59 GMT If-Modified-Since: Fri Dec 31 23:59:59 1999
因此此次,HTTP統一使用格林威治標準時間表示。
儘管服務器必須接受三種時間格式,HTTP1.1客戶端和服務器只能生成一種時間格式。若是沒有這個頭,請求將返回不成功的狀態碼。
If-Modified-Since頭被用在GET請求上。若是請求資源在給的這段時間修改過,忽略這個頭,正常的返回資源。不然返回‘304 Not Modified’響應,包含Date頭,同時沒有消息實體。好比:
HTTP/1.1 304 Not Modified Date: Fri, 31 Dec 1999 23:59:59 GMT [blank line here]
If-Unmodified-Since頭和If-Modified-Since頭是類似的,可是不能用於任何方法。指定的請求資源只有在字段值內指定的日期時間以後,未發生更新的狀況下,才能處理請求。若是在指定日期時間後發生更新,則以狀態碼412 Precondition Failed
做爲響應返回。例如:
HTTP/1.1 412 Precondition Failed [blank line here]
HTTP1.1服務器必須支持GET和HEAD方法。若是你正在使用CGI腳本,你須要也支持POST方法。
HTTP1.1定義的其它四個方法(PUT, DELETE, OPTIONS, and TRACE),它們不常常被用到。若是客戶端請求的方法,服務器不支持,則返回‘501 Not Implemented’,以下:
HTTP/1.1 501 Not Implemented [blank line here]
爲了兼容老的瀏覽器,HTTP1.1服務器必須支持HTTP1.0請求。當一個請求使用HTTP1.0: