Java網絡編程-HTTP協議

HTTP協議的定義

這篇文章暫時不研究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

  • 一、默認狀況下,客戶端在端口80開啓與服務器的一個TCP鏈接,固然也能夠指定其餘的端口。
  • 二、客戶端向服務器發送消息,請求指定路徑上的資源。一個HTTP請求包括一個首部,可選項包括一個空行和此次請求的數據。
  • 三、服務器向客戶端發送響應。響應以響應碼開頭,接着是包含元數據的首部,可選項包括一個空行以及所請求的文檔數據或者錯誤信息。
  • 四、服務器關閉TPC鏈接。

在HTTP1.1(目前最經常使用的就是HTTP1.1)以及之後的HTTP版本中,能夠經過一個TCP鏈接連續發送多個請求和接收多個響應。也就是說,上面的1和4步驟中間的2和3步驟能夠反覆執行屢次。另外,HTTP1.1中,請求數據和響應數據能夠分塊發送,提升了擴展性。nginx

HTTP請求方法

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協議中的請求方法的一些規範,沒有硬性規定必定要遵循。跨域

常見的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 服務不可用

簡單歸納以下:緩存

  • 響應碼100-199表示一個提供信息的響應。
  • 響應碼200-299表示請求成功。
  • 響應碼300-399表示重定向。
  • 響應碼400-499表示一個客戶端引起的錯誤。
  • 響應碼500-599表示一個服務器引起的錯誤。

常見的HTTP首部

下面簡單列舉一些比較經常使用的首部以及它們的做用。

User-Agent

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通常做爲請求首部,用於指定接收該請求的服務器的主機名和端口號。例如:

Host: www.importnew.com

Accept

Accept通常做爲請求首部,它的做用是告知服務器它可使用或者想要什麼已經不能使用或者不想要什麼。下面是幾個Accept首部以及它們的做用:

首部 做用
Accept 告知服務器客戶端能夠接收和處理哪些媒體類型
Accept-Charset 告知服務器客戶端能夠接收和處理哪些字符集
Accept-Encoding 告知服務器客戶端能夠接收和處理哪些編碼方式
Accept-Language 告知服務器客戶端能夠接收和處理哪些語言

Accept首部用於指定接收媒體類類型的時候,須要指定類型和子類型,這是由於媒體類型(MIME)原本就是按二級分類的,例如JPEG圖像的媒體類型是image/jpeg,類型是image,子類型是jpeg。MIME已經定義了八種頂級的類型:

  • text/*表示人可讀的文字。
  • image/*表示圖片。
  • model/*表示3D模型,如VRML文件。
  • audio/*表示音頻。
  • video/*表示多媒體圖片、視頻,也多是音頻。
  • application/*表示二進制數據。
  • message/*表示協議特定的信封,如Email消息和HTTP響應。
  • muitipart/*表示多個文檔和資源的容器。

舉個例子,若是客戶端只接收JSON數據:

Accept: application/json

Referer

Referer通常做爲請求首部,它提供了包含當前請求的URL的文檔的URL,也就是當前請求的上一個來源的文檔,通常用做防盜鏈。例如www.baidu.com/search?name=doge,服務器在處理此請求的時候,須要判斷Referer是否爲www.baidu.comwww.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通常做爲請求首部,告知服務器對當前的請求的響應結果進行緩存相關操做。Cache-Control支持的值比較多,這裏不展開細節,常見的如no-cache表示在沒有成功經過源站校驗的狀況下不得使用緩存,如max-age表示響應結果須要緩存到指定的最大時間。

Content-Type

Content-Type是通用首部,能夠做爲請求首部或者響應首部,它的做用是告知服務器或者客戶端當前請求或者響應結果的內容(媒體)類型。

Content-Length

Content-Length是通用首部,能夠做爲請求首部或者響應首部,它的做用是告知服務器或者客戶端當前請求或者響應數據體的長度。

Content-Encoding

Content-Encoding通常做爲響應首部,與Accept-Encoding對應,用於服務器告知客戶端當前響應結果的內容編碼。

Content-Language

Content-Language通常做爲響應首部,與Accept-Language對應,用於服務器告知客戶端當前響應結果的內容語言。

Connection

Connection通常做爲請求首部,表示是否須要持久鏈接。在HTTP1.1中,若是指定爲Keep-Alive,能夠提供持久鏈接,提升Socket的複用率從而下降屢次鏈接的性能消耗。下面有一個小節專門介紹Keep-Alive。

Orgin

Origin通常做爲請求首部,指明當前的請求是一個針對跨域資源共享的請求(該請求要求服務器在響應中加入一個Access-Control-Allow-Origin的消息頭,表示訪問控制所容許的來源)。

Origin: http://www.baidu.com

Access-Control-Allow-Origin

Access-Control-Allow-Origin通常做爲響應首部,和Origin對應,表示服務器容許的該跨域資源共享的請求來源。

Access-Control-Allow-Origin: http://www.baidu.com

Server

Server通常做爲響應首部,用於告知客戶端服務器的相關信息。

HTTP請求體

若是採用GET請求方法,只須要向遠處服務器提供URL,URL中的路徑和查詢字符串就能夠匹配到須要查詢的資源。可是URL中沒法提供詳細的客戶端信息。另外,像POST和PUT這些請求方法所攜帶的數據體有可能比較大,沒法放在URL的查詢字符串。所以HTTP須要請求體。HTTP請求體包括下面四個部分:

  • 一、一個起始請求行,包括HTTP方法、路徑、查詢字符串以及HTTP版本。
  • 二、HTTP請求的首部。
  • 三、一個空行(兩個連續的回車或者換行對)。
  • 四、請求數據體。

文字描述可能比較抽象,用圖表示以下:

http-1

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響應體

響應體和請求體的格式相似,主要是返回服務器的響應數據到客戶端,包括服務器的一些信息和響應數據體。HTTP響應體主要包括下面的四個部分:

  • 一、一個起始響應行,包括HTTP版本、狀態碼、狀態碼描述。
  • 二、HTTP響應的首部。
  • 三、一個空行(兩個連續的回車或者換行對)。
  • 四、響應數據體。

文字描述可能比較抽象,用圖表示以下:

http-2

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

Keep-Alive

在使用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:

  • http.keepAlive:默認值爲true,默認開啓HTTP的Keep-Alive。
  • http.maxConnections:同時保持打開的Socket數量的最大值,默認值爲5。
  • http.keepAlive.remainingData:默認值爲false,若是設置爲true,則JDK在丟棄鏈接以後會完成剩餘數據的清理。
  • sun.net.http.errorstream.enableBuffering:默認值爲false,若是設置爲true,則嘗試緩存400和500狀態碼的相對小的錯誤流,從而能釋放鏈接以備後續使用。
  • sun.net.http.errorstream.bufferSize:爲緩存錯誤流的緩衝區的字節大小,默認值爲4096字節,只有上一項爲true的時候纔有意義。
  • sun.net.http.errorstream.timeout:默認值爲300ms,讀取錯誤流超時的毫秒數。

Cookie和Cookie管理

不少網站使用一些小文本串在鏈接之間存儲持久的客戶端狀態,這些小文本串稱爲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)

相關文章
相關標籤/搜索