Java 基礎(十六)網絡編程

寫了這麼久 Java 代碼,對網絡編程的瞭解還停留在簡單使用網絡請求框架的階段。
提及網絡編程的知識點,好像大部分的東西也都知道,可是好像就知道一個專有名詞的意思。好比說:
「URL 是什麼?」
「URL 就是統一資源定位器」
「嗯?完了?」
「它就是一個專有名詞啊」
「...」html

再好比說:
「你瞭解 http 嗎?」
「哦,這個呀,我知道,超文本傳輸協議呀」
「嗯?還有嗎?」
「...」
「那 https 呢?」
「這就是一個安全的超文本傳輸協議呀」
"..."java

網絡編程嘛,說白了就是和服務器的一次通話/交互資源,提及來其實很簡單,用起來好像也挺簡單的。
如下是使用 PostMan 自動生成的一次網絡請求代碼,和咱們項目中的代碼基本上差很少。node

OkHttpClient client = new OkHttpClient();

Request request = new Request.Builder()
  .url("http://gank.io/api/history/content/2/1")
  .get()
  .addHeader("cache-control", "no-cache")
  .addHeader("postman-token", "c9b415ce-4a35-097c-1a53-b12d526d8d97")
  .build();

Response response = client.newCall(request).execute();複製代碼

這個過程真的很簡單了,由於這特麼是已經封裝好的OkHttp,那麼問題來了,這些參數到底都表示什麼意思?參數都是以什麼樣的格式上傳給服務器的?服務器怎麼響應的?服務器響應的數據是什麼格式?緩存、Token、性能優化、異常怎麼處理?
今天,就和你們一塊兒來探討這些問題。
在探討這些問題以前,得先作一些準備工做,首先咱們來學習如下幾個知識點。android

  • Internet 地址
  • URL 和 URI
  • HTTP
  • URLConnection
  • HttpURLConnection

Internet 地址

原本標題是想用 IP 的,查了一下發現IP 是Internet Protocol(網絡協議)的縮寫,這個話題太大了,咱們簡單瞭解一下 Internet 地址便可。chrome

咱們將全部鏈接到 Internet 的設備看作一個節點(node),計算機節點稱爲主機(host)。每一個節點或主機都由至少一個惟一的數來標識,這稱爲 Internet 地址,也就是咱們所說的 IP 地址。編程

IPv4和 IPv6

就是4個字節長度的ip 地址和6個字節長度的 ip 地址。
IPv6目前還沒在世界範圍內推廣,你們記住由於 IPv4地址緊張不夠用才引入 IPv6的概念便可。
好比 61.135.169.121就是百度首頁服務器的 ip 地址。json

域名解析 DNS

剛剛我說了,61.135.169.121是百度服務器的地址,可是咱們訪問百度的時候用的是 www.baidu.com,這個網址叫域名,一般訪問的時候使用這個域名進行訪問,訪問的過程當中會去 DNS 服務器查找域名「www.baidu.com」所對應的 IP 地址「61.135.169.121」,而後經過 IP 地址訪問服務器。設計模式

InetAddress

java.net.InetAddress 類是 Java 對 IP地址(包括 v4和 v6)的高層表示。大多數其餘網絡都要用到這個類,包括 Socket、ServerSocket、URP、DatagramSocket、DatagramPacket 等。通常來講,它包括一個主機名和一個 IP 地址。
具體 api 就不細講了,反正和 IP地址相關的問題,均可以來找這個類。
子類實現有 Inet4Address 和 Inet6Address。
大多數狀況下,咱們不用考慮 v4仍是 v6,由於 v6還沒普及,從代碼層面來講,用 v4仍是 v6,Java 已經幫咱們處理好了,做爲應用層(OSI 模型的最高層)的咱們不須要關心這些。api

URL 和 URI

在上面咱們了街道如何經過主機名和 IP 地址肯定主機在 Internet 的地址(其實就是調用 InetAddress 的 api)。這裏咱們繼續學習如何肯定資源的地址。瀏覽器

