IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 网络协议 -> Android 基础总结(十一)网络编程 -> 正文阅读

[网络协议]Android 基础总结(十一)网络编程

一、网络相关知识

1.1 网络分层

OSI七层模型
OSI七层协议模型主要是:应用层(Application)、表示层(Presentation)、会话层(Session)、传输层(Transport)、网络层(Network)、数据链路层(Data Link)、物理层(Physical)。

1.2 TCP/IP五层模型

TCP/IP五层模型:应用层(Application)、传输层(Transport)、网络层(Network)、数据链路层(Data Link)、物理层(Physical)。

1.3 Android与互联网交互的三种方式

在这里插入图片描述

1.4 TCP与UDP区别总结:

  • 1、TCP面向连接(如打电话要先拨号建立连接);UDP是无连接的,即发送数据之前不需要建立连接
  • 2、TCP提供可靠的服务。也就是说,通过TCP连接传送的数据,无差错,不丢失,不重复,且按序到达;UDP尽最大努力交付,即不保 证可靠交付
  • 3、TCP面向字节流,实际上是TCP把数据看成一连串无结构的字节流;UDP是面向报文的
    UDP没有拥塞控制,因此网络出现拥塞不会使源主机的发送速率降低(对实时应用很有用,如IP电话,实时视频会议等)
  • 4、每一条TCP连接只能是点到点的;UDP支持一对一,一对多,多对一和多对多的交互通信
  • 5、TCP首部开销20字节;UDP的首部开销小,只有8个字节
  • 6、TCP的逻辑通信信道是全双工的可靠信道,UDP则是不可靠信道

二、网络协议

2.1 Http

什么是Http协议?
hypertext transfer protocol(超文本传输协议),TCP/IP协议的一个应用层协议,用于 定义WEB浏览器与WEB服务器之间交换数据的过程。客户端连上web服务器后,若想获得web服务器 中的某个web资源,需遵守一定的通讯格式,HTTP协议用于定义客户端与web服务器通迅的格式。

主要特点

  • 支持C/S模式
  • 简单快速:只需传送请求方法和路径,请求常用的方法有:GET、POST、HEAD等
  • 灵活:允许传输任意类型的数据对象,用Content-Type进行标记
  • 无连接:限制每次连接只处理一个请求
  • 无状态:对事务处理没有记忆功能

HTTP的URL格式

  • http://host[:port] [/path]
    • http表示要通过HTTP协议来定位网络资源;
    • host表示合法的Internet主机域名或者IP地址;
    • port指定一个端口号,为空则使用默认端口80;
    • path指定请求资源的URI

Http协议的底层工作
两个名词:

  • SYN(synchronous):TCP/IP建立连接时使用的握手信号
  • ACK(Acknowledgement):确认字符,确认发来的数据已经接受无误

接着就到TCP/IP三次握手的概念:

  • 客户端发送syn包(syn = j)到服务器,进入SYN_SEND状态,然后等待服务器确认
  • 服务器收到syn包,确认客户的syn(ack = j + 1),同时在自己也发送一个SYN包(syn=k), 即SYN + ACK包,服务器进入SYN_RECV状态
  • 客户端收到SYN + ACK包,向服务器发送确认包ACK(ack = k +1),发送完毕后,客户端与服务端 进入ESTABLISHED状态,完成三次握手,然后两者开始传送数据

Http的几种请求方式

  • Get:请求获取Request-URI所标识的资源
  • POST:在Request-URI所标识的资源后附加新的数据
  • HEAD:请求获取由Request-URI所标识的资源的响应信息报头
  • PUT:请求服务器存储一个资源,并用Request-URI作为其标识
  • DELETE:请求服务器删除Request-URI所标识的资源
  • TRACE:请求服务器回送收到的请求信息,主要用于测试或诊断
  • CONNECT:保留将来使用
  • OPTIONS:请求查询服务器的性能,或者查询与资源相关的选项

Get和Post的对比
http是应用层的协议,底层基于TCP/IP协议,所以本质上,get和post请求都是TCP请求。所以二者的区别都是体现在应用层上(HTTP的规定和浏览器/服务器的限制)

  • 1.参数的传输方式:GET参数通过URL传递,POST放在Request body中。
  • 2.GET请求在URL中传送的参数是有长度限制的,而POST没有。
  • 3.对于GET方式的请求,浏览器会把http header和data一并发送出去,服务器响应200(返回数据);而对于POST,浏览器先发送header,服务器响应100 continue,浏览器再发送data,服务器响应200 ok(返回数据)。不过要注意,并不是所有浏览器都会在POST中发送两次包,比如火狐
  • 4.对参数的数据类型,GET只接受ASCII字符,而POST没有限制。
  • 5.GET比POST更不安全,因为参数直接暴露在URL上,所以不能用来传递敏感信息。
  • 6.GET请求只能进行url编码,而POST支持多种编码方式。
  • 7.GET在浏览器回退时是无害的,而POST会再次提交请求。
  • 8.GET产生的URL地址可以被Bookmark,而POST不可以。
  • 9.GET请求会被浏览器主动cache,而POST不会,除非手动设置。

