日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 运维知识 > Android >内容正文

Android

Android HTTP必知必会

發(fā)布時(shí)間:2025/3/15 Android 32 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Android HTTP必知必会 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

HTTP協(xié)議使用如此廣泛,開(kāi)發(fā)者務(wù)必要做到“知”,“會(huì)”。

引子

用curl請(qǐng)求百度首頁(yè)全解析的過(guò)程:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 @feng ? jayfeng.com (master) ? curl -v http://www.baidu.com > ~/http_get.txt * Rebuilt URL to: http://www.baidu.com/ % Total % Received % Xferd Average Speed Time Time Time Current Dload Upload Total Spent Left Speed 0 0 0 0 0 0 0 0 --:--:-- --:--:-- --:--:-- 0* Trying 119.75.217.109... * Connected to www.baidu.com (119.75.217.109) port 80 (#0) > GET / HTTP/1.1 > Host: www.baidu.com > User-Agent: curl/7.43.0 > Accept: */* > < HTTP/1.1 200 OK < Date: Thu, 28 Jan 2016 14:53:51 GMT < Content-Type: text/html; charset=utf-8 < Transfer-Encoding: chunked < Connection: Keep-Alive < Vary: Accept-Encoding < Set-Cookie: BAIDUID=D75C20ED3D7551221E1C32F79C698867:FG=1; expires=Thu, 31-Dec-37 23:55:55 GMT; max-age=2147483647; path=/; domain=.baidu.com < Set-Cookie: BIDUPSID=D75C20ED3D7551221E1C32F79C698867; expires=Thu, 31-Dec-37 23:55:55 GMT; max-age=2147483647; path=/; domain=.baidu.com < Set-Cookie: PSTM=1453992831; expires=Thu, 31-Dec-37 23:55:55 GMT; max-age=2147483647; path=/; domain=.baidu.com < Set-Cookie: BDSVRTM=0; path=/ < Set-Cookie: BD_HOME=0; path=/ < Set-Cookie: H_PS_PSSID=18880_1458_18879_12824_18205_18777_17000_17072_15544_11476_10634; path=/; domain=.baidu.com < P3P: CP=" OTI DSP COR IVA OUR IND COM " < Cache-Control: private < Cxy_all: baidu+1257c154f891fc3a17374fed141622bd < Expires: Thu, 28 Jan 2016 14:53:00 GMT < X-Powered-By: HPHP < Server: BWS/1.1 < X-UA-Compatible: IE=Edge,chrome=1 < BDPAGETYPE: 1 < BDQID: 0xe5ff10b4000dd380 < BDUSERID: 0 < { [2880 bytes data] 100 98345 0 98345 0 0 665k 0 --:--:-- --:--:-- --:--:-- 671k * Connection #0 to host www.baidu.com left intact

示意圖

把上面的過(guò)程畫(huà)成示意圖如下:

但是那些代碼到底是什么意思呢?
聽(tīng)我慢慢說(shuō)來(lái)。

結(jié)構(gòu)

說(shuō)起來(lái)http的結(jié)構(gòu)確實(shí)是簡(jiǎn)單,從上面的示意圖大概也能看出來(lái),包括三部分(請(qǐng)求和響應(yīng)用/區(qū)分):

1 2 3 4 5 6 7 8 9 10 - - - - - - - - - - - - - - - - - - - - - - - - - - | Request Line / Response Line | - - - - - - - - - - - - - - - - - - - - - - - - - - | ... | | Request Header / Response Header | | ... | - - - - - - - - - - - - - - - - - - - - - - - - - - | Optional Request Body / Optional Response Body | | ... | - - - - - - - - - - - - - - - - - - - - - - - - - -

1. 請(qǐng)求行/狀態(tài)行

以上面百度為例子,請(qǐng)求行是:

1 2 // 包括了基本的請(qǐng)求方法: GET,請(qǐng)求資源路徑: /, HTTP協(xié)議版本: HTTP/1.1 > GET / HTTP/1.1

狀態(tài)行是:

1 2 // 包括服務(wù)器響應(yīng)的HTTP協(xié)議版本: HTTP/1.1, 響應(yīng)狀態(tài)碼: 200, 狀態(tài)碼描述: OK < HTTP/1.1 200 OK

2. 首部