HTML 是一個超文本標記語言,由於它提供了一種方法,能夠知道 URL 標識的其餘文檔的連接。URL 能夠惟一地標識一個資源在 Internet 上的位置。URL 是最多見的 URI(統一資源標識符)。URI 能夠由資源的網絡位置來標識資源,也能夠由資源的名字、編號或其餘特性來標識。

URL 類是 Java 程序在網絡上定位和獲取數據最簡單的方法。你不須要考慮所使用協議的細節,也不用擔憂如何與吳福氣通訊。只要把 URL 告訴 Java,它就會爲你得到數據。

URI 和 URL 的區別

統一資源標誌符URI就是在某一規則下能把一個資源獨一無二地標識出來。拿人作例子,假設這個世界上全部人的名字都不能重複,那麼名字就是URI的一個實例,經過名字這個字符串就能夠標識出惟一的一我的。
現實當中名字固然是會重複的,因此身份證號纔是URI,經過身份證號能讓咱們能且僅能肯定一我的。
那統一資源定位符URL是什麼呢。也拿人作例子而後跟HTTP的URL作類比,就能夠有:動物住址協議://地球/中國/浙江省/杭州市/西湖區/某大學/14號宿舍樓/525號寢/張三.人能夠看到,這個字符串一樣標識出了惟一的一我的,起到了URI的做用,因此URL是URI的子集。
URL是以描述人的位置來惟一肯定一我的的。在上文咱們用身份證號也能夠惟一肯定一我的。對於這個在杭州的張三,咱們也能夠用:身份證號:123456789來標識他。因此不管是用定位的方式仍是用編號的方式,咱們均可以惟一肯定一我的,都是URI的一種實現,而URL就是用定位的方式實現的URI。回到Web上,假設全部的Html文檔都有惟一的編號,記做html:xxxxx,xxxxx是一串數字,即Html文檔的身份證號碼,這個能惟一標識一個Html文檔,那麼這個號碼就是一個URI。而URL則經過描述是哪一個主機上哪一個路徑上的文件來惟一肯定一個資源,也就是定位的方式來實現的URI。對於如今網址我更傾向於叫它URL,畢竟它提供了資源的位置信息,若是有一天網址經過號碼來標識變成了http://741236985.html,那感受叫成URI更爲合適,不過這樣子的話還得想辦法找到這個資源咯…

以上,摘抄自知乎。接下來,咱們來看看 URL 和 URI 的 api 吧

URI

如下,是從URI 源碼裏面摘選出來的九個重要字段

private transient String scheme;//方案
private transient String fragment;//特定於方案的部分
private transient String authority;//受權
private transient String userInfo;//用戶信息
private transient String host;//主機
private transient int port = -1;//端口
private transient String path;//路徑
private transient String query;//查詢
private volatile transient String schemeSpecificPart;//片斷複製代碼

可能你們仍是不太理解 URI 是啥,其實就是將一個String地址分解成特定的屬性。看看下圖就能明白了~

URL

直接看圖吧~~

URL 的 api 基本和 URI 差很少,大多都是構造方法和各個字段的get、set 方法。可是有個方法咱們須要注意一下。

  • public URLConnection openConnection()

URL 能夠根據咱們傳的參數直接建立一個通訊連接,咱們來簡單看看是怎麼建立的。

public URLConnection openConnection() throws java.io.IOException {
    return handler.openConnection(this);
}複製代碼

在 URL 類裏面找到 handler 的建立代碼以下:

if (handler == null) {
    try {
            if (protocol.equals("file")) {
                handler = (URLStreamHandler)Class.forName("sun.net.www.protocol.file.Handler").newInstance();
            } else if (protocol.equals("ftp")) {
                handler = (URLStreamHandler)Class.forName("sun.net.www.protocol.ftp.Handler").newInstance();
            } else if (protocol.equals("jar")) {
                handler = (URLStreamHandler)Class.forName("sun.net.www.protocol.jar.Handler").newInstance();
            } else if (protocol.equals("http")) {
                handler = (URLStreamHandler)Class.forName("com.android.okhttp.HttpHandler").newInstance();
            } else if (protocol.equals("https")) {
                handler = (URLStreamHandler)Class.forName("com.android.okhttp.HttpsHandler").newInstance();
            }
        } catch (Exception e) {
            throw new AssertionError(e);
        }
    }複製代碼