Http状态码合集

  • 100~199 : 成功接受请求,客户端需提交下一次请求才能完成整个处理过程
  • 200: OK,客户端请求成功
  • 300~399:请求资源已移到新的地址(302,307,304)
  • 401:请求未授权,改状态代码需与WWW-Authenticate报头域一起使用
  • 403:Forbidden,服务器收到请求,但是拒绝提供服务
  • 404:Not Found,请求资源不存在,这个就不用说啦
  • 500:Internal Server Error,服务器发生不可预期的错误
  • 503:Server Unavailable,服务器当前不能处理客户端请求,一段时间后可能恢复正常

2.2 HTTPS

HTTPS(全称:Hyper Text Transfer Protocol over Secure Socket Layer),是以安全为目标的HTTP通道,简单讲是HTTP的安全版。
HTTP是应用层协议,位于HTTP协议之下是传输协议TCP。TCP负责传输,HTTPS则定义了数据如何进行包装,在HTTPS跟TCP中间加多了一层加密层TLS/SSL,SSL是个加密套件,负责对HTTPS的数据进行加密。TLS是SSL的升级版。现在提到HTTPS,加密套件基本指的是TLS。
传输加密的流程:http是应用层将数据直接给到TCP进行传输,https是应用层将数据给到TLS/SSL,将数据加密后,再给到TCP进行传输。
HTTPS是如何加密数据的:
一般来说,加密分为对称加密、非对称加密

对称加密:

  • 对称加密的意思就是,加密数据用的密钥,跟解密数据用的密钥是一样的。
    对称加密的优点在于加密、解密效率通常比较高。缺点在于,数据发送方、数据接收方需要协商、共享同一把密钥,并确保密钥不泄露给其他人。传输过程中容易被截获。
  • 网上一个很形象的例子:假如现在小客与小服要进行一次私密的对话,他们不希望这次对话内容被其他外人知道。可是,我们平时的数据传输过程中又是明文传输的,万一被某个黑客把他们的对话内容给窃取了,那就难受了。为了解决这个问题,小服这家伙想到了一个方法来加密数据,让黑客看不到具体的内容。该方法是这样子的:在每次数据传输之前,小服会先传输小客一把密钥,然后小服在之后给小客发消息的过程中,会用这把密钥对这些消息进行加密。小客在收到这些消息后,会用之前小服给的那把密钥对这些消息进行解密,这样,小客就能得到密文里面真正的数据了。如果小客要给小服发消息,也同样用这把密钥来对消息进行加密,小服收到后也用这把密钥进行解密。 这样,就保证了数据传输的安全性。

非对称加密

  • 非对称加密的意思就是,加密数据用的密钥(公钥),跟解密数据用的密钥(私钥)是不一样的。
  • 网上一个很形象的例子:小服还是挺聪明的,得意了一会之后,小服意识到了密钥会被截取这个问题。倔强的小服又想到了另外一种方法:用非对称加密的方法来加密数据。该方法是这样的:小服和小客都拥有两把钥匙,一把钥匙的公开的(全世界都知道也没关系),称之为公钥;而另一把钥匙是保密(也就是只有自己才知道),称之为私钥。并且,用公钥加密的数据,只有对应的私钥才能解密;用私钥加密的数据,只有对应的公钥才能解密。所以在传输数据的过程中,小服在给小客传输数据的过程中,会用小客给他的公钥进行加密,然后小客收到后,再用自己的私钥进行解密。小客给小服发消息的时候,也一样会用小服给他的公钥进行加密,然后小服再用自己的私钥进行解密。 这样,数据就能安全着到达双方。是什么原因导致非对称加密这种方法的不安全性呢?它和对称加密方法的不安全性不同。非对称加密之所以不安全,是因为小客收到了公钥之后,无法确定这把公钥是否真的是小服。
    解决的办法就是数字证书:小服再给小客发公钥的过程中,会把公钥以及小服的个人信息通过Hash算法生成消息摘要,为了防止摘要被人调换,小服还会用CA提供的私钥对消息摘要进行加密来形成数字签名,当小客拿到这份数字证书之后,就会用CA提供的公钥来对数字证书里面的数字签名进行解密得到消息摘要,然后对数字证书里面小服的公钥和个人信息进行Hash得到另一份消息摘要,然后把两份消息摘要进行对比,如果一样,则证明这些东西确实是小服的,否则就不是。

