android端的的網絡訪問

一.Android端進行網絡訪問的幾種實現方式

  • Socket

   套接字,爲TCP/IP協議網絡通訊的網絡操做單元;html

   而抽象上來講:Socket只是一個供上層調用的抽象接口,至關因而傳輸層下的數據,還沒通過應用層的封裝,或者說不須要應用層的封裝,由於直接使用socket鏈接的有兩種狀況,第一種狀況就是直接獲取傳輸層傳輸過來的輸入流,按順序合成以後就是一個完整的文件,或者是一個字符串等等不須要所謂的解析;另外一種狀況就是從socket鏈接返回的數據進行二次封裝,進行應用層解析,達到大牛的網絡訪問框架,實現應用層封裝,不過不少細節須要注意。java

  • UrlConnection
    UrlConnection是基於Http協議的,Http協議是應用層的協議,基於Tcp/IP協議之上的協議,是Web瀏覽器和Web服務器之間的應用層的協議,基於傳輸層上再進行了一次協議封裝,是無狀態協議,不須要你往考慮線程、同步、狀態治理等,內部是經過socket進行鏈接和收發數據的,不過通常在數據傳輸完成以後須要封閉socket鏈接

聯繫:UrlConnection基於Http協議,只不過多了封裝,本質上也是創建Tcp鏈接,利用socket進行鏈接和數據傳輸,只不過每次鏈接以後都要手動關閉鏈接。所以直接使用Socket進行網絡通信得考慮線程治理、客戶狀態監控等,可是不用發送頭信息等,更省流量。android

區別web

 二.客戶端與服務器交互

客戶端經過Internet去發送到服務器當中,而Internet內部能夠經過三種方式來實現發送信息和數據:
  • 第一種:HTTP協議,也是在工做中最經常使用的,是創建在TCP/IP基礎上實現的。
  • 第二種:FTP協議
  • 第三種:TCP/IP協議,它也是最底層的協議,其它的方式必須是要經過它,可是要想實現這種協議必需要實現socket編程,這種方法是用來上傳一些比較大的文件,視頻,進行斷點續傳的操做。

 

三.Android中達到網絡訪問的封裝類/框架

  • HttpURLConnection
    HttpURLConnection只是繼承UrlConnection,二者都是接口,只是在該接口的基礎上進行簡單封裝apache

    從Android4.4開始HttpURLConnection的底層實現採用的是okHttp編程

  • HttpClientjson

 

   HttpClient就是對java提供的方法的一些封裝,在HttpURLConnection的輸入輸出流操做,在HttpClient接口裏直接封裝成HttpPost、HttpGet、HttpResponse。很方便,另外須要注意的是post方式的狀況下,咱們須要進行字符編碼,不然會出錯。
   Apache HttpClient早就不推薦httpclient,5.0以後乾脆廢棄,後續會刪除。6.0刪除了HttpClient。設計模式

 

  • OkHttp
    okhttp是高性能的http庫,支持同步、異步,並且實現了spdy、http二、websocket協議,api很簡潔易用,和volley同樣實現了http協議的緩存。picasso就是利用okhttp的緩存機制實現其文件緩存,實現的很優雅,很正確,反例就是UIL(universal image loader),本身作的文件緩存,並且不遵照http緩存機制。api

   OkHttp的最底層是Socket,而不是HTTP,它經過Platform的Class.forName()反射得到當前Runtime使用的socket庫跨域

  • volley
    volley是一個簡單的異步http庫,僅此而已。缺點是不支持同步,這點會限制開發模式。自帶緩存,支持自定義請求。不適合大文件上傳和下載。
    Volley在Android 2.3及以上版本,使用的是HttpURLConnection,而在Android 2.2及如下版本,使用的是HttpClient。
    Volley本身的定位是輕量級網絡交互,適合大量的,小數據傳輸。
    不過再怎麼封裝Volley在功能拓展性上始終沒法與OkHttp相比。Volley中止了更新,而OkHttp獲得了官方的承認,並在不斷優化。

  • android-async-http。
    與volley同樣是異步網絡庫,但volley是封裝的httpUrlConnection,它是封裝的httpClient,而android平臺不推薦用HttpClient了,因此這個庫已經不適合android平臺了。

  • Retrofit
    Retrofit 是一個 RESTful 的 HTTP 網絡請求框架的封裝。注意這裏並無說它是網絡請求框架,主要緣由在於網絡請求的工做並非 Retrofit 來完成的。Retrofit 2.0 開始內置 OkHttp,前者專一於接口的封裝,後者專一於網絡請求的高效,兩者分工協做,宛如古人的『你耕地來我織布』,小日子別提多幸福了。參考深刻淺出 Retrofit
    retrofit與picasso同樣都是在okhttp基礎之上作的封裝,項目中能夠直接用了。Retrofit由於也是square出的,因此你們可能對它更崇拜些。Retrofit的跟Volley是一個套路,但解耦的更完全:比方說經過註解來配置請求參數,經過工廠來生成CallAdapter,Converter,你可使用不一樣的請求適配器(CallAdapter), 比方說RxJava,Java8, Guava。你可使用不一樣的反序列化工具(Converter),比方說json, protobuff, xml, moshi等等。炒雞解耦,裏面涉及到超多設計模式,我的以爲是很經典的學習案例。雖然支持Java8, Guava你可能也不須要用到。xml,protobuff等數據格式你也可能不須要解析。but,萬一遇到鬼了呢。至於性能上,我的以爲這徹底取決於請求client,也就是okhttp的性能,跟這些封裝工具沒太大關係。