這裏根據 protocol 字段,建立了不一樣的 handler,根據邏輯咱們能夠看到建立了 HttpHandler,使用 HttpHandler 的 openConnection()方法建立了一個sun.net.www.protocol.http.HttpURLConnection 對象。

HTTP

超文本傳輸協議(Hypertext Transfer Protocol,HTTP)是一個標準,定義了客戶端如何與服務器對話,以及數據如何從服務器傳回客戶端。儘管一般認爲 HTTP是一種傳輸 HTML 文件以及文件中內嵌圖片的方法,但實際上 HTTP 是一個數據格式。它能夠用來傳輸 TIFF 圖片、Word 文檔、exe 文件等。這裏咱們將深刻後臺,瞭解當咱們子安地址欄輸入http://www.google.com並按下回車鍵時到底發生了什麼。

HTTP 協議

HTTP 是客戶端和服務器之間通訊的標準協議。HTTP 指定客戶端與服務器如何創建鏈接、客戶端如何從服務器請求數據,服務器如何響應請求,以及最後如何關閉鏈接。HTTP 鏈接使用 TCP/IP 來傳輸數據。對於從客戶端到服務器的每個請求,都有4個步驟:

1.默認狀況下,客戶端在端口80打開與服務器的一個 TCP 鏈接,URL 中還能夠指定其餘端口。
2.客戶端向服務器發生消息,請求指定路徑上的資源。這個請求包括一個首部,可選的(取決於請求的性質)還能夠有一個空行,後面是這個請求的數據。
3.服務器向客戶端發生響應,響應以響應碼開頭,後面是包含數據的首部、一個空行以及所請求的文檔或錯誤消息。
4.服務器關閉鏈接。

這是基本 HTTP1.0過程,在 HTTP1.1以及之後的版本中,能夠經過一個 TCP 鏈接連續發送多個請求和響應。也就是說,在第一步和第四步直接,第二步和第三步能夠反覆屢次。另外,在 HTTP1.1中,請求和響應能夠分爲多個塊發送。這樣有更好的擴展性。

HTTP Request

每一個請求和響應都有一樣的基本形式:一個首部行、一個包含元素數據的 HTTP 首部、一個空行,而後是一個消息體。

格式以下:

  • 請求行,分爲三部分:請求方法、請求地址、協議版本

例如 GET/image/logo.gif HTTP/1.1,表示從/image目錄下請求 logo.gif這個文件

  • 請求頭,用於傳遞一些附加信息

常見通用請求 Header

名稱 做用
Content-Type 請求體的類型,如:text/plain,application/json
Accept 說明接收的類型,能夠多個值,用","隔開
Content-Length 請求體長度
Content-Encoding 請求體的編碼格式,如 gzip、deflate
Accept-Encoding 告知對方我方接受的 Content-Encoding
Catche-Control 取值通常爲 no-catche、max-age=xx(xx 爲整數,表示自願緩存 xx 秒)
  • 空行 請求頭結束的標識符
  • 可選消息體

也就是三個部分,分別是:請求行、請求頭、請求正文

請求方法:

HTTP/1.1協議中一共定義了八種方法來表面 Request-URI指定的資源的不一樣操做方式:OPTIONS、HEAD、GET、POST、PUT、DELETE、TRACE、CONNECT

這八種方法裏面,如今咱們通常都只有 GET 和 POST 方法。

