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的区别
-
缓存处理:HTTP/1.0 使用 Pragma:no-cache + Last-Modified/If-Modified-Since来作为缓存判断的标准;HTTP/1.1 引入了更多的缓存控制策略:Cache-Control、Etag/If-None-Match等。
-
错误状态管理:HTTP/1.1新增了24个错误状态响应码,如409(Conflict)表示请求的资源与资源的当前状态发生冲突;410(Gone)表示服务器上的某个资源被永久性的删除。
-
范围请求:HTTP/1.1在请求头引入了range头域,它允许只请求资源的某个部分,即返回码是206(Partial Content),这样就方便了开发者自由的选择以便于充分利用带宽和连接,支持断点续传。
-
Host头:HTTP1.0中认为每台服务器都绑定一个唯一的IP地址,因此,请求消息中的URL并没有传递主机名(hostname)。但随着虚拟主机技术的发展,在一台物理服务器上可以存在多个虚拟主机(Multi-homed Web Servers),并且它们共享一个IP地址。HTTP1.1的请求消息和响应消息都应支持Host头域,且请求消息中如果没有Host头域会报告一个错误(400 Bad Request)。有了Host字段,就可以将请求发往同一台服务器上的不同网站,为虚拟主机的兴起打下了基础。
-
持久连接:HTTP/1.1 最大的变化就是引入了持久连接(persistent connection),在HTTP/1.1中默认开启 Connection: keep-alive,即TCP连接默认不关闭,可以被多个请求复用。
-
管道机制:HTTP/1.1中引入了管道机制(pipelining),即在同一个TCP连接中,客户端可以同时发送多个请求。
HTTP1.1 与 HTTP2.0的区别
Http2.0是以Google发布的SPDY协议为基础的。HTTP/2协议只在HTTPS环境下才有效,升级到HTTP/2,必须先启用HTTPS。HTTP/2解决了HTTP/1.1的性能问题,主要如下:
-
二进制分帧:HTTP/1.1的头信息是文本(ASCII编码),数据体可以是文本,也可以是二进制;HTTP/2 头信息和数据体都是二进制,统称为“帧”:头信息帧和数据帧;
-
多路复用(双工通信):通过单一的 HTTP/2 连接发起多重的请求-响应消息,即在一个连接里,客户端和浏览器都可以同时发送多个请求和响应,而不用按照顺序一一对应,这样避免了“队头堵塞”。HTTP/2 把 HTTP 协议通信的基本单位缩小为一个一个的帧,这些帧对应着逻辑流中的消息。并行地在同一个 TCP 连接上双向交换消息。
-
数据流:因为 HTTP/2 的数据包是不按顺序发送的,同一个连接里面连续的数据包,可能属于不同的回应。因此,必须要对数据包做标记,指出它属于哪个回应。HTTP/2 将每个请求或回应的所有数据包,称为一个数据流(stream)。每个数据流都有一个独一无二的编号。数据包发送的时候,都必须标记数据流ID,用来区分它属于哪个数据流。另外还规定,客户端发出的数据流,ID一律为奇数,服务器发出的,ID为偶数。数据流发送到一半的时候,客户端和服务器都可以发送信号(RST_STREAM帧),取消这个数据流。HTTP/1.1取消数据流的唯一方法,就是关闭TCP连接。这就是说,HTTP/2 可以取消某一次请求,同时保证TCP连接还打开着,可以被其他请求使用。客户端还可以指定数据流的优先级。优先级越高,服务器就会越早回应。
-
头部压缩:HTTP 协议不带有状态,每次请求都必须附上所有信息。所以,请求的很多字段都是重复的,,一模一样的内容,每次请求都必须附带,这会浪费很多带宽,也影响速度。HTTP/2 对这一点做了优化,引入了头信息压缩机制(header compression)。一方面,头信息压缩后再发送(SPDY 使用的是通用的DEFLATE 算法,而 HTTP/2 则使用了专门为首部压缩而设计的 HPACK 算法)。;另一方面,客户端和服务器同时维护一张头信息表,所有字段都会存入这个表,生成一个索引号,以后就不发送同样字段了,只发送索引号,这样就提高速度了。
-
服务端推送: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 ,使得简洁易用。具体过程如下:
- Retrofit 将 Http请求 抽象 成 Java接口
- 在接口里用 注解 描述和配置 网络请求参数
- 用动态代理 的方式,动态将网络请求接口的注解 解析 成HTTP请求
- 最后执行HTTP请求
一张 大神
的源码分析图:
3、OKHttp 3
- 支持 HTTP1.1/HTTP2 和 SPDY
- 对同一主机共享同一个socket连接,减少握手次数,提高效率
- 支持Gzip 降低传输内容大小
- 支持自动重连
- 拥有Dispatcher线程池,并发支持
- 拥有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);
}
}
client.dispatcher().executed(this)
同步调用主要是使用了 dispatcher 线程池的同步方法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);
}
- client.interceptors()
- 添加OkHttp 自定义的 拦截器。如
HttpLoggingInterceptor
等- RetryAndFollowUpInterceptor
- 定义:重定向拦截器;
- 作用:在无法请求服务器或者请求失败时,服务器会告诉客户端可以处理请求的url,然后重定向拦截器承当重新请求新url的作用(服务器返回3XX错误码为重定向,可以通过响应头的Location获取新请求的url);
- BridgeInterceptor
- 定义:桥拦截器;
- 作用:封装请求头(Content-Type、Connection、Cookie...)与响应头("Content-Encoding...)的信息。
- CacheInterceptor
- 定义:缓存拦截器;
- 作用:为网络请求提供缓存功能,加快相同请求的访问速度,减少资源损耗。
- ConnectInterceptor
- 定义:连接拦截器;
- 作用:与服务器建立通讯连接。
- CallServerInterceptor
- 定义:请求服务器拦截器;
- 作用:与服务器进行数据通讯(包含请求头、请求内容)。
注:调用链采用 *责任链* 的方式,向下请求,向上响应。类似 Android View 的事件传递
StreamAllocation、RealConnection、HttpCodec 连接与请求
- StreamAllocation :负责初始化 RealConnection、HttpCodec,并将前2者与RealCall进行关联;
- StreamAllocation 在
RetryAndFollowUpInterceptor
中初始化 - RealConnection 在 ConnectInterceptor 中通过 StreamAllocation 的
newStream()
初始化 - HttpCodec 在 RealConnection 中被初始化
- RealConnection:真正负责完成网络连接
connectSocket(connectTimeout, readTimeout, call, eventListener);
最后通过 Socket
进行网络连接
- HttpCodec:负责完成发送请求头与数据内容(使用okio完成数据的写入与读出)
有 Http1Codec
和 Http2Codec
,分包处理 HTTP1.1 和 HTTP2.0 的数据传递
网友评论