Android 网络知识梳理

1、网络体系结构

TCP/IP 体系结构 五层体系结构
5.应用层
4.应用层(HTTP) 4.运输层
3.运输层(TCP、UDP) 3.网络层
2.网际层(IP) 2.链路层
1.网络接口层 1.物理层

其中 我们熟悉的 HTTP DNS POP FTP SSH 等协议,都存在于 应用层

TCP

特点

面向连接、面向字节流、全双工通信、可靠

优缺点

优点:数据传输可靠
缺点:效率慢(因需建立连接、发送确认包等)

建立连接

TCP的三次握手

第一次握手:客户端向服务器发送一个连接请求的报文段
第二次握手:服务器收到请求连接报文段后,若同意建立连接,则向客户端发回连接确认的报文段
第三次握手:大客户收到确认报文段后,向服务器再次发出连接确认报文段
注:三次握手期间,任何一次未收到对面的回复,则都会重发

成功进行TCP的三次握手后,就建立起一条TCP连接,即可传送应用层数据

为什么TCP建立要三次握手

结论:防止服务器端因接收了早已失效的连接请求报文,从而一直等待客户端请求,最终导致形成死锁、浪费资源

释放连接

TCP的四次握手

第一次握手:客户端向服务器发送一个连接释放的报文段
第二次握手:服务器收到连接释放报文段后,则向客户端发回连接释放确认的报文段(至此,TCP连接处半关闭状态,即 客户端->服务器TCP已断开, 服务器-> 客户端未断开)
第三次握手:若服务器已无要向客户端发送数据,则发出释放连接的报文段(服务器进入最后确认状态)
第四次握手:客户端收到连接释放报文段后,向服务器发回连接释放确认的报文段(经过2MSL后,客户端进入关闭状态,服务器比客户端先关闭)
注:MSL=最长报文段寿命(Maximum Segment Lifetime)

为什么TCP释放要四次握手

结论:为了保证通信双方都能通知对方 需释放 & 断开连接

HTTP

是基于TCP的应用层协议

请求

请求行 、请求头(header)、请求体(可选,GET 可无请求数据)

响应

响应行 、响应头(header)、响应体

GET POST 区别

使用方式 传参长度限制 传参类型 安全性 应用场景
GET 最长2048个字符http1.1后无限制 只允许ASCII 字符 差 小量、数据不敏感
POST 不受限制 任何类型 好 大量、数据敏感

HTTP1.1 与 HTTP1.0的区别

  1. 缓存处理:HTTP/1.0 使用 Pragma:no-cache + Last-Modified/If-Modified-Since来作为缓存判断的标准;HTTP/1.1 引入了更多的缓存控制策略:Cache-Control、Etag/If-None-Match等。

  2. 错误状态管理:HTTP/1.1新增了24个错误状态响应码,如409(Conflict)表示请求的资源与资源的当前状态发生冲突;410(Gone)表示服务器上的某个资源被永久性的删除。

  3. 范围请求:HTTP/1.1在请求头引入了range头域,它允许只请求资源的某个部分,即返回码是206(Partial Content),这样就方便了开发者自由的选择以便于充分利用带宽和连接,支持断点续传。

  4. Host头:HTTP1.0中认为每台服务器都绑定一个唯一的IP地址,因此,请求消息中的URL并没有传递主机名(hostname)。但随着虚拟主机技术的发展,在一台物理服务器上可以存在多个虚拟主机(Multi-homed Web Servers),并且它们共享一个IP地址。HTTP1.1的请求消息和响应消息都应支持Host头域,且请求消息中如果没有Host头域会报告一个错误(400 Bad Request)。有了Host字段,就可以将请求发往同一台服务器上的不同网站,为虚拟主机的兴起打下了基础。

  5. 持久连接:HTTP/1.1 最大的变化就是引入了持久连接(persistent connection),在HTTP/1.1中默认开启 Connection: keep-alive,即TCP连接默认不关闭,可以被多个请求复用。

  6. 管道机制:HTTP/1.1中引入了管道机制(pipelining),即在同一个TCP连接中,客户端可以同时发送多个请求。

HTTP1.1 与 HTTP2.0的区别