四.HttpUrlConnection和HttpClient的簡單使用

對於咱們熟知的網絡訪問工具類HttpURLConnection和HttpClient,這兩個接口均可以用來開發Http訪問。

 

須要引入httpClient包,在本人AS環境,SDK處於23的狀況下,須要引入:

android {
useLibrary 'org.apache.http.legacy'//httpClient須要包
}

須要的權限:

<uses-permission android:name="android.permission.INTERNET" /> 

 

簡單的使用:

httpURLConnection、HttpClient:

/**
     * 使用HTTPUrlConnection例子
     * @param username
     * @param password
     * @return
     */
    public static String login(String username,String password){
        String msg = "";
        try {
            username = URLEncoder.encode(username,"UTF-8");//這裏要注意編碼,若是參數含有漢字或是空格(尤爲是日期中的空格),不編碼會發生錯誤
            password = URLEncoder.encode(password,"UTF-8");
        } catch (UnsupportedEncodingException e1) {
            // TODO Auto-generated catch block
            e1.printStackTrace();
        }
        //要訪問的HttpServlet
        String urlStr="http://127.0.0.1:8080/MyProject/getUser?";
        //要傳遞的數
        String params ="username="+username+"&password="+password;
        urlStr = urlStr+params;
        try{
            URL url =new URL(urlStr);
            //得到鏈接
            HttpURLConnection conn = (HttpURLConnection)url.openConnection();
            conn.setConnectTimeout(6000);
            conn.setRequestMethod("GET");//請求方式
            InputStream in = conn.getInputStream();

            BufferedReader reader = new BufferedReader(new InputStreamReader(in, HTTP.UTF_8));
            String line = null;
            while ((line = reader.readLine()) != null) {
                if(msg==null){
                    msg=line;
                }else{
                    msg += line;
                }
            }
            reader.close();
            in.close();//關閉數據流
            conn.disconnect();
        }catch(Exception e){
            e.printStackTrace();
            return null;
        }
        return msg;
    }

    /**
     * 使用HttpClient訪問,get方式,若是sdk版本爲23,須要引入org.apache.http.legacy
     * @return
     */
    private static String loginHttpClientGet(){
        // http地址
        String httpUrl = "http://192.168.1.110:8080/httpget.jsp?par=HttpClient_android_Get";
        //HttpGet鏈接對象
        HttpGet httpRequest = new HttpGet(httpUrl);
        String strResult = "";
        try
        {
            //取得HttpClient對象
            HttpClient httpclient = new DefaultHttpClient();
            //請求HttpClient,取得HttpResponse
            HttpResponse httpResponse = httpclient.execute(httpRequest);
            //請求成功
            if (httpResponse.getStatusLine().getStatusCode() == HttpStatus.SC_OK)
            {
                //取得返回的字符串
                strResult = EntityUtils.toString(httpResponse.getEntity());
//                mTextView.setText(strResult);
            }
            else
            {
//                mTextView.setText("請求錯誤!");
            }
            return strResult;
        }
        catch (ClientProtocolException e)
        {
//            mTextView.setText(e.getMessage().toString());
        }
        catch (IOException e)
        {
//            mTextView.setText(e.getMessage().toString());
        }
        catch (Exception e)
        {
//            mTextView.setText(e.getMessage().toString());
        }
        return strResult;

    }

    /**
     * 使用HttpClient訪問,post方式
     * @return
     */
    private static String loginHttpClientPost(){
        // http地址
        String httpUrl = "http://192.168.1.110:8080/httpget.jsp";
        //HttpPost鏈接對象
        HttpPost httpRequest = new HttpPost(httpUrl);
        //使用NameValuePair來保存要傳遞的Post參數
        List<NameValuePair> params = new ArrayList<NameValuePair>();
        //添加要傳遞的參數
        params.add(new BasicNameValuePair("par", "HttpClient_android_Post"));
        String strResult = "";
        try
        {
            //設置字符集
            HttpEntity httpentity = new UrlEncodedFormEntity(params, "gb2312");
            //請求httpRequest
            httpRequest.setEntity(httpentity);
            //取得默認的HttpClient
            HttpClient httpclient = new DefaultHttpClient();
            //取得HttpResponse
            HttpResponse httpResponse = httpclient.execute(httpRequest);
            //HttpStatus.SC_OK表示鏈接成功
            if (httpResponse.getStatusLine().getStatusCode() == HttpStatus.SC_OK)
            {
                //取得返回的字符串
                strResult = EntityUtils.toString(httpResponse.getEntity());
//                mTextView.setText(strResult);
            }
            else
            {
//                mTextView.setText("請求錯誤!");
            }
            return strResult;
        }
        catch (ClientProtocolException e)
        {
//            mTextView.setText(e.getMessage().toString());
        }
        catch (IOException e)
        {
//            mTextView.setText(e.getMessage().toString());
        }
        catch (Exception e)
        {
//            mTextView.setText(e.getMessage().toString());
        }
        return strResult;
    }