2.3 加密算法

对称加密算法

  • Data Encryption Standard(DES)
    DES 是一种典型的块加密方法:将固定长度的明文通过一系列复杂的操作变成同样长度的密文,块的长度为64位。同时,DES 使用的密钥来自定义变换过程,因此算法认为只有持有加密所用的密钥的用户才能解密密文。 DES 的密钥表面上是64位的,实际有效密钥长度为56位,其余8位可以用于奇偶校验。
    DES 现在已经不被视为一种安全的加密算法,主要原因是它使用的56位密钥过短。
    为了提供实用所需的安全性,可以使用 DES 的派生算法 3DES 来进行加密 (虽然3DES 也存在理论上的攻击方法)。
  • Advanced Encryption Standard(AES)
    AES 在密码学中又称 Rijndael 加密法,用来替代原先的 DES,已经被多方分析且广泛使用。
    DES与AES的比较
    自DES 算法公诸于世以来,学术界围绕它的安全性等方面进行了研究并展开了激烈的争论。在技术上,对DES的批评主要集中在以下几个方面:
    • 1、作为分组密码,DES 的加密单位仅有64 位二进制,这对于数据传输来说太小,因为每个分组仅含8 个字符,而且其中某些位还要用于奇偶校验或其他通讯开销。
    • 2、DES 的密钥的位数太短,只有56 比特,而且各次迭代中使用的密钥是递推产生的,这种相关必然降低密码体制的安全性,在现有技术下用穷举法寻找密钥已趋于可行。
    • 3、DES 不能对抗差分和线性密码分析。
    • 4、DES 用户实际使用的密钥长度为56bit,理论上最大加密强度为256。DES 算法要提高加密强度(例如增加密钥长度),则系统开销呈指数增长。除采用提高硬件功能和增加并行处理功能外,从算法本身和软件技术方面都无法提高DES 算法的加密强度。

非对称加密算法

  • RSA
    1977年由 MIT 的 Ron Rivest、Adi Shamir 和 Leonard Adleman 一起提出,以他们三人姓氏开头字母命名,是一种获得广泛使用的非对称加密算法。
    对极大整数做因数分解的难度 (The Factoring Problem) 决定了 RSA 算法的可靠性。换言之,对一个极大整数做因数分解愈困难,RSA 算法就愈可靠。假如有人找到一种快速因数分解的算法的话,那么用 RSA 加密的信息的可靠性就肯定会极度下降。目前看来找到这样的算法的可能性非常小。
  • DES与RSA的比较
    RSA算法的密钥很长,具有较好的安全性,但加密的计算量很大,加密速度较慢限制了其应用范围。为减少计算量,在传送信息时,常采用传统加密方法与公开密钥加密方法相结合的方式,即信息采用改进的DES对话密钥加密,然后使用RSA密钥加密对话密钥和信息摘要。对方收到信息后,用不同的密钥解密并可核对信息摘要。
    采用DES与RSA相结合的应用,使它们的优缺点正好互补,即DES加密速度快,适合加密较长的报文,可用其加密明文;RSA加密速度慢,安全性好,应用于DES 密钥的加密,可解决DES 密钥分配的问题。
    目前这种RSA和DES结合的方法已成为EMAIL保密通信标准。

三、常用的网络编程框架

3.1 Volley

  • Volley的特点
    Volley是谷歌大会上推出的网络通信框架(2.3之前使用HttpClient,之后使用HttpUrlConnection),它既可以访问网络获取数据,也可以加载图片,并且在性能方面进行了大幅度的调整,它的设计目的就是适合进行数据量不大但通信频繁的网络操作,而对于大数据量的操作,比如文件下载,表现很糟糕,因为volley处理http返回的默认实现是BasicNetwork,它会把返回的流全部导入内存中,下载大文件会发生内存溢出
  • Volley执行的过程:
    默认情况下,Volley中开启四个网络调度线程和一个缓存调度线程,首先请求会加入缓存队列,,缓存调度线程从缓存队列中取出线程,如果找到该请求的缓存就直接读取该缓存并解析,然后回调给主线程,如果没有找到缓存的响应,则将这个请求加入网络队列,然后网络调度线程会轮询取出网络队列中的请求,发起http请求,解析响应并将响应存入缓存,回调给主线程
  • Volley为什么不适合下载上传大文件?为什么适合数据量小的频率高的请求?
    1.volley基于请求队列,Volley的网络请求线程池默认大小为4。意味着可以并发进行4个请求,大于4个,会排在队列中。并发量小所以适合数据量下频率高的请求
    2.因为Volley下载文件会将流存入内存中(是一个小于4k的缓存池),大文件会导致内存溢出,所以不能下载大文件,不能上传大文件的原因和1中差不多,设想你上传了四个大文件,同时占用了volley的四个线程,导致其他网络请求都阻塞在队列中,造成反应慢的现象

