android端的的网络访问

一.Android端进行网络访问的几种实现方式

  • Socket

   套接字,为TCP/IP协议网络通信的网络操作单元;

   而抽象上来说:Socket只是一个供上层调用的抽象接口,相当于是传输层下的数据,还没经过应用层的封装,或者说不需要应用层的封装,因为直接使用socket连接的有两种情况,第一种情况就是直接获取传输层传输过来的输入流,按顺序合成之后就是一个完整的文件,或者是一个字符串等等不需要所谓的解析;另一种情况就是从socket连接返回的数据进行二次封装,进行应用层解析,达到大牛的网络访问框架,实现应用层封装,不过很多细节需要注意。

  • UrlConnection
    UrlConnection是基于Http协议的,Http协议是应用层的协议,基于Tcp/IP协议之上的协议,是Web浏览器和Web服务器之间的应用层的协议,基于传输层上再进行了一次协议封装,是无状态协议,不需要你往考虑线程、同步、状态治理等,内部是通过socket进行连接和收发数据的,不过一般在数据传输完成之后需要封闭socket连接

联系:UrlConnection基于Http协议,只不过多了封装,本质上也是建立Tcp连接,利用socket进行连接和数据传输,只不过每次连接之后都要手动关闭连接。因此直接使用Socket进行网络通讯得考虑线程治理、客户状态监控等,但是不用发送头信息等,更省流量。

区别

 二.客户端与服务器交互

客户端通过Internet去发送到服务器当中,而Internet内部可以通过三种方式来实现发送信息和数据:
  • 第一种:HTTP协议,也是在工作中最常用的,是建立在TCP/IP基础上实现的。
  • 第二种:FTP协议
  • 第三种:TCP/IP协议,它也是最底层的协议,其它的方式必须是要通过它,但是要想实现这种协议必须要实现socket编程,这种方法是用来上传一些比较大的文件,视频,进行断点续传的操作。

 

三.Android中达到网络访问的封装类/框架

  • HttpURLConnection
    HttpURLConnection只是继承UrlConnection,两者都是接口,只是在该接口的基础上进行简单封装

    从Android4.4开始HttpURLConnection的底层实现采用的是okHttp

  • HttpClient

 

   HttpClient就是对java提供的方法的一些封装,在HttpURLConnection的输入输出流操作,在HttpClient接口里直接封装成HttpPost、HttpGet、HttpResponse。很方便,另外需要注意的是post方式的情况下,我们需要进行字符编码,否则会出错。
   Apache HttpClient早就不推荐httpclient,5.0之后干脆废弃,后续会删除。6.0删除了HttpClient。

 

  • OkHttp
    okhttp是高性能的http库,支持同步、异步,而且实现了spdy、http2、websocket协议,api很简洁易用,和volley一样实现了http协议的缓存。picasso就是利用okhttp的缓存机制实现其文件缓存,实现的很优雅,很正确,反例就是UIL(universal image loader),自己做的文件缓存,而且不遵守http缓存机制。

   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接口

 输入流输出流详解

posted on 2017-04-20 17:32  右耳Deng  阅读(769)  评论(0编辑  收藏  举报

导航