GET 和 POST 的區別:
1.GET 提交的數據會放在 URL 以後,以?分割 URL 和傳輸數據,參數之間以&相連。
2.GET 提交的數據大小有限制,最多隻能有1024字節,而 POST 方法提交的數據沒有限制
3.GET 方式須要使用 Requ.QueryString來取得變量的值,而 POST 方式經過 Request.Form來獲取值
4.Get 方式提交數據,會帶來安全問題,好比一個登錄頁面,經過 GET 方式提交數據時,用戶名和密碼將出如今 URL 上,若是頁面能夠被緩存或者其餘人能夠訪問這臺機器,就能夠從歷史記錄獲取該用戶的帳號和密碼。

HTTP Response

當客戶端向服務器發生一個請求,服務器以一個狀態做爲相應,相應的內容包括:消息協議的版本、成功或者錯誤編碼、服務器信息、實體元信息以及必要的實體內容。根據響應類別,服務器響應能夠包含實體內容,但不是全部的響應都應有實體內容。

響應消息的結構也分爲三部分

  • 響應狀態行
    主要包含 HTTP 版本信息(通常是1.1)和狀態碼,狀態碼對於信息以下所示:
狀態碼 對應信息
1xx 提示信息-表示請求已接受,繼續處理
2xx 用於表示請求已經被成功接收、處理、
3xx 用於表示自願被永久轉到其餘 URL,也就是重定向
4xx 客戶端的錯誤-請求有語法錯誤或沒法實現
5xx 服務器端錯誤-服務器未能實現合法的請求
  • 響應頭

響應頭一樣可用於傳遞一些附加信息。
常見響應 Header

名詞 做用
Date 服務器日期
Last-Modified 最後被修改時間
Transfer-Encoding 取值通常爲 chunked,通常出如今響應體長度不能肯定的狀況下
Set-cookie 設置 Cookie
Location 重定向到另外一個 URL
Server 後臺服務器
  • 響應體

響應體也就是咱們須要的內容,通常在相應頭中會用 Content-Length來明確相應體的長度,便於瀏覽器接受,對於大數據量的正文消息,也會使用 chunked 的編碼方式。

HTTPS

簡介

HTTPS(Hypertext Transfer Protocol over Secure Socket Layer),是以安全爲目標的 http 通道,簡單的講就算 HTTP 的安全版。即HTTP 下加入 SSL 層,HTTPS 的安全基礎是 SSL,所以加密的詳細內容就須要 SSL。

HTTPS 和 HTTP 的區別

1.https 須要到 ca 申請證書,通常免費證書不多,須要交費。
2.http 是超文本傳輸協議,信息是明文傳輸;https 則是具備安全性的 ssl 加密傳輸協議。
3.http 和 https 室友徹底不一樣的鏈接方式,用的端口也不同,前者是80,後者是443.
4.http 的連接很簡單,無狀態的。https 協議是由ssl+http 協議構建的可進行加密傳輸、身份認證的網絡協議,比 http 協議安全。

https 的做用

它的主要做用能夠分爲兩種:一種是創建一個信息安全通道,來保證數據傳輸安全;另外一種是確認網站的真實性。

1.通常意義上的 https,就是服務器有一個證書。主要目的是保證服務器就是他聲稱的服務器,這個跟第一點同樣;服務端和客戶端之間的全部通信,都是加密的。
2.具體講,是客戶端產生的一個對稱的祕鑰,經過服務器的證書來交換祕鑰,即通常意義上的握手過程。
3.接下來全部的信息往來都是加密的。即便第三方截取,也沒有任何意義,由於他沒有祕鑰,固然篡改也就沒有什麼意義了。
4.少量對客戶端有妖氣的狀況下,會要求客戶端也必須有一個證書。

這裏的客戶端證書,其實就相似表示我的信息的時候,除了用戶名/密碼,還有一個 CA 認證過的身份。由於我的證書通常來講是別人沒法模擬的,全部這樣可以更深的確認本身的身份。少數我的銀行的專業版是這種作法,好比 U 盾。

SSL 就不講了吧,涉及的東西太多了。

URLConnection

