写点什么

Android 开发经验分享:挺重要的网络基础,android 实现选择题模式

用户头像
Android架构
关注
发布于: 刚刚

3.网络层:这一层 IP 协议,以及一些路由选择协议等等,所以这一层的指定了数据要传输到哪个 IP 地址。中间涉及到一些最优线路,路由选择算法等等。


4.数据链路层:印象比较深的就是 ARP 协议,负责把 IP 地址解析为 MAC 地址,即硬件地址,这样就找到了对应的唯一的机器。


5.物理层:这一层就是最底层了,提供二进制流传输服务,也就是也就是真正开始通过传输介质(有线、无线)开始进行数据的传输了。


所以通过上面五层的各司其职,实现物理传输介质–MAC 地址–IP 地址–端口号–获取到数据根据应用层协议解析数据最终实现了网络通信和数据传输。


下面会着重讲一下 HTTP 和 TCP 相关的东西,关于其他层,毕业了这么久也忘的很多,如果想更加细致具体的了解像下面三层的如路由选择算法、ARP 寻址以及物理层等等还是要重新去看一下《计算机网络原理》~


2 HTTP 相关


============================================================================


这里主要讲一些关于 Http 的基础知识,以及在 Android 中的一些实际应用和碰到的问题和解决。


一、无连接与无状态


Http 是无连接无状态的。


无连接并不是说不需要连接,Http 协议只是一个应用层协议,最终还是要靠运输层的如 TCP 协议向上提供的服务进行连接。


无连接的含义是 http 约定了每次连接只处理一个请求,一次请求完成后就断开连接,这样主要是为了缓解服务器的压力,减小连接对服务器资源的占用。我的理解是,建立连接实际上是运输层的事,面向应用层的 http 来说的话,它就是无连接的,因为上层对下


《Android学习笔记总结+最新移动架构视频+大厂安卓面试真题+项目实战源码讲义》
浏览器打开:qq.cn.hn/FTe 免费领取
复制代码


层无感知。


无状态的指每个请求之间都是独立的,对于之前的请求事务没有记忆的能力。所以就出现了像 Cookie 这种,用来保存一些状态的东西。


二、请求报文与响应报文


这里主要简单说一下请求报文和响应报文的格式:


请求报文:



响应报文:



关于 Get 和 Post:我们都熟知的关于 Get 和 Post 的区别大致有以下几点:


Get 会把请求参数都拼接在 url 后面,最终显示在地址栏,而 Post 则会把请求参数数据放进请求体中,不会再地址栏显示出来


传递参数的长度限制


问题:


对于第一点,如果是在浏览器里把隐私数据暴露在地址栏上确实不妥,但是如果是在 App 开发中呢,没有地址栏的概念,那么这一点是不是还会成为选择 post 还是 get 的制约条件。


对于第二点,长度的限制应该是浏览器的限制,跟 get 本身无关,如果是在 App 开发中,这一点是否也可以忽略。


三、HTTP 的缓存机制


之所以想介绍以下 Http 的缓存机制,是因为 Okhttp 中对于网络请求缓存这一块就是利用了 Http 的的缓存机制,而不是像 Volley 等框架那样客户端完全自己写一套缓存策略自己玩。


Http 的缓存主要利用 header 里的两个字段来控制:


  1. Cache-control 主要包含以及几个字段:


private:则只有客户端可以缓存


public:客户端和代理服务器都可以缓存


max-age:缓存的过期时间


no-cache:需要使用对比缓存来验证缓存数据


no-store:所有内存都不会进行缓存


实际上就是在这里面设置了一个缓存策略,由服务端第一次通过 header 下发给客户端,可以看到:


max-age 即缓存过期的时间,则之后再次请求,如果没有超过缓存失效的时间则可以直接使用缓存。


no-cache:表示需要使用对比缓存来验证缓存数据,如果这个字段是打开的,则就算 max-age 缓存没有失效,则还是需要发起一次请求向服务端确认一下资源是否有更新,是否需要重新请求数据,至于怎么做对比缓存,就是下面要说的 Etag 的作用。如果服务端确认资源没有更新,则返回 304,取本地缓存即可,如果有更新,则返回最新的资源。


no-store:这个字段打开,则不会进行缓存,也不会取缓存。