首部可分為請(qǐng)求首部,響應(yīng)首部, 實(shí)體首部,非正式首部,但是這些首部會(huì)有一些相同名稱(chēng)的首部,我們把它們定位為通用首部。
請(qǐng)求首部:

1 > User-Agent: curl/7.43.0

響應(yīng)首部:

1 < Connection: Keep-Alive

實(shí)體首部:

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

非正式首部:

1 Set-Cookie: BDSVRTM=0; path=/

更多首部,下一節(jié)會(huì)專(zhuān)門(mén)詳解。

3. 實(shí)體內(nèi)容

對(duì)于請(qǐng)求消息,如果是POST請(qǐng)求,可以設(shè)置請(qǐng)求內(nèi)容:傳參,甚至上傳文件。
對(duì)于響應(yīng)消息,返回的主體內(nèi)容,就是響應(yīng)內(nèi)容:網(wǎng)頁(yè),圖片等資源都是。

首部字段概覽

從HTTP的結(jié)構(gòu)來(lái)看,HTTP的重頭戲當(dāng)屬那些預(yù)定義的首部了。

首部字段名 說(shuō)明
通用首部字段 請(qǐng)求報(bào)文和響應(yīng)報(bào)文兩方都會(huì)使用的首部
CacheControl 控制緩存的行
Connection 允許客戶(hù)端和服務(wù)器指定與請(qǐng)求/響應(yīng)連接有關(guān)的選項(xiàng)
Date 報(bào)文創(chuàng)建時(shí)間
Progma 報(bào)文指令
Trailer 報(bào)文末端的首部一覽
Transfer-Encoding 指定報(bào)文主體的傳輸編碼方式
Upgrade 升級(jí)為其它協(xié)議
Via 代理服務(wù)器的相關(guān)信息
Warning 錯(cuò)誤通知
請(qǐng)求首部字段 從客戶(hù)端向服務(wù)器端發(fā)送請(qǐng)求報(bào)文時(shí)使用的首部。補(bǔ)充了請(qǐng)求的附加內(nèi)容、客戶(hù)端信息、響應(yīng)內(nèi)容相關(guān)優(yōu)先級(jí)等信息
Accept 用戶(hù)代理可處理的媒體類(lèi)型
Accept-Charset 優(yōu)先的字符集
Content-Encoding 優(yōu)先的內(nèi)容編碼
Connectionntent-Language 優(yōu)先的語(yǔ)言
Authorization Web認(rèn)證信息
Expect 期待服務(wù)器的特定行為
From 用戶(hù)的電子郵箱地址
Host 請(qǐng)求資源所在服務(wù)器
If-Match 比較實(shí)體標(biāo)記(ETag)
If-Modified-Since 比較資源的更新時(shí)間
If-None-Match 比較實(shí)體標(biāo)記較實(shí)體標(biāo)記(與If-Match相反)
If-Range 資源未更新時(shí)發(fā)送實(shí)體Byte的范圍請(qǐng)求
If-Unmodified-Since 比較資源的更新??間(與If-Modified-Since相反)
Max-Forwards 最大傳輸逐跳數(shù)
Proxy-Authorization 代理服務(wù)器要求客戶(hù)端的認(rèn)證信息
Range 實(shí)體的字節(jié)范圍請(qǐng)求
Referer 對(duì)請(qǐng)求中URI的原始獲取方
TE 傳輸編碼的優(yōu)先級(jí)
User-Agent HTTP客戶(hù)端程序的信息
響應(yīng)首部字段 從服務(wù)器端向客戶(hù)端返回響應(yīng)報(bào)文時(shí)使用的首部。補(bǔ)充了響應(yīng)的附加內(nèi)容,也會(huì)要求客戶(hù)端附加額外的內(nèi)容信息
Accept-Ranges 是否接受字節(jié)范圍請(qǐng)求
Agente 推算資源創(chuàng)建經(jīng)過(guò)時(shí)間
Etag 資源的匹配信息
Location 令客戶(hù)端重定向至指定URI
Proxy-Authenticate 代理服務(wù)器對(duì)客戶(hù)端的認(rèn)證信息
Retry-After 對(duì)再次發(fā)起請(qǐng)求的時(shí)機(jī)要求
Server HTTP服務(wù)器的安裝信息
Vary 代理服務(wù)器緩存的管理信息
WWW-Authenticate 服務(wù)器對(duì)客戶(hù)端的認(rèn)證信息
實(shí)體首部字段 針對(duì)請(qǐng)求報(bào)文和響應(yīng)報(bào)文的實(shí)體部分使用的首部。補(bǔ)充了資源內(nèi)容更新時(shí)間等與實(shí)體相關(guān)的信息
Allow 資源可支持的HTTP方法
Content-Encoding 實(shí)體主體適用的編碼方式
Content-Language 實(shí)體主體的自然語(yǔ)言
Content-Length 實(shí)體主體的大小
Content-Location 替代對(duì)應(yīng)資源的URI
Content-MD5 實(shí)體主體的報(bào)文摘要
Content-Rangesge 實(shí)體主體的位置范圍
Content-Type 實(shí)體主體的媒體類(lèi)型
Expires 實(shí)體主體過(guò)期的日期時(shí)間
Last-Modified 資源的最后修改日期時(shí)間