URLConnection 是一個抽象類,表示指向 URL 指定資源的活動鏈接。URLConnection 有了兩個不一樣但相關的用途。首先,與 URL 類相比,它對與服務器(特別是 HTTP 服務器)的交互提供了更多的控制。URLConnection 能夠檢查服務器發送的首部,並相應地作出響應。它能夠設置客戶端請求中使用的首部字段。最後,URLConnection 能夠用 POST、PUT 和其餘 HTTP 請求方法向服務器發生數據。

打開 URLConnection

直接使用URLConnection 類的程序遵循如下基本步驟:

1.構造一個 URL 對象
2.調用這個 URL 對象的 openConnection()獲取一個對應該 URL 的 URLConnection 對象。(如 http 請求獲取的是 HttpURLConnection)
3.配置這個 URLConnection
4.讀取首部字段
5.獲取輸入流並讀取數據
6.獲取輸出流並寫入數據
7.關閉鏈接

並不必定執行全部這些步驟。例如,若是某種 URL 的默認設置是能夠接受的,那麼可能會跳過步驟3。若是隻須要服務器的數據,不關心任何元信息,或者協議不提供任何元信息,那麼能夠跳過步驟4。若是隻但願接受服務器的數據,而不向服務器發生數據,就能夠跳過步驟6.依據不一樣協議,步驟5和6的順序可能會反過來或者交替出現。

URLConnection 類僅有一個構造函數爲保護類型。

protected RULConnection(RUL url)複製代碼

所以,除非派生 URLConnection 的子類來處理新的 URL 類型,不然要經過調用 URL 類的 openConnection()方法來建立這樣一個對象。

try{
    URL u = new URL("http://www.baidu.com");
    URLConnection uc = u.openConnection();
}catch(Exception e){
    System.out.println(e);
}複製代碼

URLConnection類聲明爲抽象類。不過,除了一個方法外,其他方法都已經實現。你會發現覆蓋這個類的其餘方法很方便,或者可能頗有必要。必須由子類實現的一個方法是 connect(),他創建與服務器的鏈接,於是依賴於服務類型(HTTP,FTP 等)。例如 sun.net.www.protocol.http.HttpURLConnection的 connect()方法會建立一個sun.net.www.http.HttpClient對象,由他負責鏈接服務器。

第一次構造 URLConnection 時,它是未鏈接的。也就是說,本地和原創主機沒法發送和接收數據。沒有 sockey 鏈接這兩個主機。connect()方法在本地和遠程主機之間創建一個鏈接(通常使用 TCP socket,但也可能經過其餘機制來創建),這樣就能夠收發數據了,不過,對於 getInputStream()、getContent()、getHeaderFiled()和其餘要求打開鏈接的方法,若是鏈接還沒有打開,他們就會調用 connect()。所以,你不多須要直接調用 connect()。

讀取服務器的數據

下面是使用 URLConnection 對象從一個 URL 獲取數據所需的最起碼的步驟:

1.構造一個 URL 對象。
2.調用這個 URL 對象的 openConnection()方法,獲取對應的 URLConnection。
3.調用這個 URLConnection 的 getInputStream()方法
4.使用一般的流 API 讀取輸入流。

getInputStream() 方法返回一個通用的 InputStream,能夠讀取和解析服務器發送的數據。如下示例使用 URLConnection 下載一個 Web 頁面:

對於這個例子中這樣一個簡單的輸入流,URL 和 URLConnection 直接的區別並不明顯。
這兩個類的最大不一樣在於:

  • URLConnection 提供了對 HTTP 首部的訪問
  • URLConnection 能夠配置發送給服務器的請求參數
  • URLConnection 除了讀取服務器數據外,還能夠向服務器寫入數據

讀取首部

HTTP 服務器在每一個響應前面的首部中提供了大量信息。例如,下面是一個服務器返回的典型的 HTTP 首部:

bdpagetype →1
bdqid →0xef0ab4fd0001728d
bduserid →0
cache-control →private
connection →Keep-Alive
content-encoding →gzip
content-type →text/html; charset=utf-8
cxy_all →baidu+631df64ada7e1077f47313160f7a4090
date →Thu, 16 Nov 2017 08:20:15 GMT
expires →Thu, 16 Nov 2017 08:19:54 GMT
p3p →CP=" OTI DSP COR IVA OUR IND COM "
server →BWS/1.1
strict-transport-security →max-age=172800
transfer-encoding →chunked
vary →Accept-Encoding
x-powered-by →HPHP
x-ua-compatible →IE=Edge,chrome=1複製代碼

這裏有大量信息,haha,我不少都不認識,具體字段可對照我上文列的那個響應頭字段表。

URLConnection 有幾個獲取經常使用響應頭字段的方法

  • getContentType() 獲取響應主體的 MIME 內容類型
  • getContentLength() 獲取內容中有多少字節
  • getContentEncoding()獲取內容的編碼格式
  • getDate() 獲取內容返回時間
  • getLastModified() 獲取最近修改時間
  • getExpires() 表示文檔過時時間,過時後須要從新下載
  • getHeaderField(String name)獲取首部的任意字段

緩存

Web 瀏覽器多年來一直在緩存頁面和圖片。若是一個 logo 在網站的每個頁面上重複出現,瀏覽器通常只會從遠程服務器上加載一次,將它保存在緩存上,每次須要的時候會從緩存從新加載,而不是每次遇到這個 logo 都請求遠程服務器加載。一些 HTTP 首部(包括 Expires 和 Cache-control)能夠控制緩存。

默認狀況下,通常認爲使用 GET 經過 HTTP 訪問的頁面能夠緩存,也應當緩存。使用 HTTPS 或 POST 訪問的頁面不該緩存。不過 HTTP 首部能夠對此做出調整:

  • Expires 首部(主要針對 HTTP1.0)指示能夠緩存這個資源表示,直到指定的世界爲止。
  • Catche-control 首部(HTTP1.1)提供了細粒度的緩存策略:

    • max-age=[seconds]:從如今直到緩存項過時以前的秒數
    • s-maxage=[seconds]:從如今起,直到緩存項在共享緩存中過時以前的秒數。私有緩存能夠將緩存項保存更長時間
    • public:能夠緩存一個通過認證的響應。不然已認證的響應不能緩存。
    • private:僅耽擱用戶緩存能夠保存響應,而共享緩存不該保存。
    • no-catch:這個策略的做用與名字不太一致。緩存項仍然能夠緩存,不過客戶端在每次訪問時要用一個 Etag 或 Last-modified 首部從新驗證響應的狀態。
    • no-store:無論怎樣都不緩存。

      若是 Catch-control 和 Expires 首部都出現,Cache-control 會覆蓋 Expires。服務器能夠在一個首部中發送多個 Cache-control 首部,只要它們沒有衝突

  • Last-modified 首部指示資源最後一次修改的日期。客戶端能夠用一個 HEAD 請求來檢查這個日期,只要當本地緩存的副本遭遇 Last-modified 日期時,它纔會真正執行 GET 來獲取資源。

  • Etag 首部(HTTP1.1)是資源改變時這個資源的位移標識符。客戶端可使用一個 HEAD 請求來檢查這個標識符,只有當本地緩存的副本有一個不一樣的 Etag 時,它纔會真正執行 GET 來獲取資源。

配置鏈接

URLConnection 類有7個保護的實例字段,定義了客戶端如何向服務器作出請求。這些字段包括

protected URL url;
protected boolean doInpt = true;
protected boolean doOutput = false;
protected boolean allowUserInteraction = defaultAllowUserInteraction;
protected useCaches = defaultUserCachesl;
protected long ifModifiedSince = 0;
protected boolean connected = false;複製代碼

例如,若是 doOutput 爲 true,那麼除了經過 URLConnection 讀取數據外,還能夠經過將數據寫入到服務器。若是useCaches爲 false,鏈接會染過全部本地緩存,從新從服務器下載文件。

