編輯:關於android開發
前言:近期在慕課網學習了慕課網課程Android中的HTTP通信,就自己總結了一下,其中參考了不少博文,感謝大家的分享。
文章內容包括:
1.HTTP簡介
2.HTTP/1.0和HTTP/1.1之間的區別
3.HTTP的請求頭、響應頭和狀態碼
4.Android中的HttpUrlConnection
1.Http簡介
Http(Hypertext transfer protocol)定義了浏覽器怎麼向萬維網服務器發送萬維網文檔,以及服務器怎麼將文檔發送給服務器。從層次上看,http是面向應用層協議的,它是萬維網能夠可靠交換文檔的基礎。
http的工作流程
當用戶點擊一個鏈接(假設URL為http://www.tsinghua.edu.cn/chn/yxsz/index.html ),所發生的事件流程:
(1)浏覽器分析連接所指向的頁面的URL。
(2)浏覽器向DNS請求解析www.tsinghua.edu.cn的IP地址。
(3)浏覽器解析出服務器的IP地址。
(4)浏覽器與服務器建立TCP連接。
(5)浏覽器發出取文件指令:GET /chn/yxsz/index.html。
(5)服務器給出響應,將文件index.html發送給浏覽器。
(6)釋放TCP連接。
(7)浏覽器顯示index.html的所用信息。
Http的特點
(1)支持客服/服務器(C/S)模式
(2)簡單快速,客戶向服務器請求服務時,只需傳送請求方法和路徑。請求方法常用的有GET、POST、HEAD。每種方法規定了與服務器的連接不同,由於HTTP協議簡單,使得HTTP服務器的程序規模更小、因而通信更快。
(3)HTTP是無連接的。無連接意味者HTTP每次只處理一個請求,服務器處理完處理完客戶的請求,並且收到客戶的應答後,即斷開連接,可以節省傳輸時間。
(4)HTTP是無狀態的。無狀態意味者HTTP協議對於事務沒有記憶能力,缺少狀態表示後續處理需要前面的信息,則它必須重傳。這可能使它沒次連接傳輸的信息量增大,另一方面、服務器在不需要先前信息就表現的非常快,同時是服務器更容易支持大量的並發的HTTP請求。
PS:雖然HTTP是無連接的協議,但HTTP使用了面向連接的運輸層協議TCP,因此保證了數據的可靠傳輸,HTTP不用考慮數據在傳輸過程中被丟棄了如何重傳。
2.HTTP/1.0和HTTP/1.1之間的區別
HTTP/1.0的主要缺點是它使用非持續連接每請求一個文檔需要兩倍的RTT的開銷。這時的協議如果一個主頁有很多鏈接的對象(如圖片),每個鏈接都需要建立新的TCP連接,那麼每一次鏈接下載都會導致2×RTT的開銷。
HTTP/1.1協議很好的解決了這個問題,它使用了持續連接,萬維網服務器在發送響應後的一段時間內仍然保持著這個連接,是同一個客戶可以和該服務器傳送後續的HTTP請求報文和響應報文。
3.HTTP的請求頭、響應頭和狀態碼
請求頭(進入簡書的請求頭,可以通過Firfox浏覽器通過開發者選項打開網絡查看(快捷鍵ctrl+shift+Q))。
GET http://www.jianshu.com/
Host: www.jianshu.com
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64; rv:47.0) Gecko/20100101 Firefox/47.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,/;q=0.8
Accept-Language: zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3
Accept-Encoding: gzip, deflate
Referer: https://www.google.com.hk
Cookie: (略)
Connection: keep-alive
If-None-Match: W/"b4e2a47d84be2df34bb1d5b79be9c040"
Cache-Control: max-age=0
下面說下具體的含義:
GET定義了請求的方法。同樣的方法共有8種(下面會列出)。
Host:初始URL中的主機和端口。
User-Agent:浏覽器類型,如果Servlet返回的內容與浏覽器類型有關則該值非常有用。
Accept:浏覽器可接受的MIME類型。
Accept-Charset:浏覽器可接受的字符集。
Accept-Language:浏覽器所希望的語言種類,當服務器能夠提供一種以上的語言版本時要用到。
Accept-Encoding:浏覽器能夠進行解碼的數據編碼方式,比如gzip。Servlet能夠向支持gzip的浏覽器返回經gzip編碼的HTML頁面。許多情形下這可以減少5到10倍的下載時間。
Referer:包含一個URL,用戶從該URL代表的頁面出發訪問當前請求的頁面。
Cookie:這是最重要的請求頭信息之一,HTTP請求發送時,會把保存在該請求域名下的所有cookie值一起發送給web服務器。
Connection:
表示是否需要持久連接。如果Servlet看到這裡的值為“Keep- Alive”,或者看到請求使用的是HTTP 1.1(HTTP
1.1默認進行持久連接),它就可以利用持久連接的優點,當頁面包含多個元素時(例如Applet,圖片),顯著地減少下載所需要的時間。要實現這一
點,Servlet需要在應答中發送一個Content-Length頭,最簡單的實現方法是:先把內容寫入
ByteArrayOutputStream,然後在正式寫出內容之前計算它的大小。
Cache-Control:If-None-Match:如果內容未改變返回304代碼,參數為服務器先前發送的Etag,與服務器回應的Etag比較判斷是否改。
Cache-Control:指定請求和響應遵循的緩存機制。
8中請求方法解釋(摘自:http://itbilu.com/other/relate/EkwKysXIl.html)
GET
請求會顯示請求指定的資源。一般來說GET方法應該只用於數據的讀取,而不應當用於會產生副作用的非冪等的操作中。GET方法請求指定的頁面信息,並返回響應主體,GET被認為是不安全的方法,因為GET方法會被網絡蜘蛛等任意的訪問。
HEAD
方法與GET方法一樣,都是向服務器發出指定資源的請求。但是,服務器在響應HEAD
請求時不會回傳資源的內容部分,即:響應主體。這樣,我們可以不傳輸全部內容的情況下,就可以獲取服務器的響應頭信息。HEAD方法常被用於客戶端查看服務器的性能。
POST
請求會向指定資源提交數據,請求服務器進行處理,如:表單數據提交、文件上傳等,請求數據會被包含在請求體中。POST方法是非冪等的方法,因為這個請求可能會創建新的資源或/和修改現有資源。
PUT
請求會身向指定資源位置上傳其最新內容,PUT方法是冪等的方法。通過該方法客戶端可以將指定資源的最新數據傳送給服務器取代指定的資源的內容。
DELETE
請求用於請求服務器刪除所請求URI(統一資源標識符,Uniform Resource Identifier)所標識的資源。DELETE請求後指定資源會被刪除,DELETE方法也是冪等
的。
CONNECT
該方法是HTTP/1.1協議預留的,能夠將連接改為管道方式的代理服務器。通常用於SSL加密服務器的鏈接與非加密的HTTP代理服務器的通信。
OPTIONS
請求與HEAD類似,一般也是用於客戶端查看服務器的性能。這個方法會請求服務器返回該資源所支持的所有HTTP請求方法,該方法會用''來代替資源名稱,向服務器發送OPTIONS請求,可以測試服務器功能是否正常。JavaScript的XMLHttpRequest對象進行CORS跨域資源共享時,就是使用OPTIONS方法發送嗅探請求,以判斷是否有對指定資源的訪問權限。允許
*TRACE
請求服務器回顯其收到的請求信息,該方法主要用於HTTP請求的測試或診斷。
*響應頭(同樣是在請求簡書首頁的響應頭
Cache-Control: max-age=0, private, must-revalidate
Connection: keep-alive
Content-Encoding: gzip
Content-Type: text/html; charset=utf-8
Date: Sun, 19 Jun 2016 15:29:41 GMT
Etag: W/"e9a43aabd740855cd3fe0097faf6180d"
Server: nginx
Set-Cookie: (略)
Vary: Accept-Encoding
X-Request-Id: ce26a795-7e99-4959-a498-45f689471d7f
X-Runtime: 0.596683
x-content-type-options: nosniff
x-frame-options: DENY
x-xss-protection: 1; mode=block
Cache-Control指定請求和響應遵循的緩存機制。
Connection:表示是否需要持久連接。
Content-Encoding 文檔的編碼(Encode)方法。只有在解碼之後才可以得到Content-Type頭指定的內容類型。利用gzip壓縮文檔能夠顯著地減少HTML文檔 的下載時間。
Content-
Type
表示後面的文檔屬於什麼MIME類型。Servlet默認為text/plain,但通常需要顯式地指定為text/html。由於經常要設置
Content-Type,因此HttpServletResponse提供了一個專用的方法setContentTyep。
Date 當前的GMT時間。你可以用setDateHeader來設置這個頭以避免轉換時間格式的麻煩。
Etag 請求變量的實體標簽的當前值。
Server 服務器名字。Servlet一般不設置這個值,而是由Web服務器自己設置。
Set-Cookie 設置和頁面關聯的Cookie。
Vary 告訴下游代理是使用緩存響應還是從原始服務器請求
更為詳細的請求頭與響應頭信息,請參考:HTTP Header 詳解
*狀態碼
1XX :表示通知信息的,如請求收到了或正在處理。
2XX :表示成功,如接收或知道了。
3XX :表示重定向,如要完成還需采取進一步處理。
4XX :表示客戶的差錯,如請求中有錯誤的語法或不能完成。
5XX :表示服務器的差錯,如服務器失效無法完成請求。
4.Android中的HttpUrlConnection
Android中的連接主要是通過HttpUrlConnection來完成的,下面將要從HttpUrlConnection使用、get和post傳遞參數、多線程下載三個方面來看HttpUrlClient的用法:
(1)HttpUrlConnection的使用格式:URL url = new URL("http://localhost:8080/TestHttpURLConnectionPro/index.jsp"); //將地址轉換為URL
URLConnection rulConnection = url.openConnection(); // 此處的urlConnection對象實際上是根據URL的請求協議(此處是http)生成的URLConnection類的子類HttpURLConnection,故此處最好將其轉化為HttpURLConnection類型的對象
HttpURLConnection httpUrlConnection = (HttpURLConnection) rulConnection;
設置HttpUrlClient的連接參數:// 設置是否向httpUrlConnection輸出,因為這個是post請求,參數要放在。http正文內,因此需要設為true, 默認情況下是false;
httpUrlConnection.setDoOutput(true);
//設置是否從httpUrlConnection讀入,默認情況下是true;
httpUrlConnection.setDoInput(true);
// Post 請求不能使用緩存
httpUrlConnection.setUseCaches(false);
// 設定傳送的內容類型是可序列化的java對象
// (如果不設此項,在傳送序列化對象時,當WEB服務默認的不是這種類型時可能拋java.io.EOFException)
httpUrlConnection.setRequestProperty("Content-type", "application/x-java-serialized-object");
// 設定請求的方法為"POST",默認是GET
httpUrlConnection.setRequestMethod("POST");
// 連接,從上述第2條中url.openConnection()至此的配置必須要在connect之前完成,
httpUrlConnection.connect();
對於HttpUrlConnection在代碼中的具體用法,看下面都是一樣的用法,看過就懂了。
(2)get和post方式傳遞參數
get方式
使用get方式傳遞參數關鍵在於URl,在代碼中可以看出我們在url中附加了一些數據,實際上get方式就是在通過在url中附加數據來傳遞參數的,因此采用這種方式是很不安全的。private void doGet(){
try {
url = url + "?name=" + URLEncoder.encode(name,"utf-8") + "&age=" + age;
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
try {
URL httpUrl = new URL(url); //新建URL對象
HttpURLConnection conn = (HttpURLConnection) httpUrl.openConnection();//打開一個連接
conn.setRequestMethod("GET");//設置請求方法為GET
conn.setReadTimeout(5000);//設置從服務器讀取數據的超時限制為5秒
BufferedReader reader = new BufferedReader(
new InputStreamReader(conn.getInputStream()));//獲取服務器傳遞的數據輸入流
String str;
StringBuffer sb = new StringBuffer(); //存儲讀取的數據
while((str = reader.readLine()) != null){//讀取數據
sb.append(str);
}
System.out.println("result:"+sb.toString());
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
POST方式
post傳遞參數的方式與get是不同的,它會將傳遞的數據寫入到請求的正文中。private void doPost(){
try {
URL HttpUrl = new URL(url);
HttpURLConnection conn = (HttpURLConnection) HttpUrl.openConnection();
conn.setRequestMethod("POST");
conn.setReadTimeout(5000);
OutputStream out = conn.getOutputStream(); //新建輸出流對象
String content = "name="+name+"&age="+age;//傳遞對象
out.write(content.getBytes());//將傳遞對象轉為字符流寫入輸出流中
//下面是對於服務器返回數據的處理
BufferedReader reader = new BufferedReader(new InputStreamReader(conn.getInputStream()));
StringBuffer sb = new StringBuffer();
String str;
while((str=reader.readLine())!=null){
sb.append(str);
}
System.out.println(sb.toString());
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
(3)多線程下載
public class DownLoad {
private Executor threadPool = Executors.newFixedThreadPool(3);
private Handler handler;
public DownLoad(Handler handler){
this.handler = handler;
}
static class DownLoadRunnable implements Runnable {
private String url;
private String fileName;
private long start;
private long end;
private Handler handler;
public DownLoadRunnable(String url,String fileName,long start,long end,Handler handler){
this.url = url;
this.fileName = fileName;
this.start = start;
this.end = end;
this.handler = handler;
}
@Override
public void run() {
try {
URL httpUrl = new URL(url);
HttpURLConnection conn = (HttpURLConnection) httpUrl.openConnection();
conn.setRequestMethod("GET");
conn.setRequestProperty("Range","bytes="+start+"-"+end);
conn.setReadTimeout(5000);
RandomAccessFile access = new RandomAccessFile(new File(fileName),"rwd");
access.seek(start);
InputStream in = conn.getInputStream();
byte[] b = new byte[1024*4];
int len=0;
while((len=in.read(b))!=-1){
access.write(b,0,len);
}
if (access!=null){
access.close();
}
if (in!=null){
in.close();
}
Message msg = new Message();
msg.what = 1;
handler.sendMessage(msg);
} catch (IOException e) {
e.printStackTrace();
}
}
}
public void loadFile(String url) {
try {
URL httpUrl = new URL(url);
HttpURLConnection conn = (HttpURLConnection) httpUrl.openConnection();
conn.setRequestMethod("GET");
conn.setReadTimeout(5000);
int count = conn.getContentLength();
int block = count / 3;
String fileName = getFileName(url);
File parent = Environment.getExternalStorageDirectory();
File download = new File(parent,fileName);
for (int i=0;i<3;i++){
long start = i*block;
long end = (i+1)*block-1;
if (i==2){
end = count;
}
DownLoadRunnable runnable = new DownLoadRunnable(url,download.getAbsolutePath(),start,end,handler);
threadPool.execute(runnable);
}
} catch (IOException e) {
e.printStackTrace();
}
}
public String getFileName(String url){
return url.substring(url.lastIndexOf("/")+1);
}
}
參考:
(1)慕課網課程Android中的HTTP通信
(2)HTTP請求方法:GET、HEAD、POST、PUT、DELETE、CONNECT、OPTIONS、TRACE
(3)HTTP響應頭信息和請求頭信息詳解
(4)HTTP Header 詳解
安卓圖片加載之使用universalimageloader加載圓形圓角圖片 前言 話說這universalimageloader加載圖片對搞過2年安卓程序都是用爛了再
Android加殼原理分析 0x00 閱讀本文前,建議讀者首先閱讀Android加殼原理,參考文章Android中的Apk的加固(加殼)原理解析和實現。如果沒有看過這篇文
【React Native開發】React Native控件之ToolbarAndroid工具欄控件講解以及使用(15) (一)前言 今天我們一起來看一下工具欄控件T
Android 手機衛士--是否有密碼區分對話框類型,android衛士本文開始逐步實現設置中心的“手機防盜”功能模塊 本文地址:/www.cn