HTTP基礎與Android之(安卓與服務器通訊)——使用HttpClient和HttpURLConnection

查看原文:http://blog.csdn.net/sinat_29912455/article/details/51122286java

一、客戶端鏈接服務器實現內部的原理:

這裏寫圖片描述

分析上圖,步驟以下:android

第一步:在瀏覽器客戶端中獲得用戶輸入的內容。apache

第二步:瀏覽器獲得這個網址以後,內部會將這個域名發送到DNS上,進行域名解析。獲得它的IP以後就會連接到指定的服務器上,假如服務器的地址是:221.104.13.32:80,從瀏覽器到服務器端口它使用到最底層的TCP/IP協議。瀏覽器

第三步:實現TCP/IP協議用Socket來完成,使用了Socket的套接字。緩存

第四步:服務器端的80端口監聽客戶端的連接,這樣客戶端到服務器就連接上了。
服務器接收到這些內容以後,並按照這些請求的路徑找到對應的頁面,進一步找到對應的網頁內容,返回給客戶端。安全

通俗一點講,用戶在瀏覽器輸入網址,經過http協議發出去,網址通過DNS域名解析,解析成指定的ip地址,並在80端口上監聽用戶的請求。服務器監聽到請求以後,會以三種方式返回給客戶端:HTML、XML、JASON。服務器

GET方式和POST方式的差異:

GET是從服務器上獲取數據,POST是向服務器傳送數據。
在客戶端,GET方式在經過URL提交數據,數據在URL中能夠看到;POST方式,數據放在HTML HEADER內提交。
對於GET方式,服務器端用Request.QueryString獲取變量的值,對於POST方式,服務器用Request.Form獲取提交的數據。
GET方式提交的數據不能大於2KB(主要是URL長度限制),而POST則沒有此限制。
安全性問題。正如2中提到,使用GET的時候,參數會顯示在地址欄上,而POST不會。因此,若是這些數據是中文數據並且是非敏感數據,那麼使用GET;若是用戶輸入的數據不是中文字符並且包含敏感數據,那麼仍是使用POST爲好。網絡

HTTP返回請求數據的三種方式:

一、以HTML代碼內容返回。 app

二、以XML字符串的形式返回,在之後的android開發中這種形式返回數據比較多。 框架

三、以JSON對象形式返回,在網絡流量上考慮JSON要比XML方式要好一些,便於解析。

在Android當中,通常使用xml和Json數據解析。

二、使用HTTP協議訪問網絡:

Android中的WebView控件已經在後臺幫咱們處理好了發送HTTP請求、接收服務響應、解析返回數據,以及最終的頁面展現這幾步工做,不 過因爲它封裝得太好了,反而不能直觀地看出HTTP協議是如何工做的。所以接下來咱們經過手動發送HTTP請求的方式,來更加深刻的瞭解這一過程。

在Android上發送HTTP請求的方式通常有兩種:HttpURLConnection和HttpCient。咱們先來學習HttpCient。

三、HttpCient:

HttpClient是Apache開源組織提供的HTTP網絡訪問接口(一個開源的項目),從名字上就能夠看出,它是一個簡單的HTTP客戶端 (並非瀏覽器),能夠發送HTTP請求,接受HTTP響應。可是不會緩存服務器的響應,不能執行HTTP頁面中籤入嵌入的JS代碼,天然也不會對頁面內 容進行任何解析、處理,這些都是須要開發人員來完成的。

如今Android已經成功集成了HttpClient,因此開發人員在Android項目中能夠直接使用HttpClient來想Web站點提交 請求以及接受響應,若是使用其餘的Java項目,須要引入進相應的Jar包。HttpClient能夠在官網上下載。官網連接:http://hc.apache.org/downloads.cgi

HttpClient實際上是一個interface類型,HttpClient封裝了對象須要執行的Http請求、身份驗證、鏈接管理和其它特性。既然HttpClient是一個接口,所以沒法建立它的實例。從文檔上看,HttpClient有三個已知的實現類分別是:AbstractHttpClient, AndroidHttpClient, DefaultHttpClient,會發現有一個專門爲Android應用準備的實現類AndroidHttpClient,固然使用常規的DefaultHttpClient也能夠實現功能。