3.2 Retrofit

Retrofit底层是基于OkHttp实现的,与其他网络框架不同的是,它更多使用运行时注解的方式提供功能

  • 原理
    通过java接口以及注解来描述网络请求,并用动态代理的方式生成网络请求的request,然后通过client调用相应的网络框架(默认okhttp)去发起网络请求,并将返回的response通过converterFactorty转换成相应的数据model,最后通过calladapter转换成其他数据方式(如rxjava Observable)
  • Retrofit流程
    • ①通过解析 网络请求接口的注解 配置 网络请求参数
    • ②通过 动态代理 生成 网络请求对象
    • ③通过 网络请求适配器 将 网络请求对象 进行平台适配
    • ④通过 网络请求执行器 发送网络请求
    • ⑤通过 数据转换器 解析服务器返回的数据
    • ⑥通过 回调执行器 切换线程(子线程 ->>主线程)

用户在主线程处理返回结果

  • Retrofit优点
    • ①.可以配置不同HTTP client来实现网络请求,如okhttp、httpclient等;
    • ②.请求的方法参数注解都可以定制;
    • ③.支持同步、异步和RxJava;
    • ④.超级解耦;
    • ⑤.可以配置不同的反序列化工具来解析数据,如json、xml等
    • ⑥.框架使用了很多设计模式

3.3 Okhttp

Okhttp:高性能的http库,支持同步、异步、而且支持http2、websocket协议,api简洁易用,实现了http缓存
Android网络访问的源码已用okHttp代替了HttpURLConnection

  • GET异步请求