對(duì)一些常用字段深入了解是很有必要,這里不做詳述,有些字段單獨(dú)拿出來(lái)就能另外再寫(xiě)一篇文章了,請(qǐng)參考文末附錄。

常見(jiàn)狀態(tài)碼

HTTP狀態(tài)碼標(biāo)明客戶(hù)端HTTP請(qǐng)求的返回結(jié)果,結(jié)果是否正確,應(yīng)該怎么處理等信息。

狀態(tài)碼 描述
200 OK
301 Moved Permanently
302 Found
304 Not Modified
307 Temporary Redirect
400 Bad Request
401 Unauthorized
403 Forbidden
404 Not Found
410 Gone
500 Internal Server Error
501 Not Implemented

值得注意的幾個(gè)熱點(diǎn)

1. 持久連接

在這個(gè)無(wú)網(wǎng)不沖浪,推送滿(mǎn)天飛的年代,理解持久連接的概念非常重要。
引用wiki的解釋:

HTTP持久連接(HTTP persistent connection,也稱(chēng)作HTTP keep-alive或HTTP connection reuse)是使用同一個(gè)TCP連接來(lái)發(fā)送和接收多個(gè)HTTP請(qǐng)求/應(yīng)答,而不是為每一個(gè)新的請(qǐng)求/應(yīng)答打開(kāi)新的連接的方法。

可以說(shuō),http1.1相對(duì)于http1.0的一個(gè)最大的改進(jìn)就是默認(rèn)支持http持久連接了。

在android客戶(hù)端中如果要關(guān)閉持久連接(以google http client為例)

1 request.getHeaders().set("Connection", "close");

另外,關(guān)于持久連接造成EOFException的問(wèn)題,我一直沒(méi)用找到可靠的解決方案,okhttp的issues下關(guān)于這個(gè)討論也是很熱鬧:
EOFException in RealBufferedSource.readUtf8LineStrict
EOFException in RealBufferedSource.readUtf8LineStrict(): 0-bytes in stream
EOFException in RealBufferedSource.readUtf8LineStrict(): corrupt stream
但是,像xutils3這樣的修復(fù)方案是真的對(duì)嗎?
嘗試修復(fù)Android4.4之前HttpUrlConnection偶發(fā)的EOFException問(wèn)題

直接把4.4之前的長(zhǎng)連接給關(guān)閉了,雖然干凈了,但是是否會(huì)對(duì)性能造成影響?這個(gè)問(wèn)題的解法是否要聯(lián)調(diào)一下服務(wù)器的keepalive_timeout?如果真的和keepalive_timeout,keepalive_timeout設(shè)置應(yīng)該設(shè)置多少(這個(gè)值不能設(shè)置太大,否則可能會(huì)把服務(wù)器搞掛)?
請(qǐng)高手賜教。

2. 斷點(diǎn)續(xù)傳

斷點(diǎn)續(xù)傳的原理其實(shí)非常簡(jiǎn)單,就是利用HTTP的請(qǐng)求首部中的Range字段。
第一步,計(jì)算本地文件大小。