因爲這些字段是保護字段,因此要讀寫這些字段只能經過 get、set 方法。

只能在 URLConnection 鏈接以前修改這些字段。對於設置字段的方法,若是調用這些方法時鏈接已經打開,大多數會拋出一個 IllegaStateException 異常。通常狀況下,只能在鏈接打開以前設置 URLConnection 對象的屬性。

  • protected URL url

url 字段制定了這個 URLConnection 鏈接的 URL。構造函數會在建立 URLConnection 時設置這個字段,此後不能再改變。能夠經過調用 getURL()方法獲取這個字段的值。

  • protected boolean connected

若是鏈接已經打開,boolean 字段 connected 爲 true,若是鏈接關閉,這個字段則爲 false。因爲在建立一個新 URLConnection 對象時鏈接還沒有打開,因此初始值爲 false。

  • protected boolean allowUserInteraction

有些 URLConnection 須要與用戶交互。例如,Web 瀏覽器可能須要用戶名和口令。不過,不少應用程序不能假定真實存在一個能夠與它交互的用戶。不過好像咱們 Android 開發用不上。

  • protected boolean doInput

URLConnection 能夠用於讀取服務器、寫入服務器,或者同時用於讀/寫服務器。若是 URLConnection 能夠用來讀取,保護類型 boolean 字段 doInput 就爲 true,不然爲 false。默認值是 true。

  • protected boolean doOutput

程序能夠用 URLConnection 將輸出發回服務器。例如,若是程序須要使用 POST 方法向服務器發生數據,能夠經過從 URLConnection 獲取輸出流來完成。若是 URLConnection 能夠用於寫入,字段 doOutput 就爲 true,不然爲 false。

  • protected boolean ifModifySince

許多客戶端會保留之前獲取的文檔緩存。若是用戶再次要求相同的文檔,能夠從緩存中獲取。不過,在最後一次獲取這個文檔以後,服務器上的文檔可能會改變。要判斷是否有變化,惟一的辦法就是詢問服務器。客戶端能夠在戶請求 HTTP 首部中包括一個If-MOdified_since。這個首部包括一個日期和時間。若是文檔在這個時間以後有所修改,服務器就發送該文檔,不然就不發生。通常狀況下,這個世界是客戶端最後得到文檔的時間。

  • protected boolean useCaches

有些客戶端能夠從本地緩存獲取文檔,而不是從服務器獲取。applet 能夠訪問瀏覽器的緩存。獨立的應用程序能夠用 java.net.ResponseCache類。若是有緩存,useCaches 變量肯定了是否可使用緩存。默認值爲 true,表示將使用緩存。

超時

public void setConnectionTimeout()

調用這個方法設置鏈接超時。

配置客戶端請求 HTTP 首部

HTTP 客戶端(如瀏覽器)向服務器發生一個請求行和一個首部。

服務器能夠根據請求信息向客戶端返回不一樣的數據,獲取和設置cookie,經過口令認證用戶等。經過再客戶端發送和服務器響應的首部中放置不一樣的字段,就能夠完成這些工做。

  • public void setRequestProperty(String name,String value)

setRequestProperty()方法指定的名和值爲這個 URLConnection 的首部加一個字段。這個方法只能在鏈接打開以前使用。重複調用這個方法會覆蓋以前的字段。若是要增長一對,須要使用 addRequestProperty()方法。

並無一個固定的合法首部列表。服務器通常會忽略沒法識別的首部。HTTP 確實對首部字段的名和值有一些限制。例如,名不能包含空白符,值不能包含任何換行符。若是一個字段包含換行符,會拋出 IllegalArgumentException 異常。

向服務器寫入數據

有時候須要向URLConnection 寫入數據,例如,使用 POST 向服務器提交表單,或者使用 PUT 上傳文件。getOutputStream()方法返回一個 OutputStream,能夠用來寫入數據傳給服務器:

public OutputStream getOutputStream()複製代碼