private void get(String url) {
        // 1. 构造Request
        final Request request = new Request.Builder().url( url )
                .header( "User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64)" +
                        "AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.120 Safari/537.36 " )
                .addHeader( "Accept", "application/json" )
                .get()
                .method( "GET", null )
                .build();

        // 2. 发送请求,并处理回调
        OkHttpClient client = HttpsUtil.handleSSLHandshakeByOkHttp();
        client.newCall( request ).enqueue( new Callback() {
            //失败
            @Override
            public void onFailure(Call call, IOException e) {
                Log.e( "OkHttpActivity", e.getMessage() );
            }

            //回应
            @Override
            public void onResponse(@NonNull Call call, @NonNull Response response) throws IOException {
                // 1. 获取响应主体的json字符串
                if (response.isSuccessful()) {
                    String json = response.body().string();
                    final Ip ip = JSON.parseObject( json, Ip.class );
                    // 2. 使用FastJson库解析json字符串
                    runOnUiThread( new Runnable() {
                        @Override
                        public void run() {
                            // 3. 根据返回的code判断获取是否成功
                            if (ip.getCode() != 0) {
                                tv_textView.setText( "未获得数据" );
                            } else {
                                // 4. 解析数据
                                IpData data = ip.getIpdata();
                                tv_textView.setText( data.getIp() + "," + data.getCity() );
                            }
                        }
                    } );
                }
            }
        } );
  • POST异步请求
 //post请求
    private void post(String url, Map <String, String> params) {
        // 1. 构建RequestBody
        RequestBody body = setRequestBody( params );
        // 2. 创建Request对象
        Request request = new Request.Builder().url( url ).post( body )
                .header( "User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64)" +
                        "AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.120 Safari/537.36 " )
                .addHeader( "Accept", "application/json" )
                .build();

        // 2. 发送请求,并处理回调
        OkHttpClient client = HttpsUtil.handleSSLHandshakeByOkHttp();
        client.newCall( request ).enqueue( new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {
                Log.e( "OkHttpActivity", e.getMessage() );
            }

            @Override
            public void onResponse(Call call, Response response) throws IOException {
                if (response.isSuccessful()) {
                    // 1. 获取响应主体的json字符串
                    String json = response.body().string();

                    // 2. 使用FastJson库解析json字符串
                    final Ip ip = JSON.parseObject( json, Ip.class );
                    runOnUiThread( new Runnable() {
                        @Override
                        public void run() {
                            // 3. 根据返回的code判断获取是否成功
                            if (ip.getCode() != 0) {
                                tv_textView.setText( "未获得数据" );
                            } else {
                                // 4. 解析数据
                                IpData data = ip.getIpdata();
                                tv_textView.setText( data.getIp() + "," + data.getCity() );
                            }
                        }
                    } );
                }
            }
        } );
    }
        //将请求的参数组装成RequestBody
    private RequestBody setRequestBody(Map <String, String> params) {
        FormBody.Builder builder = new FormBody.Builder();
        for (String key : params.keySet()) {
            builder.add( key, params.get( key ) );
        }
        return builder.build();
    }

  • 上传文件
   //上传文件
    private void uploadFile(String url, String fileName) {
        Request request = new Request.Builder().url( url )
                .post( RequestBody.create( MEDIA_TYPE_MARKDOWN, new File( fileName ) ) )
                .build();
        OkHttpClient client = HttpsUtil.handleSSLHandshakeByOkHttp();
        client.newCall( request ).enqueue( new Callback() {
            @Override
            public void onFailure(Call call, final IOException e) {
                Log.e( TAG, e.getMessage() );
                tv_textView.post( new Runnable() {
                    @Override
                    public void run() {
                        tv_textView.setText( "上传失败," + e.getMessage() );
                    }
                } );
            }

            @Override
            public void onResponse(Call call, Response response) throws IOException {
                if (response.isSuccessful()) {
                    final String str = response.body().string();
                    runOnUiThread( new Runnable() {
                        @Override
                        public void run() {
                            tv_textView.setText( "上传成功," + str );
                        }
                    } );
                } else {
                    Log.d( TAG, response.body().string() );
                }
            }
        } );
    }
  • 下载文件
  //下载文件
    public static void writeFile(InputStream is, String path, String fileName) throws IOException {
        // 1. 根据path创建目录对象,并检查path是否存在,不存在则创建
        File directory = new File( path );
        if (!directory.exists()) {
            directory.mkdirs();
        }
        // 2. 根据path和fileName创建文件对象,如果文件存在则删除
        File file = new File( path, fileName );
        if (file.exists()) {
            file.delete();
        }
        // 3. 创建文件输出流对象,根据输入流创建缓冲输入流对象
        FileOutputStream fos = new FileOutputStream( file );
        BufferedInputStream bis = new BufferedInputStream( is );

        // 4. 以每次1024个字节写入输出流对象
        byte[] buffer = new byte[1024];
        int len;
        while ((len = bis.read( buffer )) != -1) {
            fos.write( buffer, 0, len );
        }
        fos.flush();
        // 5. 关闭输入流、输出流对象
        fos.close();
        bis.close();
    }

    private void downFile(final String url, final String path) {
        // 1. 创建Requet对象
        Request request = new Request.Builder().url( url ).build();

        // 2. 创建OkHttpClient对象,发送请求,并处理回调
        OkHttpClient client = HttpsUtil.handleSSLHandshakeByOkHttp();
        client.newCall( request ).enqueue( new Callback() {
            @Override
            public void onResponse(Call call, Response response) throws IOException {
                if (response.isSuccessful()) {
                    // 1. 获取下载文件的后缀名
                    String ext = url.substring( url.lastIndexOf( "." ) + 1 );
                    // 2. 根据当前时间创建文件名,避免重名冲突
                    final String fileName = System.currentTimeMillis() + "." + ext;
                    // 3. 获取响应主体的字节流
                    InputStream is = response.body().byteStream();
                    // 4. 将文件写入path目录
                    writeFile( is, path, fileName );
                    // 5. 在界面给出提示信息
                    tv_textView.post( new Runnable() {
                        @Override
                        public void run() {
                            tv_textView.setText( fileName + "下载成功,存放在" + path );
                        }
                    } );
                }
            }

            @Override
            public void onFailure(Call call, IOException e) {
                Log.e( TAG, e.getMessage() );
                runOnUiThread( new Runnable() {
                    @Override
                    public void run() {
                        tv_textView.setText( "下载文件失败" );
                    }
                } );
            }
        } );
    }
  网络协议 最新文章
使用Easyswoole 搭建简单的Websoket服务
常见的数据通信方式有哪些?
Openssl 1024bit RSA算法---公私钥获取和处
HTTPS协议的密钥交换流程
《小白WEB安全入门》03. 漏洞篇
HttpRunner4.x 安装与使用
2021-07-04
手写RPC学习笔记
K8S高可用版本部署
mySQL计算IP地址范围
上一篇文章      下一篇文章      查看所有文章
加:2021-09-22 15:00:59  更:2021-09-22 15:02:17 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/26 2:01:45-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码