1 2 3 4 5 6 7 8 9 10 11 12 13 FileInputStream fis = null; try { // 讀取本地文件 fis = new FileInputStream(dest); // currentSize就是本地文件大小 currentSize = fis.available(); } catch (IOException e) { throw e; } finally { if (fis != null) { fis.close(); } }

第二步,設(shè)置Range值,明確告知服務(wù)器從哪里接著下載。

1 2 3 4 5 6 7 HttpURLConnection conn; ... // 如果本地文件存在,設(shè)置RANGE為"bytes=currentSize-", -后面不寫(xiě)具體值,表示接著下載到文件結(jié)尾 if (currentSize > 0) { conn.setRequestProperty("RANGE", "bytes=" + currentSize + "-"); } ...

完整代碼請(qǐng)參考:http之download方法
PS: 這里只是說(shuō)明原理,如果是可變文件(比如圖片資源一般定義為不變文件),還要考慮文件校驗(yàn)。

3. 上傳文件

對(duì)上傳文件的理解程度某個(gè)意義上就代表了你對(duì)HTTP結(jié)構(gòu)的理解程度。
第一步,為了后續(xù)代碼可讀性,先定義幾個(gè)常量。

1 2 3 4 String BOUNDARY = "--------------" + UUID.randomUUID().toString(); String PREFIX = "--", String LINEND = "\r\n"; String MULTIPART_FROM_DATA = "multipart/form-data";

第二步,定義Content-Type。
Content-Type為”multipart/form-data”,因?yàn)橛形募荒芤远M(jìn)制的形式傳輸。同時(shí)定義內(nèi)容分隔符。

1 2 // ${bound} 是一個(gè)占位符, 為了表示唯一,可以用一些特殊的隨機(jī)組合,比如---------------4365423423423423 Content-Type: multipart/form-data; boundary=${bound}

第三步,傳參數(shù)(可選)。
傳文件并不是說(shuō)就不能再傳參數(shù)了。

1 2 3 4 5 6 7 8 9 10 11 for (Map.Entry<String, String> entry : params.entrySet()) { sb.append(PREFIX); sb.append(BOUNDARY); sb.append(LINEND); sb.append("Content-Disposition: form-data; name=\"" + entry.getKey() + "\"" + LINEND); sb.append("Content-Type: text/plain; charset=GBK" + LINEND); sb.append("Content-Transfer-Encoding: 8bit" + LINEND); sb.append(LINEND); sb.append(entry.getValue()); sb.append(LINEND); }

第四步,傳文件。

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 for (Map.Entry<String, File> file : files.entrySet()) { StringBuilder sb1 = new StringBuilder(); sb1.append(PREFIX); sb1.append(BOUNDARY); sb1.append(LINEND); // 添加文件描述 sb1.append("Content-Disposition: form-data; name=\"uploadfile\"; filename=\"" + file.getValue().getName() + "\"" + LINEND); sb1.append("Content-Type: application/octet-stream; charset=GBK" + LINEND); sb1.append(LINEND); os.write(sb1.toString().getBytes()); is = new FileInputStream(file.getValue()); byte[] buffer = new byte[1024]; int len = 0; while ((len = is.read(buffer)) != -1) { os.write(buffer, 0, len); } is.close(); os.write(LINEND.getBytes()); }

第五步,末尾邊界。
特別寫(xiě)出這一步是為了強(qiáng)調(diào),請(qǐng)務(wù)必注意各個(gè)段落的分割。

1 2 byte[] end_data = (PREFIX + BOUNDARY + PREFIX + LINEND).getBytes(); os.write(end_data);

可以看的出來(lái),所謂上傳文件,就是以二進(jìn)制的形式把這些參數(shù),文件等數(shù)據(jù)以一定邊界區(qū)分并拼裝在一起發(fā)送給服務(wù)器。
完整代碼請(qǐng)參考:http之upload方法
關(guān)于上傳如果想了解更多,可以學(xué)習(xí)一下lite http的部分源碼:lite http之content

4. Last Modified和ETag

通過(guò)Last Modified作為服務(wù)器文件的時(shí)間戳,來(lái)判斷服務(wù)器文件是否有更新。

1 2 3 4 5 6 7 8 9 10 11 public static long getLastModified(URL url) throws IOException { HttpURLConnection connection = (HttpURLConnection) url.openConnection(); connection.setConnectTimeout(TIME_OUT); connection.setReadTimeout(TIME_OUT); long lastModified = connection.getLastModified(); connection.disconnect(); return lastModified; }

ETag,其實(shí)和Last Modified一樣,只不過(guò)它不是時(shí)間戳而是一串標(biāo)志量,也可以判斷服務(wù)器文件是否發(fā)生變化。
這個(gè)我沒(méi)有使用過(guò),這里不細(xì)講。
具體請(qǐng)參考:?ETag使用效果對(duì)比&經(jīng)驗(yàn)分享?、?對(duì)站點(diǎn)服務(wù)器如何配置ETag

5. HTTPS

HTTPS是在HTTP層之下添加了SSL層,大大增強(qiáng)了數(shù)據(jù)傳輸?shù)陌踩浴?br /> 在android中,如何解析https的接口呢?(以下代碼因?yàn)槭嵌嗄昵按a,可能有些地方欠缺嚴(yán)謹(jǐn),僅供學(xué)習(xí)參考)
第一步,生成客戶(hù)端私鑰。

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 if [ -z $1 ]; then echo "Usage: importcert.sh <CA cert PEM file>" exit 1 fi CACERT=$1 BCJAR=bcprov-jdk16-145.jar TRUSTSTORE=../app/src/main/res/raw/mytruststore.bks ALIAS=`openssl x509 -inform PEM -subject_hash -noout -in $CACERT` if [ -f $TRUSTSTORE ]; then rm $TRUSTSTORE || exit 1 fi echo "Adding certificate to $TRUSTSTORE..." keytool -import -v -trustcacerts -alias $ALIAS \ -file $CACERT \ -keystore $TRUSTSTORE -storetype BKS \ -providerclass org.bouncycastle.jce.provider.BouncyCastleProvider \ -providerpath $BCJAR \ -storepass 123456abc echo "" echo "Added '$CACERT' with alias '$ALIAS' to $TRUSTSTORE..."