因爲 URLConnection 在默認狀況下不容許輸出,因此在請求輸入流以前必須調用 setDooutput(true)。爲一個 http URL 將 doOutput 設置爲 true 時,請求方法將由GET 變爲 POST。

猜想 MIME 媒體類型

  • public static String guessContentTypeFromName(String var0)

經過文件擴展名判斷,通常來講沒有問題

  • public static String guessContentTypeFromStream(InputStream var0)

經過嘗試查看流中前幾個字節來猜想內容類型,猜想結果一般沒有根據擴展名猜想靠譜。

HttpURLConnection

java.net.HttpURLConnection類是 URLConnection 的抽象子類。它提供了另一些方法,在處理 http RUL 時尤爲有幫助。具體的,它包含的方法能夠得到和設置請求方法、肯定是否重定向、獲取響應碼和消息,以及肯定是否使用了代理服務器。

請求方法

當客戶端聯繫一個服務器時,它發送的第一個內容是請求行。通常狀況下,這一行以 GET 開頭,後面是客戶端但願獲取的資源路徑,以及 HTTO 版本。

一般狀況下,默認請求方法是 GET,能夠經過如下方法進行修改

  • public void setRequestMethod(String method)

請求的方法有 GET、POST、HEAD、PUT、DELETE、OPTIONS、TRACE,通常咱們 Android 都只用 GET 和 POST,可是如今好像後臺都比較喜歡統一用 POST 了。

斷開與服務器的鏈接

HTTP1.1支持持久鏈接,容許一個 TCP socket 發送多個請求和響應。服務器不會由於已經向客戶端發送了最後一個字節的數據就當即關閉鏈接。也就是說,在服務器關閉鏈接以前,若是再鏈接同一個服務器,它會重用 socket。手動調用 disconnect()方法會主動斷開鏈接,若是還有打開的流,也會關閉。可是反過來關閉打開的流,並不會關閉鏈接。

處理服務器響應

前面咱們介紹過服務器響應的格式,這裏就再也不贅述。

  • getResponseCode() 獲取響應狀態碼
  • getResponseMessage()獲取響應碼後面的文本字符串

重定向

300一級的響應嗎都表示某種重定向,即請求的資源再也不指望的位置上,但有可能會在其餘位置找到。遇到這樣的響應時,大多數瀏覽器會自動重新位置加載文檔。

默認狀況下,HTTPURLConnection 會跟隨重定向,不過 HTTPURLConnection 有兩個靜態方法能夠控制是否跟隨重定向。

  • public static boolean getFollowRedirects()
  • public static void steFollowRedirects(boolean follow)

注意,這裏是靜態方法,全局修改。

結束語

可能這一章寫的比較凌亂,其實我也很絕望,不少東西我也是一邊學一邊記錄的,這已是整理的第三版筆記了。

這一章學習的主要目的是爲了對整個 HTTP請求有個大體的瞭解,不少知識點都是一筆帶過(還忽略了一些),同窗們知道有這麼回事就好了,大概在十8、十九課的時候,我會和你們一塊兒用咱們學過的知識點,手擼一個 Volley 網絡請求架構。

我提早透露一下此次手擼的 Volley會實現哪些需求以及用到了哪些知識點。

實現的需求

  • 支持請求 JSON 文本類型學,音頻,圖片類型,批量下載。上傳~
  • 請求各類數據時,調用層不用關心上傳參數的封裝
  • 獲取數據後,調用層不用關心 JSON 數據的解析
  • 回調時,調用層只須要知道傳入的 JSON 的對應響應類
  • 回調響應結果發生在主線程(線程切換)
  • 對下載,上傳擴展
  • 支持高併發請求,請求隊列一次獲取,能夠設置最大併發數,設置先請求先執行

會用到的知識點

  • 泛型
  • 請求隊列
  • 阻塞隊列
  • 線程拒絕策略

用到的設計模式

  • 模板方法模式
  • 單例模式
  • 策略模式
  • 生產者消費者模式(不屬於23種基本的消費模式 haha)
相關文章
相關標籤/搜索