2.ETag:即用来进行对比缓存,Etag 是服务端资源的一个标识码


当客户端发送第一次请求时服务端会下发当前请求资源的标识码 Etag,下次再请求时,客户端则会通过 header 里的 If-None-Match 将这个标识码 Etag 带上,服务端将客户端传来的 Etag 与最新的资源 Etag 做对比,如果一样,则表示资源没有更新,返回 304。


通过 Cache-control 和 Etag 的配合来实现 Http 的缓存机制。


四、Cookie


上面说了 Http 协议是无状态的,而 Cookie 就是用来在本地缓存记住一些状态的,一个 Cookie 一般都包含 domin(所属域)、path、Expires(过期时间)等几个属性。服务端可以通过在响应头里的 set-cookies 来将状态写入客户端的 Cookie 里。下次客户端发起请求时可以将 Cookie 带上。


Android 开发中遇到的问题及解决:


说起 Cookie,一般如果平常只是做 App 开发,比较不经常遇到,但是如果是涉及到 WebView 的需求,则有可能会遇到。


下面就说一下我在项目里遇到过的一个关于 WebView Cookie 的揪心往事:需求是这样的,加载的 WebView 中的 H5 页面需要是已登录状态的,所以我们需要在原生页面登录后,手动将 ticket 写入 WebView 的 Cookie,之后 WebView 里加载的 H5 页面带着 Cookie 里的 ticket 给服务端验证通过就好了。


但是遇到一个问题:通过 Chrome inspect 调试 WebView,手动写的 Cookie 确实是已经写进去了,但是发起请求的时候,Cookie 就是没有带上,导致请求验证失败,之后通过排查,是 WebView 的属性默认关闭引起,通过下面的代码设置打开即可:


CookieManager cookieManager = CookieManager.getInstance();


if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {


cookieManager.setAcceptThirdPartyCookies(mWebView, true);


} else {


cookieManager.setAcceptCookie(true);


}


五、Https


我们都知道 Https 保证了我们数据传输的安全,Https=Http+Ssl,之所以能保证安全主要的原理就是利用了非对称加密算法,平常用的对称加密算法之所以不安全,是因为双方是用统一的密匙进行加密解密的,只要双方任意一方泄漏了密匙,那么其他人就可以利用密匙解密数据。


而非对称加密算法之所以能实现安全传输的核心精华就是:公钥加密的信息只能用私钥解开,私钥加密的信息只能被公钥解开。


1.简述非对称加密算法为什么安全:


服务端申请 CA 机构颁发的证书,则获取到了证书的公钥和私钥,私钥只有服务器端自己知道,而公钥可以告知其他人,如可以把公钥传给客户端,这样客户端通过服务端传来的公钥来加密自己传输的数据,而服务端利用私钥就可以解密这个数据了。由于客户端这个用公钥加密的数据只有私钥能解密,而这个私钥只有服务端有,所以数据传输就安全了。


上面只是简单说了一下非对称加密算法是如何保证数据安全的,实际上 Https 的工作过程远比这要复杂(篇幅限制这里就不细说了,网上有很多相关文章):


一个是客户端还需要验证服务端传来的 CA 证书的合法性、有效性,因为存在传输过程 CA 证书被人调包的风险,涉及到客户端如何验证服务器证书的合法性的问题,保证通信双方的身份合法。


另一个是非对称算法虽然保证了数据的安全,但是效率相对于对称算法来说比较差,如何来优化,实现既保证了数据的安全,又提高了效率。


2.客户端如何验证证书的合法性:


首先 CA 证书一般包括以下内容:


证书的颁发机构以及版本


证书的使用者


证书的公钥


证书有效时间


证书的数字签名 Hash 值以及签名 Hash 算法(这个数字签名 Hash 值是用证书的私钥加密过的值)


等等


客户端验证服务端传过来的证书的合法性是通过:先利用获取到的公钥来解密证书中的数字签名 Hash 值 1(因为它是利用私钥加密的嘛),然后在利用证书里的签名 Hash 算法生成一个 Hash 值 2,如果两个值相等,则表示证书合法,服务器端可以被信任。


Android 开发中遇到的问题及解决:


顺便说一个在项目开发中使用 Android WebView 加载公司测试服务器上网页证书过期导致网页加载不出来白屏的问题:


解决方案就是测试环境下暂时忽略 SSL 的报错,这样就可以把网页加载出来,当然在生产上不要这么做,一个是会有安全问题,一个是 google play 应该审核也不会通过。 重写 WebViewClient 的 onReceivedSslError():


@Override


public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) {


if (ContextHolder.sDebug) {


handler.proceed();


return;


}


super.onReceivedSslError(view, handler, error);


}


六、Http 2.0


Okhttp 支持配置使用 Http 2.0 协议,Http2.0 相对于 Http1.x 来说提升是巨大的,主要有以下几点:


二进制格式:http1.x 是文本协议,而 http2.0 是二进制以帧为基本单位,是一个二进制协议,一帧中除了包含数据外同时还包含该帧的标识:Stream Identifier,即标识了该帧属于哪个 request,使得网络传输变得十分灵活。


多路复用:一个很大的改进,原先 http1.x 一个连接一个请求的情况有比较大的局限性,也引发了很多问题,如建立多个连接的消耗以及效率问题。


http1.x 为了解决效率问题,可能会尽量多的发起并发的请求去加载资源,然而浏览器对于同一域名下的并发请求有限制,而优化的手段一般是将请求的资源放到不同的域名下来突破这种限制。


而 http2.0 支持的多路复用可以很好的解决这个问题,多个请求共用一个 TCP 连接,多个请求可以同时在这个 TCP 连接上并发,一个是解决了建立多个 TCP 连接的消耗问题,一个也解决了效率的问题。


那么是什么原理支撑多个请求可以在一个 TCP 连接上并发呢?基本原理就是上面的二进制分帧,因为每一帧都有一个身份标识,所以多个请求的不同帧可以并发的无序发送出去,在服务端会根据每一帧的身份标识,将其整理到对应的 request 中。


header 头部压缩:主要是通过压缩 header 来减少请求的大小,减少流量消耗,提高效率。因为之前存在一个问题是,每次请求都要带上 header,而这个 header 中的数据通常是一层不变的。


支持服务端推送


3 TCP 相关


==========================================================================


TCP 面向连接,提供可靠的数据传输。在这一层,我们通常都是通过 Socket Api 来操作 TCP,建立连接等等。


一、三次握手建立连接



第一次:发送 SNY=1 表示此次握手是请求建立连接的,然后 seq 生成一个客户端的随机数 X


第二次:发送 SNY=1,ACK=1 表示是回复请求建立连接的,然后 ack=客户端的 seq+1(这样客户端收到后就能确认是之前想要连接的那个服务端),然后把服务端也生成一个代表自己的随机数 seq=Y 发给客户端。


第三次:ACK=1。 seq=客户端随机数+1,ack=服务端随机数+1(这样服务端就知道是刚刚那个客户端了)


为什么建立连接需要三次握手?


首先非常明确的是两次握手是最基本的,第一次握手,C 端发了个连接请求消息到 S 端,S 端收到后 S 端就知道自己与 C 端是可以连接成功的,但是 C 端此时并不知道 S 端是否接收到这个消息,所以 S 端接收到消息后得应答,C 端得到 S 端的回复后,才能确定自己与 S 端是可以连接上的,这就是第二次握手。


C 端只有确定了自己能与 S 端连接上才能开始发数据。所以两次握手肯定是最基本的。


那么为什么需要第三次握手呢?假设一下如果没有第三次握手,而是两次握手后我们就认为连接建立,那么会发生什么?


第三次握手是为了防止已经失效的连接请求报文段突然又传到服务端,因而产生错误


具体情况就是:


C 端发出去的第一个网络连接请求由于某些原因在网络节点中滞留了,导致延迟,直到连接释放的某个时间点才到达 S 端,这是一个早已失效的报文,但是此时 S 端仍然认为这是 C 端的建立连接请求第一次握手,于是 S 端回应了 C 端,第二次握手。


如果只有两次握手,那么到这里,连接就建立了,但是此时 C 端并没有任何数据要发送,而 S 端就会傻傻的等待着,造成很大的资源浪费。所以需要第三次握手,只有 C 端再次回应一下,就可以避免这种情况。

用户头像

Android架构

关注

还未添加个人签名 2021.10.31 加入

还未添加个人简介

评论

发布
暂无评论
Android 开发经验分享:挺重要的网络基础,android实现选择题模式