從兩個類包全部在位置就能夠看出區別,AndroidHttpClient定義在 android.net.http.AndroidHttpClient包下,屬於Android原生的http訪問,而 DefaultHttpClient定義在org.apache.http.impl.client.DefaultHttpClient包下,屬於對 apche項目的支持。而AndroidHttpClient沒有公開的構造函數,只能經過靜態方法newInstance()方法來得到 AndroidHttpClient對象。

簡單來講,用HttpClient發送請求、接收響應都很簡單,只須要五大步驟便可:(要牢記)

一、建立表明客戶端的HttpClient對象。

二、建立表明請求的對象,若是須要發送GET請求,則建立HttpGet對象,若是須要發送POST請求,則建立HttpPost對象。注:對於發 送請求的參數,GET和POST使用的方式不一樣,GET方式可使用拼接字符串的方式,把參數拼接在URL結尾;POST方式須要使用 setEntity(HttpEntity entity)方法來設置請求參數。

三、調用HttpClient對象的execute(HttpUriRequest request)發送請求,執行該方法後,將得到服務器返回的HttpResponse對象。服務器發還給咱們的數據就在這個HttpResponse相 應當中。調用HttpResponse的對應方法獲取服務器的響應頭、響應內容等。

四、檢查相應狀態是否正常。服務器發給客戶端的相應,有一個相應碼:相應碼爲200,正常;相應碼爲404,客戶端錯誤;相應碼爲505,服務器端錯誤。

五、得到相應對象當中的數據

四、DefaultHttpClient:

GET方式

佈局文件,activity_main.xml

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" tools:context=".MainActivity" > <Button android:id="@+id/button1" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="Send Request" /> <ScrollView android:layout_width="match_parent" android:layout_height="match_parent" > <TextView android:id="@+id/TextView1" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="@string/hello_world" /> </ScrollView> </LinearLayout>

 

佈局文件中,咱們用一個ScrollView來包裹TextView。藉助ScrollView控件的話,就能夠容許咱們一滾動的形式查看屏幕外i的那部份內容。

MainActivity.java的代碼以下:

package com.example.m04_http01; import org.apache.http.HttpEntity; import org.apache.http.HttpResponse; import org.apache.http.client.HttpClient; import org.apache.http.client.methods.HttpGet; import org.apache.http.impl.client.DefaultHttpClient; import org.apache.http.util.EntityUtils; import android.app.Activity; import android.os.Bundle; import android.os.Handler; import android.os.Message; import android.view.View; import android.view.View.OnClickListener; import android.widget.Button; import android.widget.TextView; public class MainActivity extends Activity { public static final int SHOW_RESPONSE = 0; private Button button_sendRequest; private TextView textView_response; //新建Handler的對象,在這裏接收Message,而後更新TextView控件的內容 private Handler handler = new Handler() { @Override public void handleMessage(Message msg) { super.handleMessage(msg); switch (msg.what) { case SHOW_RESPONSE: String response = (String) msg.obj; textView_response.setText(response); break; default: break; } } }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); textView_response = (TextView)findViewById(R.id.TextView1); button_sendRequest = (Button)findViewById(R.id.button1); button_sendRequest.setOnClickListener(new OnClickListener() { //點擊按鈕時,執行sendRequestWithHttpClient()方法裏面的線程 @Override public void onClick(View v) { // TODO Auto-generated method stub sendRequestWithHttpClient(); } }); } //方法:發送網絡請求,獲取百度首頁的數據。在裏面開啓線程 private void sendRequestWithHttpClient() { new Thread(new Runnable() { @Override public void run() { //用HttpClient發送請求,分爲五步 //第一步:建立HttpClient對象 HttpClient httpCient = new DefaultHttpClient(); //第二步:建立表明請求的對象,參數是訪問的服務器地址 HttpGet httpGet = new HttpGet("http://www.baidu.com"); try { //第三步:執行請求,獲取服務器發還的相應對象 HttpResponse httpResponse = httpCient.execute(httpGet); //第四步:檢查相應的狀態是否正常:檢查狀態碼的值是200表示正常 if (httpResponse.getStatusLine().getStatusCode() == 200) { //第五步:從相應對象當中取出數據,放到entity當中 HttpEntity entity = httpResponse.getEntity(); String response = EntityUtils.toString(entity,"utf-8");//將entity當中的數據轉換爲字符串 //在子線程中將Message對象發出去 Message message = new Message(); message.what = SHOW_RESPONSE; message.obj = response.toString(); handler.sendMessage(message); } } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } } }).start();//這個start()方法不要忘記了 } 

最後要記得在清單文件中聲明訪問網絡的權限:

<uses-sdk

android:minSdkVersion="8" android:targetSdkVersion="16" /> <uses-permission android:name="android.permission.INTERNET"/>

 

這裏寫圖片描述

POST方式

POST提交數據的步驟:

  1. 構造請求對象;
  2. 將須要傳遞給服務器端的數據放置在鍵值對對象當中;
  3. 將準備好的鍵值對放置在List當中;
  4. 生成表明請求體的對象;
  5. 將存有請求鍵值對的List對象放置在請求題對象當中;
  6. 將請求體對象放置到請求對象當中;
  7. 發送請求對象
    後面的步驟(即處理請求對象)和GET方法是一致的。
package com.example.m04_http02; import java.io.BufferedReader; import java.io.InputStreamReader; import java.util.ArrayList; import org.apache.http.HttpEntity; import org.apache.http.HttpResponse; import org.apache.http.NameValuePair; import org.apache.http.client.HttpClient; import org.apache.http.client.entity.UrlEncodedFormEntity; import org.apache.http.client.methods.HttpGet; import org.apache.http.client.methods.HttpPost; import org.apache.http.impl.client.DefaultHttpClient; import org.apache.http.message.BasicNameValuePair; import android.app.Activity; import android.os.Bundle; import android.util.Log; import android.view.View; import android.view.View.OnClickListener; import android.widget.Button; import android.widget.EditText; public class MainActivity extends Activity { private EditText nameText; private EditText pwdText; private Button button; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); nameText = (EditText) findViewById(R.id.nameText); pwdText = (EditText) findViewById(R.id.pwdText); button = (Button) findViewById(R.id.button1); button.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { // TODO Auto-generated method stub // 用戶輸入用戶名密碼, 而後經過Get方法發送給本地服務器 String name = nameText.getText().toString(); String pwd = pwdText.getText().toString(); // 使用GET方法向本地服務器發送數據 //GetThread getThread = new GetThread(name, pwd); //getThread.start(); //使用POST方法向服務器發送數據 PostThread postThread = new PostThread(name, pwd); postThread.start(); } }); } //子線程:經過GET方法向服務器發送用戶名、密碼的信息 class GetThread extends Thread { String name; String pwd; public GetThread(String name, String pwd) { this.name = name; this.pwd = pwd; } @Override public void run() { //用HttpClient發送請求,分爲五步 //第一步:建立HttpClient對象 HttpClient httpClient = new DefaultHttpClient(); //注意,下面這一行中,我以前把連接中的"test"誤寫成了"text",致使調BUG調了半天沒弄出來,真是浪費時間啊 String url = "http://192.168.1.112:8080/test.jsp?name=" + name+ "&password=" + pwd; //第二步:建立表明請求的對象,參數是訪問的服務器地址 HttpGet httpGet = new HttpGet(url); try { //第三步:執行請求,獲取服務器發還的相應對象 HttpResponse response = httpClient.execute(httpGet); //第四步:檢查相應的狀態是否正常:檢查狀態碼的值是200表示正常 if (response.getStatusLine().getStatusCode() == 200) { //第五步:從相應對象當中取出數據,放到entity當中 HttpEntity entity = response.getEntity(); BufferedReader reader = new BufferedReader( new InputStreamReader(entity.getContent())); String result = reader.readLine(); Log.d("HTTP", "GET:" + result); } } catch (Exception e) { e.printStackTrace(); } } } //子線程:使用POST方法向服務器發送用戶名、密碼等數據 class PostThread extends Thread { String name; String pwd; public PostThread(String name, String pwd) { this.name = name; this.pwd = pwd; } @Override public void run() { HttpClient httpClient = new DefaultHttpClient(); String url = "http://192.168.1.112:8080/test.jsp"; //第二步:生成使用POST方法的請求對象 HttpPost httpPost = new HttpPost(url); //NameValuePair對象表明了一個須要發往服務器的鍵值對 NameValuePair pair1 = new BasicNameValuePair("name", name); NameValuePair pair2 = new BasicNameValuePair("password", pwd); //將準備好的鍵值對對象放置在一個List當中 ArrayList<NameValuePair> pairs = new ArrayList<NameValuePair>(); pairs.add(pair1); pairs.add(pair2); try { //建立表明請求體的對象(注意,是請求體) HttpEntity requestEntity = new UrlEncodedFormEntity(pairs); //將請求體放置在請求對象當中 httpPost.setEntity(requestEntity); //執行請求對象 try { //第三步:執行請求對象,獲取服務器發還的相應對象 HttpResponse response = httpClient.execute(httpPost); //第四步:檢查相應的狀態是否正常:檢查狀態碼的值是200表示正常 if (response.getStatusLine().getStatusCode() == 200) { //第五步:從相應對象當中取出數據,放到entity當中 HttpEntity entity = response.getEntity(); BufferedReader reader = new BufferedReader( new InputStreamReader(entity.getContent())); String result = reader.readLine(); Log.d("HTTP", "POST:" + result); } } catch (Exception e) { e.printStackTrace(); } } catch (Exception e) { e.printStackTrace(); } } } }

 

這裏寫圖片描述

五、Java中使用HTTP——HttpURLConnection

HttpURLConnection繼承了URLConnection,因此在URLConnection的基礎上進一步改進,增長了一些用於操做 HTTP資源的便捷方法。Java中HttpURLConnection對象經過URL.openConnection()方法來得到,須要進行強制轉 換。先來介紹幾個HttpURLConnection的經常使用方法:

  • void setConnectTimeout(int timeout):設置鏈接超時時長,若是超過timeout時長,則放棄鏈接,單位以毫秒計算。

  • void setDoInput(boolean newValue) :標誌是否容許輸入。

  • void setDoOutput(boolean newValue):標誌是否容許輸出。

  • String getRequestMethod():獲取發送請求的方法。

  • int getResponseCode():獲取服務器的響應碼。

  • void setRequestMethod(String method):設置發送請求的方法。

  • void setRequestProperty(String field,String newValue):設置請求報文頭,而且只對當前HttpURLConnection有效。

GET方式

這個例子經過GET方式從服務端獲取一張圖片的信息,並把其保存在本地磁盤中。服務器爲本機上的IIS,一張靜態圖片,直接經過URL訪問。

package com.http.get; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.net.HttpURLConnection; import java.net.MalformedURLException; import java.net.URL; public class HttpUtils { private static String URL_PATH = "http://192.168.1.106:8080/green.jpg"; /** * @param args */ public static void main(String[] args) { // 調用方法獲取圖片並保存 saveImageToDisk(); } /** * 經過URL_PATH的地址訪問圖片並保存到本地 */ public static void saveImageToDisk() { InputStream inputStream= getInputStream(); byte[] data=new byte[1024]; int len=0; FileOutputStream fileOutputStream=null; try { //把圖片文件保存在本地F盤下 fileOutputStream=new FileOutputStream("F:\\test.png"); while((len=inputStream.read(data))!=-1) { //向本地文件中寫入圖片流 fileOutputStream.write(data,0,len); } } catch (IOException e) { e.printStackTrace(); } finally { //最後關閉流 if(inputStream!=null) { try { inputStream.close(); } catch (IOException e) { e.printStackTrace(); } } if(fileOutputStream!=null) { try { fileOutputStream.close(); } catch (IOException e) { e.printStackTrace(); } } } } /** * 經過URL獲取圖片 * @return URL地址圖片的輸入流。 */ public static InputStream getInputStream() { InputStream inputStream = null; HttpURLConnection httpURLConnection = null; try { //根據URL地址實例化一個URL對象,用於建立HttpURLConnection對象。 URL url = new URL(URL_PATH); if (url != null) { //openConnection得到當前URL的鏈接 httpURLConnection = (HttpURLConnection) url.openConnection(); //設置3秒的響應超時 httpURLConnection.setConnectTimeout(3000); //設置容許輸入 httpURLConnection.setDoInput(true); //設置爲GET方式請求數據 httpURLConnection.setRequestMethod("GET"); //獲取鏈接響應碼,200爲成功,若是爲其餘,均表示有問題 int responseCode=httpURLConnection.getResponseCode(); if(responseCode==200) { //getInputStream獲取服務端返回的數據流。 inputStream=httpURLConnection.getInputStream(); } } } catch (MalformedURLException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } return inputStream; } }

 

POST方式

 這個例子經過POST方式訪問一個登錄頁面,須要輸入用戶名(username)和密碼(password)。雖然這裏使用的Java在講解問 題,可是服務端是使用.Net的框架,一個很簡單的HTML頁面加一個表單傳送的通常處理程序,輸入爲admin+123爲登錄成功,這裏不累述了。
 

package com.http.post; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.UnsupportedEncodingException; import java.net.HttpURLConnection; import java.net.URL; import java.net.URLEncoder; import java.util.HashMap; import java.util.Map; public class postUtils { private static String PATH = "http://192.168.222.1:1231/loginas.ashx"; private static URL url; public postUtils() { } static { try { url = new URL(PATH); } catch (Exception e) { e.printStackTrace(); } } /** * 經過給定的請求參數和編碼格式,獲取服務器返回的數據 * @param params 請求參數 * @param encode 編碼格式 * @return 得到的字符串 */ public static String sendPostMessage(Map<String, String> params, String encode) { StringBuffer buffer = new StringBuffer(); if (params != null && !params.isEmpty()) { for (Map.Entry<String, String> entry : params.entrySet()) { try { buffer.append(entry.getKey()) .append("=") .append(URLEncoder.encode(entry.getValue(), encode)) .append("&");//請求的參數之間使用&分割。 } catch (UnsupportedEncodingException e) { e.printStackTrace(); } } buffer.deleteCharAt(buffer.length() - 1); System.out.println(buffer.toString()); try { HttpURLConnection urlConnection = (HttpURLConnection) url .openConnection(); urlConnection.setConnectTimeout(3000); //設置容許輸入輸出 urlConnection.setDoInput(true); urlConnection.setDoOutput(true); byte[] mydata = buffer.toString().getBytes(); //設置請求報文頭,設定請求數據類型 urlConnection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded"); //設置請求數據長度 urlConnection.setRequestProperty("Content-Length", String.valueOf(mydata.length)); //設置POST方式請求數據 urlConnection.setRequestMethod("POST"); OutputStream outputStream = urlConnection.getOutputStream(); outputStream.write(mydata); int responseCode = urlConnection.getResponseCode(); if (responseCode == 200) { return changeInputStream(urlConnection.getInputStream(), encode); } } catch (IOException e) { e.printStackTrace(); } } return ""; } /** * 把服務端返回的輸入流轉換成字符串格式 * @param inputStream 服務器返回的輸入流 * @param encode 編碼格式 * @return 解析後的字符串 */ private static String changeInputStream(InputStream inputStream, String encode) { ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); byte[] data = new byte[1024]; int len = 0; String result=""; if (inputStream != null) { try { while ((len = inputStream.read(data)) != -1) { outputStream.write(data,0,len); } result=new String(outputStream.toByteArray(),encode); } catch (IOException e) { e.printStackTrace(); } } return result; } /** * @param args */ public static void main(String[] args) { //經過Map設置請求字符串。 Map<String, String> params = new HashMap<String, String>(); params.put("username", "admin"); params.put("password", "123"); String result=sendPostMessage(params, "utf-8"); System.out.println(result); } }
相關文章
相關標籤/搜索