View Code

socket:

//服務器端
public class MyServer {  
      
    private  static int count=0;  
    public static void main(String[]args){  
          
        try {  
            //實例化服務器套接字 設置端口號8888  
            ServerSocket server=new ServerSocket(8888);  
            while(true){  
                //鏈接編號設置  
                count=count+1;  
                //時間格式  
                SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");  
                //實例化客戶端  
                Socket client=server.accept();  
                //實例化時間  以及 id  
                System.out.println(count+":"+sdf.format(System.currentTimeMillis()));  
                //獲取輸出流  
                OutputStream out=client.getOutputStream();  
                //輸出字符串  
                String msg="Hello,Android!";  
                //寫字符串  
                out.write(msg.getBytes());  
            }  
        } catch (IOException e) {  
            // TODO Auto-generated catch block  
            e.printStackTrace();  
        }  
          
          
    }  
}  


//客戶端
public class MyClientActivity extends Activity {  
    /** Called when the activity is first created. */  
    private Button rev=null;  
    private TextView revtext=null;  
    @Override  
    public void onCreate(Bundle savedInstanceState) {  
        super.onCreate(savedInstanceState);  
        setContentView(R.layout.main);  
        rev=(Button)findViewById(R.id.rev);      
        revtext=(TextView)findViewById(R.id.receiver);  
        rev.setOnClickListener(new receiverlistenr());  
    }  
    class receiverlistenr implements OnClickListener{  
        public void onClick(View v) {  
            // TODO Auto-generated method stub  
            try {  
                //實例化Socket  
                Socket socket=new Socket("169.254.202.149",8888);  
                //得到輸入流  
                InputStream in=socket.getInputStream();  
                //緩衝區  
                byte[] buffer=new byte[in.available()];  
                //讀取緩衝區  
                in.read(buffer);  
                //轉換字符串  
                String msg=new String(buffer);  
                //設置文本框的字符串  
                revtext.setText(msg);  
            } catch (UnknownHostException e) {  
                // TODO Auto-generated catch block  
                e.printStackTrace();  
            } catch (IOException e) {  
                // TODO Auto-generated catch block  
                e.printStackTrace();  
            }  
              
        }  
    }  
}  
View Code

 Socket鏈接---至少須要一對套接字,其中一個運行於客戶端,稱爲ClientSocket ,另外一個運行於服務器端,稱爲ServerSocket套接字之間的鏈接過程分爲三個步驟:服務器監聽,客戶端請求,鏈接確認

 

四.請求報文與響應報文

請求報文的通常格式:

這裏寫圖片描述

一般來講一個HTTP請求報文由請求行、請求報頭、空行、和請求數據4個部分組成。

 

GET http://blog.csdn.net/itachi85 HTTP/1.1                                //請求行
Host: blog.csdn.net                                                       //請求報頭
Connection: keep-alive
Cache-Control: max-age=0       
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/47.0.2526.80 Safari/537.36 QQBrowser/9.3.6872.400
Accept-Encoding: gzip, deflate, sdch
Accept-Language: zh-CN,zh;q=0.8
Cookie: bdshare_firstime=1443768140949; uuid_tt_dd=5028529250430960147_20151002;
                                                                       //不能省略的空格