使用這個(gè)腳本,利用pem文件,最終在res/raw目錄下生成一個(gè)mytruststore.bks文件。

第二步,根據(jù)私鑰和密碼生成SSLSocketFactory:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 // 為了更好的性能,這里使用全局靜態(tài)變量 private static SSLSocketFactory sCustomerSSLSocketFactory = null; public static SSLSocketFactory getCustomerSSLSocketFactory(Context context) { if (sCustomerSSLSocketFactory != null) { return sCustomerSSLSocketFactory; } try { KeyStore trusted = KeyStore.getInstance("BKS"); InputStream in = context.getResources().openRawResource(R.raw.mytruststore); try { trusted.load(in, "aike_client".toCharArray()); } finally { in.close(); } sCustomerSSLSocketFactory = new SSLSocketFactory(trusted); sCustomerSSLSocketFactory.setHostnameVerifier(new AllowAllHostnameVerifier()); return sCustomerSSLSocketFactory; } catch(Exception e) { throw new AssertionError(e); } }

第三步,在google http client中使用SSLSocketFactory。

1 2 3 4 5 6 7 ApacheHttpTransport.Builder builder = new ApacheHttpTransport.Builder(); HttpRequestFactory httpRequestFactory = builder .setSocketFactory(AppConfig.getCustomerSSLSocketFactory(mContext)) .build() .createRequestFactory(); HttpRequest request = httpRequestFactory.buildPostRequest(url, content); ...

至此https的基本使用流程大概是這樣的。

小結(jié)

通過(guò)對(duì)HTTP結(jié)構(gòu)和首部的深入學(xué)習(xí),相信大家對(duì)http協(xié)議的理解會(huì)上一個(gè)臺(tái)階。
如果有興趣,可自行去拓展學(xué)習(xí)一下HTTP2.0,SPDY,WebSocket等。

附錄

[1].?What really happens when you navigate to a URL
[2].?HTTP專(zhuān)題 by Jerry Qu
[3].?HTTP/2專(zhuān)題 by Jerry Qu
[4].?HTTP 協(xié)議中的 Transfer-Encoding
[5].?Http 協(xié)議中的Range請(qǐng)求頭例子
[6].?HTTP 2.0的那些事
[7].?HTTP持久連接


原文地址: http://jayfeng.com/2016/01/08/Android%20HTTP%E5%BF%85%E7%9F%A5%E5%BF%85%E4%BC%9A/

總結(jié)

以上是生活随笔為你收集整理的Android HTTP必知必会的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。