Android中的HTTP通訊,AndroidHTTP通訊
前言:近期在慕課網學習了慕課網課程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協議預留的,能夠將串連改為管道方式的Proxy 伺服器。通常用於SSLData Encryption Service器的連結與非加密的HTTPProxy 伺服器的通訊。
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 詳解