Http2.0是以Google发布的SPDY协议为基础的。HTTP/2协议只在HTTPS环境下才有效,升级到HTTP/2,必须先启用HTTPS。HTTP/2解决了HTTP/1.1的性能问题,主要如下:

  1. 二进制分帧:HTTP/1.1的头信息是文本(ASCII编码),数据体可以是文本,也可以是二进制;HTTP/2 头信息和数据体都是二进制,统称为“帧”:头信息帧和数据帧;

  2. 多路复用(双工通信):通过单一的 HTTP/2 连接发起多重的请求-响应消息,即在一个连接里,客户端和浏览器都可以同时发送多个请求和响应,而不用按照顺序一一对应,这样避免了“队头堵塞”。HTTP/2 把 HTTP 协议通信的基本单位缩小为一个一个的帧,这些帧对应着逻辑流中的消息。并行地在同一个 TCP 连接上双向交换消息。

  3. 数据流:因为 HTTP/2 的数据包是不按顺序发送的,同一个连接里面连续的数据包,可能属于不同的回应。因此,必须要对数据包做标记,指出它属于哪个回应。HTTP/2 将每个请求或回应的所有数据包,称为一个数据流(stream)。每个数据流都有一个独一无二的编号。数据包发送的时候,都必须标记数据流ID,用来区分它属于哪个数据流。另外还规定,客户端发出的数据流,ID一律为奇数,服务器发出的,ID为偶数。数据流发送到一半的时候,客户端和服务器都可以发送信号(RST_STREAM帧),取消这个数据流。HTTP/1.1取消数据流的唯一方法,就是关闭TCP连接。这就是说,HTTP/2 可以取消某一次请求,同时保证TCP连接还打开着,可以被其他请求使用。客户端还可以指定数据流的优先级。优先级越高,服务器就会越早回应。

  4. 头部压缩:HTTP 协议不带有状态,每次请求都必须附上所有信息。所以,请求的很多字段都是重复的,,一模一样的内容,每次请求都必须附带,这会浪费很多带宽,也影响速度。HTTP/2 对这一点做了优化,引入了头信息压缩机制(header compression)。一方面,头信息压缩后再发送(SPDY 使用的是通用的DEFLATE 算法,而 HTTP/2 则使用了专门为首部压缩而设计的 HPACK 算法)。;另一方面,客户端和服务器同时维护一张头信息表,所有字段都会存入这个表,生成一个索引号,以后就不发送同样字段了,只发送索引号,这样就提高速度了。

  5. 服务端推送:HTTP/2 允许服务器未经请求,主动向客户端发送资源,这叫做服务器推送(server push)。常见场景是客户端请求一个网页,这个网页里面包含很多静态资源。正常情况下,客户端必须收到网页后,解析HTML源码,发现有静态资源,再发出静态资源请求。其实,服务器可以预期到客户端请求网页后,很可能会再请求静态资源,所以就主动把这些静态资源随着网页一起发给客户端了。

HTTP 与 HTTPS的区别

类型 原理 功能 性能 标准端口 CA申请证书
HTTP 应用层 不加密 不安全 80 不要
HTTPS 传输层 加密SSL加密 身份认证 安全 443 需要

HTTPS加密原理

HTTPS = HTTP + SSL/TLS(安全套接层Secure Sockets Layer/安全传输层Transport Layer Security)。也就是在传统的HTTP和TCP之间加了一层用于加密解密的SSL/TLS层。
一篇读懂HTTPS

Socket

套接字,是应用层 与 TCP/IP 协议通信的中间软件抽象层,表现为一个封装了TCP/IP协议的编程接口

  • Socket不是一种协议,而是一个编程调用接口(API),属于传输层(主要解决数据如何在网络中传输)
  • 通过Socket,我们才能在Andorid平台上通过 TCP/IP协议进行开发
  • 对用户来说,只需调用Socket去组织数据,以符合指定的协议,即可通信

2、Retrofit 2

Retrofit 本质上是一个 RESTful 的 HTTP 网络请求框架的封装,即通过 大量的设计模式 封装了 OkHttp ,使得简洁易用。具体过程如下:

  1. Retrofit 将 Http请求 抽象 成 Java接口
  2. 在接口里用 注解 描述和配置 网络请求参数
  3. 用动态代理 的方式,动态将网络请求接口的注解 解析 成HTTP请求
  4. 最后执行HTTP请求

一张 大神 的源码分析图:

源码分析图

