這篇文章暫時不研究HTTP底層的TCP/IP的握手和揮手過程,只從表面的交互流程分析HTTP協議。php
HTTP英文全稱是Hypertext Transfer Protpcol,也就是超文本傳輸協議。HTTP是一個標準,定義了Web客戶端如何與服務器對話以及數據如何從服務器傳回到客戶端。在平常開發和使用過程當中,HTTP常常被認爲是一種用於傳輸HTML文件和文件中內嵌的圖片的協議或者手段,實際上HTTP是一種通用的網絡數據傳輸格式,它的傳輸內容不只僅侷限於HTML文件或者圖片,也能夠用來傳輸Microsoft Word文檔甚至是Windows的exe文件等等,全部能夠用字節序列表示的數據均可以使用HTTP進行傳輸。html
HTTP經過TCP/IP進行數據傳輸,若是忽略底層的TCP協議的握手和揮手的細節,對於從客戶端到服務器的每個請求和請求的響應,在HTTP1.0有下面幾個步驟:java
在HTTP1.1(目前最經常使用的就是HTTP1.1)以及之後的HTTP版本中,能夠經過一個TCP鏈接連續發送多個請求和接收多個響應。也就是說,上面的1和4步驟中間的2和3步驟能夠反覆執行屢次。另外,HTTP1.1中,請求數據和響應數據能夠分塊發送,提升了擴展性。nginx
HTTP中定義了多種請求方法,用於標識當次請求須要完成什麼類型的操做,經常使用的HTTP請求方法有GET、HEAD、PUT、POST、PATCH、TRACE、OPTIONS、DELETE。ajax
HTTP請求方法 | 描述 | 是否安全 | 是否冪等 |
---|---|---|---|
GET | 一般用於請求服務器獲取某個資源 | 是 | 是 |
HEAD | 相似於GET,可是響應結果中不包含響應體,只包含協議信息和首部,一般用於測試資源是否存在或者是否被修改 | 是 | - |
POST | 客戶端向服務器提交數據(支持HTML的表單數據),可能會致使新的資源的創建或者已有資源的修改 | 否 | 否 |
PUT | 從客戶端向服務器傳送的數據取代指定的文檔的內容(所有取代) | 否 | 是 |
PATCH | 客戶端向服務器傳送的數據取代指定的文檔的內容(部分取代) | 否 | 是 |
TRACE | 回顯客戶端請求服務器的原始請求報文,用於"迴環"診斷 | 是 | - |
OPTIONS | 請求服務器獲取服務器支持的各類功能,能夠詢問服務器支持什麼類型的HTTP方法,通常用於性能測試 | 是 | - |
DELETE | 請求服務器刪除指定的資源 | 否 | 否 |
上面說到的"是否安全"的選項是"是",意味着使用該種HTTP請求方法不會發生任何數據的修改或者更新動做,也就是請求屢次也不會影響到資源的狀態。若是"是否冪等"的選項是"是",意味着使用該HTTP請求方法請求屢次HTTP調用,不管調用多少次,請求結果或者資源的狀態是同樣的(能夠理解爲只有首次調用是真正修改了資源的狀態,從第二次調用開始後面的調用只獲取到第一次調用的結果)。HTTP方法的安全性和冪等性是咱們在設計HTTP接口時候須要重點考慮的兩個因素。編程
值得注意的是:上面提到的POST和PUT方法的功能能夠理解爲相同的,二者的主要區別在於POST不是冪等的,而PUT是冪等的。在目前的Web開發中,POST方法已經被濫用,通常不多人會使用PUT,除非是推崇RESTFUL風格編程。PUT方法和PATCH方法的功能相似,都是用客戶端請求的數據去替換掉服務器中指定文檔中的內容,不過PUT方法是所有替換,而PATCH方法是部分替換。json
PS:上面的方法只是HTTP協議中的請求方法的一些規範,沒有硬性規定必定要遵循。跨域
JDK中常見的HTTP狀態碼能夠在類java.net.HttpURLConnection中找到,總結一下以下:瀏覽器
狀態碼 | 狀態碼消息 | 含義 | HttpURLConnection中的常量 | 簡單描述 |
---|---|---|---|---|
1xx | - | 信息狀態碼。 | - | 不常見,暫不考慮 |
100 | Continue | 服務器準備接受請求主體,客戶端發送請求主體;這容許客戶端在請求發送大量數據以前詢問服務器是否接受請求。 | - | 不常見,暫不考慮 |
101 | Switching Protocols | 服務器接受客戶端在Upgrade首部字段中要求改變應用的協議請求,如從HTTP轉換爲WebSockets。 | - | 不常見,暫不考慮 |
2xx | - | 表示請求成功。 | - | - |
200 | OK | 最多見的響應碼,表明請求成功。若是請求方法是GET或者POST,所請求的數據與正常的首部都包含在響應體中。若是請求方法是HEAD,則只包含首部信息。 | HTTP_OK | 處理請求成功 |
201 | Created | 服務器已經在響應體中指定的URL建立了對應的資源。客戶端如今應當嘗試加載該URL。這個響應碼只在響應POST請求時發送。 | HTTP_CREATED | 建立成功 |
202 | Accepted | 表示請求已經被處理,可是處理還沒有結束,因此不會返回任何響應數據。 | HTTP_ACCEPTED | 接受請求 |
203 | Non-Authoritative Information | 由緩存代理或者其餘本地源返回資源的表示,不能保證是最新的。 | HTTP_NOT_AUTHORITATIVE | 無權威的返回結果 |
204 | No Content | 服務器已經成功處理了該請求,可是沒有信息發回給客戶端。通常是因爲服務器上的表單處理邏輯的問題,只接收數據不返回數據。 | HTTP_NO_CONTENT | 無返回內容 |
205 | Reset Content | 服務器已經成功處理了該請求,可是沒有信息發回給客戶端。客戶端應該清除發送請求的表單信息。 | HTTP_RESET | 重置內容 |
206 | Partial Content | 服務器返回客戶端請求的資源的部份內容,而不是整個文檔。 | HTTP_PARTIAL | 部份內容 |
3xx | - | 重定向。 | - | - |
300 | Multiple Choices | 服務器爲所請求的文檔提供一組不一樣的表示。 | HTTP_MULT_CHOICE | 多重選擇 |
301 | Moved Permanently | 資源已經移動到一個新的URL。客戶端應當自動加載這個URL的資源。 | HTTP_MOVE_PERM | 永久移動 |
302 | Moved Temporarity | 資源暫時移動到一個新的URL,但其位置在不久的未來還會再次改變。 | HTTP_MOVE_TEMP | 臨時移動 |
4xx | - | 客戶端錯誤 | - | - |
400 | Bad Request | 客戶端向服務器發出的請求使用了不正確的語法。 | HTTP_BAD_REQUEST | 錯誤請求 |
401 | Unauthorized | 訪問這個URL須要身份驗證,通常是用戶名和口令。 | HTTP_UNAUTHORIZED | 未受權 |
403 | Forbidden | 服務器理解請求,可是有意拒絕進行處理。 | HTTP_FORBIDDEN | 禁止訪問 |
404 | Not Found | 最多見的錯誤響應,指示服務器找不到所請求的資源。 | HTTP_NOT_FOUND | 未找到資源 |
405 | Method Not Allowed | 請求方法不支持用於請求指定的資源。 | HTTP_BAD_METHOD | 方法禁用 |
406 | Not Acceptable | 所請求的資源不能以客戶端但願的格式提供,客戶端指望的格式由請求HTTP首部Accept字段指定。 | HTTP_NOT_ACCEPTABLE | 不接受 |
5xx | - | 服務端錯誤 | - | - |
500 | Internale Server Error | 服務器內部異常。 | HTTP_SERVER_ERROR | 服務器異常 |
501 | Not Implemented | 服務器不具有完成請求的功能。 | HTTP_NOT_IMPLEMENTED | 還沒有實現 |
502 | Bad Gateway | 服務器做爲網關或代理,從上游服務器收到無效響應。 | HTTP_BAD_GATEWAY | 錯誤網關 |
503 | Service Unavailable | 服務器暫時沒法處理請求,多是超負荷或者維護等緣由。 | HTTP_UNAVAILABLE | 服務不可用 |
簡單歸納以下:緩存
下面簡單列舉一些比較經常使用的首部以及它們的做用。
User-Agent通常做爲請求首部,用於告知服務器當前客戶端使用的是什麼瀏覽器,翻譯過來就是用戶代理,做用是容許服務器響應請求時候針對客戶端用戶代理的類型優化返回的數據或者文件。例如使用Chrome發送請求時,User-Agent以下:
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.81 Safari/537.36
Host通常做爲請求首部,用於指定接收該請求的服務器的主機名和端口號。例如:
Host: www.importnew.com
Accept通常做爲請求首部,它的做用是告知服務器它可使用或者想要什麼已經不能使用或者不想要什麼。下面是幾個Accept首部以及它們的做用:
首部 | 做用 |
---|---|
Accept | 告知服務器客戶端能夠接收和處理哪些媒體類型 |
Accept-Charset | 告知服務器客戶端能夠接收和處理哪些字符集 |
Accept-Encoding | 告知服務器客戶端能夠接收和處理哪些編碼方式 |
Accept-Language | 告知服務器客戶端能夠接收和處理哪些語言 |
Accept首部用於指定接收媒體類類型的時候,須要指定類型和子類型,這是由於媒體類型(MIME)原本就是按二級分類的,例如JPEG圖像的媒體類型是image/jpeg,類型是image,子類型是jpeg。MIME已經定義了八種頂級的類型:
舉個例子,若是客戶端只接收JSON數據:
Accept: application/json
Referer通常做爲請求首部,它提供了包含當前請求的URL的文檔的URL,也就是當前請求的上一個來源的文檔,通常用做防盜鏈。例如www.baidu.com/search?name=doge
,服務器在處理此請求的時候,須要判斷Referer是否爲www.baidu.com
,www.baidu.com/search
的上一個文檔來源必須是www.baidu.com
,不然服務器應該拒絕該請求。
Cookie通常做爲請求首部,客戶端經過它向服務器傳送一個或者多個令牌,原則上Cookie並非安全的首部,Cookie的內容也會緩存在客戶端。通常在Servlet應用中,Cookie是識別當前用戶,實現持久會話的最佳方式。從過時時間分類來看,Cookie分爲會話Cookie和持久Cookie,會話Cookie的過時時間比較短,持久Cookie的過時時間比較長或者不會過時,Cookie的過時策略等控制應該由服務端控制。因爲Cookie是直接暴露在客戶端,通常不能使用Cookie存放敏感的數據,須要存放敏感數據能夠考慮使用數據加密處理。
Cookie: uid=10086; domain="localhost"
Set-Cookie通常做爲響應首部,和Cookie對應,表示服務器設置成功的Cookie。
Cache-Control通常做爲請求首部,告知服務器對當前的請求的響應結果進行緩存相關操做。Cache-Control支持的值比較多,這裏不展開細節,常見的如no-cache
表示在沒有成功經過源站校驗的狀況下不得使用緩存,如max-age
表示響應結果須要緩存到指定的最大時間。
Content-Type是通用首部,能夠做爲請求首部或者響應首部,它的做用是告知服務器或者客戶端當前請求或者響應結果的內容(媒體)類型。
Content-Length是通用首部,能夠做爲請求首部或者響應首部,它的做用是告知服務器或者客戶端當前請求或者響應數據體的長度。
Content-Encoding通常做爲響應首部,與Accept-Encoding對應,用於服務器告知客戶端當前響應結果的內容編碼。
Content-Language通常做爲響應首部,與Accept-Language對應,用於服務器告知客戶端當前響應結果的內容語言。
Connection通常做爲請求首部,表示是否須要持久鏈接。在HTTP1.1中,若是指定爲Keep-Alive,能夠提供持久鏈接,提升Socket的複用率從而下降屢次鏈接的性能消耗。下面有一個小節專門介紹Keep-Alive。
Origin通常做爲請求首部,指明當前的請求是一個針對跨域資源共享的請求(該請求要求服務器在響應中加入一個Access-Control-Allow-Origin的消息頭,表示訪問控制所容許的來源)。
Origin: http://www.baidu.com
Access-Control-Allow-Origin通常做爲響應首部,和Origin對應,表示服務器容許的該跨域資源共享的請求來源。
Access-Control-Allow-Origin: http://www.baidu.com
Server通常做爲響應首部,用於告知客戶端服務器的相關信息。
若是採用GET請求方法,只須要向遠處服務器提供URL,URL中的路徑和查詢字符串就能夠匹配到須要查詢的資源。可是URL中沒法提供詳細的客戶端信息。另外,像POST和PUT這些請求方法所攜帶的數據體有可能比較大,沒法放在URL的查詢字符串。所以HTTP須要請求體。HTTP請求體包括下面四個部分:
文字描述可能比較抽象,用圖表示以下:
PS:space表明空格,\r\n表明換行。
舉個例子:
GET /wp-admin/admin-ajax.php?postviews_id=23996&action=postviews&_=1538708851063 HTTP/1.1 Host: www.importnew.com Connection: keep-alive Pragma: no-cache Cache-Control: no-cache Accept: */* X-Requested-With: XMLHttpRequest User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.81 Safari/537.36 Referer: http://www.importnew.com/23996.html Accept-Encoding: gzip, deflate Accept-Language: zh-CN,zh;q=0.9 postviews_id=23996&action=postviews&_=1538708851063
響應體和請求體的格式相似,主要是返回服務器的響應數據到客戶端,包括服務器的一些信息和響應數據體。HTTP響應體主要包括下面的四個部分:
文字描述可能比較抽象,用圖表示以下:
PS:space表明空格,\r\n表明換行。
舉個例子:
HTTP/1.1 200 OK Server: nginx Date: Fri, 05 Oct 2018 03:07:37 GMT Content-Type: text/html; charset=UTF-8 Transfer-Encoding: chunked Connection: keep-alive Keep-Alive: timeout=2 Vary: Accept-Encoding X-Powered-By: PHP/5.3.3 X-Robots-Tag: noindex X-Content-Type-Options: nosniff X-Frame-Options: SAMEORIGIN Content-Encoding: gzip 2995
在使用HTTP1.0的時候會爲每一個請求打開一個新的TCP鏈接,實際上,這致使了一個典型Web會話中打開和關閉全部鏈接所花費的事件遠遠大於實際傳輸數據所消耗的時間,特別是響應結果包含不少小文檔的會話。對於使用SSL或者TLS加密的HTTPS鏈接,這個問題更加嚴重,由於創建一個安全的Socket的握手過程遠比創建常規的Socket須要更多的工做。
在HTTP1.1和後面的版本中,服務器沒必要在返送響應以後就關閉鏈接。已經創建的鏈接能夠保持打開,在同一個Socket上等待來自客戶端的新請求。簡單來講,就是能夠在一個TCP鏈接上連續發送多個請求和連續進行多個請求的響應。
客戶端能夠在HTTP請求首部中添加一個Connection請求頭,指定值爲Keep-Alive,這樣就能實現Socket的重用:
Connection: Keep-Alive
HTTP1.1或者以後的版本,Keep-Alive是默認開啓的,不須要顯式指定,若是須要關閉能夠設置爲close:
Connection: close
一旦開啓了Keep-Alive,服務器在關閉一個Socket鏈接以前,若是有新的客戶端再次鏈接到服務器,那麼就是重用Socket。在JDK中能夠經過系統屬性來控制若是使用HTTP的Keep-Alive:
不少網站使用一些小文本串在鏈接之間存儲持久的客戶端狀態,這些小文本串稱爲Cookie(中文翻譯爲:小甜點)。Cookie在請求和響應的首部從服務器傳到客戶端,再從客戶端傳回服務器,服務器使用Cookie來指示sessionID、購物車內容、登陸憑據等。
除了簡單的name=value
對,Cookie能夠有多個屬性來控制它們的做用域,包括過時日期、路徑、域、端口、版本和安全選項。
JDK中java.net.CookieStore類提供了對Cookie的增刪查操做,它的默認實現是java.net.InMemoryCookieStore,若是實現CookieStore,JDK中的Cookie默認是存放在內存中的。另外,java.net.CookieManager內部持有CookiePolicy和CookieStore,定義了一系列管理Cookie的方法,通常經過CookieManager操做Cookie,固然也能夠經過實現CookieStore,覆蓋默認的CookieManager來實現Cookie的自定義管理。
(本文完 c-2-d e-20181005)