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

     输入流输出流详解

  • 相关阅读:
    Oracle学习(一)SQL基础
    结构型设计模式(二)桥接模式
    dubbo学习(十)spring boot整合dubbo
    dubbo学习(九)dubbo监控中心
    结构型设计模式(一)适配器模式
    取石子游戏
    卡特兰数
    做题中踩过的坑。。。
    51Nod1130斯特林近似
    51Nod1089最长回文子串 V2(Manacher算法)
  • 原文地址:https://www.cnblogs.com/could-deng/p/6739927.html
Copyright © 2020-2023  润新知