3、OKHttp 3

  1. 支持 HTTP1.1/HTTP2 和 SPDY
  2. 对同一主机共享同一个socket连接,减少握手次数,提高效率
  3. 支持Gzip 降低传输内容大小
  4. 支持自动重连
  5. 拥有Dispatcher线程池,并发支持
  6. 拥有Interceptor拦截器及调用链,轻松处理请求和响应

OkHttp系统图

系统图

OkHttp 创建了 实例对象 OkHttpClient ,支持 WebSocket(RealWebSocket) 和 Call(RealCall) 。常使用的 Call 方法,内部是 Dispatcher 线程池 ,实际是 线程池 支持 execute (同步) 和 enqueue(异步) 调用。同步 异步 都使用了 RealInterceptorChain 链式调用方式。最后通过 StreamAllocation,RealConnection,HttpCodec 发送网络请求并得到结果

OkHttpClient

OkHttpClient 是 OkHttp 通过 建造者模式 创建的实例对象。

public class OkHttpClient implements Cloneable, Call.Factory,     WebSocket.Factory {
  static final List<Protocol> DEFAULT_PROTOCOLS = Util.immutableList(
  Protocol.HTTP_2, Protocol.HTTP_1_1);

  static final List<ConnectionSpec> DEFAULT_CONNECTION_SPECS = Util.immutableList(
  ConnectionSpec.MODERN_TLS, ConnectionSpec.CLEARTEXT);

    ......
}

可见,默认支持 http2 和 http1.1,默认使用 TSL 安全协议。

WebSocket

WebSocket 是一种在TCP协议上进行的 全双工通信的协议,支持服务器想客户端的发送请求。

@Override public WebSocket newWebSocket(Request request, WebSocketListener listener) {
RealWebSocket webSocket = new RealWebSocket(request, listener, new Random(), pingInterval);
webSocket.connect(this);
return webSocket;

}

Call(RealCall)

@Override public Call newCall(Request request) {
    return RealCall.newRealCall(this, request, false /* for web socket */);
  }

Call 的实际操作对象是 RealCall

final class RealCall implements Call {
  ......

  static RealCall newRealCall(OkHttpClient client, Request originalRequest, boolean forWebSocket) {
    // Safely publish the Call instance to the EventListener.
    RealCall call = new RealCall(client, originalRequest, forWebSocket);
    call.eventListener = client.eventListenerFactory().create(call);
    return call;
  }
}

RealCall 是真正触发网络请求的类(实现Call接口,一次请求 = 一个RealCall实例),它提供了同步请求、异步请求

execute 同步请求

@Override public Response execute() throws IOException {
    synchronized (this) {
      if (executed) throw new IllegalStateException("Already Executed");
     executed = true;
    }
    captureCallStackTrace();
    eventListener.callStart(this);
    try {
      client.dispatcher().executed(this);
      Response result = getResponseWithInterceptorChain();
      if (result == null) throw new IOException("Canceled");
      return result;
    } catch (IOException e) {
      eventListener.callFailed(this, e);
      throw e;
    } finally {
      client.dispatcher().finished(this);
    }
  }
  1. client.dispatcher().executed(this) 同步调用主要是使用了 dispatcher 线程池的同步方法
  2. getResponseWithInterceptorChain () 采用了 链式方式 获取响应

enqueue 异步请求

 @Override public void enqueue(Callback responseCallback) {
    synchronized (this) {
      if (executed) throw new IllegalStateException("Already Executed");
      executed = true;
    }
    captureCallStackTrace();
    eventListener.callStart(this);
    client.dispatcher().enqueue(new AsyncCall(responseCallback));
  }

client.dispatcher().enqueue(new AsyncCall(responseCallback)); 创建了 AsyncCall 交给 dispatcher线程池 异步处理

    final class AsyncCall extends NamedRunnable {
      // ......
    @Override protected void execute() {
      boolean signalledCallback = false;
      try {
        Response response = getResponseWithInterceptorChain();
        if (retryAndFollowUpInterceptor.isCanceled()) {
          signalledCallback = true;
          responseCallback.onFailure(RealCall.this, new IOException("Canceled"));
        } else {
          signalledCallback = true;
          responseCallback.onResponse(RealCall.this, response);
        }
      } catch (IOException e) {
        if (signalledCallback) {
          // Do not signal the callback twice!
          Platform.get().log(INFO, "Callback failure for " + toLoggableString(), e);
        } else {
          eventListener.callFailed(RealCall.this, e);
          responseCallback.onFailure(RealCall.this, e);
        }
      } finally {
        client.dispatcher().finished(this);
      }
    }
  }