28b5    }ysI 1ߡFsgl n- ]{^_ { 'z! C , m# 0 !l   ` 4x ly .ݪ* 
...省略

 

 

 

響應報文的通常格式: 
這裏寫圖片描述

HTTP的響應報文由狀態行、消息報頭、空行、響應正文組成。

 

HTTP/1.1 200 OK                                                         //狀態行
Server: openresty                                                       //響應報頭
Date: Sun, 27 Mar 2016 08:26:54 GMT
Content-Type: text/html; charset=utf-8
Transfer-Encoding: chunked
Connection: keep-alive
Keep-Alive: timeout=20
Vary: Accept-Encoding
Cache-Control: private
X-Powered-By: PHP 5.4.28
Content-Encoding: gzip
                                                                        //不能省略的空格
28b5                                    
        }ysI   1ߡFsgl n- ]{^_ { 'z!     C ,  m# 0 !l   `  4x  ly .ݪ*  
  ڴzAt_Xl *  9'O  ɬ  '  ק   3  ^1a
...省略  

 

若是是請求文件(下載)

 

要從文件已經下載的地方開始繼續下載。在之前版本的 HTTP 協議是不支持斷點的,HTTP/1.1 開始就支持了。通常斷點下載時纔用到 Range 和 Content-Range 實體頭。

Range 

用於請求頭中,指定第一個字節的位置和最後一個字節的位置,通常格式:

Range:(unit=first byte pos)-[last byte pos] 

Content-Range

用於響應頭,指定整個實體中的一部分的插入位置,他也指示了整個實體的長度。在服務器向客戶返回一個部分響應,它必須描述響應覆蓋的範圍和整個實體長度。通常格式: 

Content-Range: bytes (unit first byte pos) - [last byte pos]/[entity legth] 

請求下載整個文件: 

  1. GET /test.rar HTTP/1.1 
  2. Connection: close 
  3. Host: 116.1.219.219 
  4. Range: bytes=0-801 //通常請求下載整個文件是bytes=0- 或不用這個頭

通常正常回應

  1. HTTP/1.1 200 OK 
  2. Content-Length: 801      
  3. Content-Type: application/octet-stream 
  4. Content-Range: bytes 0-800/801 //801:文件總大小

 

 

注意:對於socket網絡訪問

服務器端:根據serversocket.accept()接收到請求socket,socket的inputstream爲請求報文(與Http請求報文一致),而且把數據寫入到socket的outputStream中(數據格式與HTTP響應報文一致)。

客戶端:根據socket,inputStream爲服務器返回的數據

 

五.HTTP的請求方式(八種)的使用環境

RFC2616標準(現行的HTTP/1.1)得知有如下8種方法:OPTIONSGETHEAD、POST、PUT、DELETE、TRACE和CONNECT。

con.setRequestMethod("");//設置請求狀態

 

HTTP請求方法有8種,分別是GET、POST、DELETE、PUT、HEAD、TRACE、CONNECT 、OPTIONS。其中PUT、DELETE、POST、GET分別對應着增刪改查,對於移動開發最經常使用的就是POST和GET了。

  1. GET:請求獲取Request-URI所標識的資源(查)
  2. POST:在Request-URI所標識的資源後附加新的數據(增)
  3. HEAD:請求獲取由Request-URI所標識的資源的響應消息報頭,相似於GET, 可是不返回body信息,用於檢查對象是否存在,以及獲得對象的元數據
  4. PUT: 請求服務器存儲一個資源,並用Request-URI做爲其標識(改)
  5. DELETE :請求服務器刪除Request-URI所標識的資源(刪)
  6. TRACE : 請求服務器回送收到的請求信息,主要用於測試或診斷
  7. CONNECT: HTTP/1.1協議中預留給可以將鏈接改成管道方式的代理服務器。用於代理進行傳輸,如使用SSL
  8. OPTIONS :請求查詢服務器的性能,或者查詢與資源相關的選項和需求
    1. 獲取服務器支持的HTTP請求方法;也是黑客常用的方法。
    2. 用來檢查服務器的性能。例如:AJAX進行跨域請求時的預檢,須要向另一個域名的資源發送一個HTTP OPTIONS請求頭,用以判斷實際發送的請求是否安全。

  9. PATCH,部分文檔更改
  10. PROPFIND查看屬性
  11. PROPPATCH, 設置屬性
  12. MKCOL,建立集合(文件夾)
  13. COPY, 拷貝
  14. MOVE,移動
  15. LOCK, 加鎖
  16. UNLOCK,解鎖
  17. TRACE,用於遠程診斷服務器

 

 

參考連接:

HTTP協議原理

HTTP協議請求報文和響應報文格式

URLConnection/HttpURLConnection/HttpClient/socket 差異

UrlConnection鏈接和Socket鏈接的區別

httpurlconnect接口,httpclient接口,與socket接口

 輸入流輸出流詳解

相關文章
相關標籤/搜索