Dispatcher线程池

Dispatcher 管理网络请求的线程池,就是把同步 RealCall 与异步 RealCall .AsyncCall 的请求放进集合中统一管理。

  • RealCall 在Dispatcher中,其实主要就是一个存储功能(即用一个集合把RealCall的请求进行存储)。
  • AsyncCall 在Dispatcher中,除了使用集合存储AsyncCall的请求,Dispatcher还初始化了一个线程池(ThreadPoolExecutor)处理AsyncCall的网络请求。
public final class Dispatcher {
  private int maxRequests = 64; //最大请求数量
  private int maxRequestsPerHost = 5; //相同host的最大请求数据
  private @Nullable Runnable idleCallback;

  ......

  //同步请求
  synchronized void executed(RealCall call) {
    runningSyncCalls.add(call);
  }

  // 异步请求
  synchronized void enqueue(AsyncCall call) {
    if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {
      runningAsyncCalls.add(call);
      executorService().execute(call);
    } else {
      readyAsyncCalls.add(call);
    }
  }
}

Interceptor拦截器及调用链

同、异步请求中都调用了 getResponseWithInterceptorChain() 方法。

    Response getResponseWithInterceptorChain() throws IOException {
    // Build a full stack of interceptors.
    List<Interceptor> interceptors = new ArrayList<>();
    interceptors.addAll(client.interceptors());
    interceptors.add(retryAndFollowUpInterceptor);
    interceptors.add(new BridgeInterceptor(client.cookieJar()));
    interceptors.add(new CacheInterceptor(client.internalCache()));
    interceptors.add(new ConnectInterceptor(client));
    if (!forWebSocket) {
      interceptors.addAll(client.networkInterceptors());
    }
    interceptors.add(new CallServerInterceptor(forWebSocket));

    Interceptor.Chain chain = new RealInterceptorChain(interceptors, null, null, null, 0,
        originalRequest, this, eventListener, client.connectTimeoutMillis(),
        client.readTimeoutMillis(), client.writeTimeoutMillis());

    return chain.proceed(originalRequest);
    }
  1. client.interceptors()
    • 添加OkHttp 自定义的 拦截器。如 HttpLoggingInterceptor 等
  2. RetryAndFollowUpInterceptor
    • 定义:重定向拦截器;
    • 作用:在无法请求服务器或者请求失败时,服务器会告诉客户端可以处理请求的url,然后重定向拦截器承当重新请求新url的作用(服务器返回3XX错误码为重定向,可以通过响应头的Location获取新请求的url);
  3. BridgeInterceptor
    • 定义:桥拦截器;
    • 作用:封装请求头(Content-Type、Connection、Cookie...)与响应头("Content-Encoding...)的信息。
  4. CacheInterceptor
    • 定义:缓存拦截器;
    • 作用:为网络请求提供缓存功能,加快相同请求的访问速度,减少资源损耗。
  5. ConnectInterceptor
    • 定义:连接拦截器;
    • 作用:与服务器建立通讯连接。
  6. CallServerInterceptor
    • 定义:请求服务器拦截器;
    • 作用:与服务器进行数据通讯(包含请求头、请求内容)。

注:调用链采用 *责任链* 的方式,向下请求,向上响应。类似 Android View 的事件传递

StreamAllocation、RealConnection、HttpCodec 连接与请求

  1. StreamAllocation :负责初始化 RealConnection、HttpCodec,并将前2者与RealCall进行关联;
  • StreamAllocation 在 RetryAndFollowUpInterceptor 中初始化
  • RealConnection 在 ConnectInterceptor 中通过 StreamAllocation 的 newStream() 初始化
  • HttpCodec 在 RealConnection 中被初始化
  1. RealConnection:真正负责完成网络连接
connectSocket(connectTimeout, readTimeout, call, eventListener); 

最后通过 Socket 进行网络连接

  1. HttpCodec:负责完成发送请求头与数据内容(使用okio完成数据的写入与读出)

有 Http1Codec 和 Http2Codec,分包处理 HTTP1.1 和 HTTP2.0 的数据传递

最后编辑于 :
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容