http的思维逻辑

本文的主要意义在于通过逻辑的分析与概括,弄清楚以下几个问题:

  1. http 是什么
  2. http 的作用是什么
  3. http 的主要结构是什么
  4. tcp 的工作机制与原理
  5. http在Android中的具体实现

首先我们来聊聊http是什么?

学过计算机的人都知道,http是基于C/S架构的 应用层 网络通信协议

1
2
3
4
5
6
7
8
9
这句话中有很多隐含的信息:

第一个,既然是 通信协议 ,那么就肯定有协议规范。

第二个,既然是基于C/S架构,那么就必然有请求端与服务端

第三个,既然是高层应用层框架,那么肯定就会有底层的支持,**http底层依赖可靠传输的TCP协议**

第四个,http是可靠的协议

来个总结: http定义了通信双方的通信规范报文格式,即 请求格式响应格式,基于C/S架构使得通信结构相对稳定,底层的实现依赖于TCP协议

所以http是 一个稳定可靠的应用层协议

来看看http协议的传输报文格式

请求报文:

1
2
3
4
5
起始行:  请求方法  url  协议及版本  		例如:GET  /v.gif  http/1.0

首部: host / key : value 的形式保存 例如:content-length : 15000 / content-type : image/gif等等

请求体: body(有些方法请求是不能由body的 如get/head)

响应报文:

1
2
3
4
5
起始行:  协议版本 状态码  当前状态信息  		例如:HTTP/1.0  200  OK

首部: key : value 的形式保存 例如:content-length : 15000 / content-type : image/gif等等

响应体: body(包含请求返回的主体内容)

请求方法主要是有以下几个:

1
2
3
4
5
6
7
8
9
1.GET :http定义的安全方法,使用该方法不会对服务器上产生什么影响,大部分用于读取服务器数据   没有body

2.POST : 主要用于提交表单数据到服务器,服务器要对此作出反应(需要严格验证)有body

3.HEAD : http定义的安全方法,与GET方法有点类似,但这种请求的响应只会返回首部 可用于对资源进行检查 没有body

4.DELETE : 请求服务器删除指定的资源(需要严格验证) 有body

5.PUT : 向服务器写入资源(需要严格验证) 有body

GET与PUT方法是幂等的,也就是说你PUT 一次与PUT十次结果是一样的并不会有所区别

响应状态码主要有以下一些:

1
2
3
4
5
6
7
8
9
1.100~199:说明收到请求的初始部分,请客户端继续 《信息类型状态码》

2.200~299 : 200 OK 请求成功 201 Create 用于创建服务器对象成功 等等 《成功类型状态码》

3.300~399 : 表示请求的资源已经改变,需要去新的地址获取 《重定向状态码》

4.400~499 : 401 未授权 404 找不到资源 等等《客户端错误状态码》

5.500~599 : 500 502 Bad Gateway《服务端错误状态码》

一个http请求主要包括协议 URL 以及端口,http默认的端口是80,其中URL对应机器的IP地址,端口则对应机器上的应用程序

http底层用的是TCP,那么TCP又是什么?

  1. TCP是一个传输层协议,为http提供了一条可靠的比特传输管道,从tcp一端写入的字节会从另一端以原有的顺序正确的传送出来

  2. TCP流是分段传输的,由网络层的IP分组传输。TCP收到数据流之后会将其砍成被称作段的一小块然后加上自己的首部(端口号)往下传递给IP层

  3. TCP在接收到一个包之后,会对当前包的顺序进行确认,如果后面再有相同的包传过来会直接丢弃掉。

  4. 由于是分段传输的,在网络不稳定的情况下,TCP需要对接收到的包进行 排序去重 以及进行 丢包重传

我们来看看TCP连接的真实过程

1.客户端要跟服务端建立连接,那么客户端会向服务端发送一个小的IP分组,并再这个分组中附加上一个SYNC的标志信息,表示想要连接服务器

2.如果服务端接受了连接,就会会连接参数进行计算,也生成一个SYNC的标志,并且对客户端的SYNC进行回应,发送一个ACK = SYNC + 1 的值,表示自己已经接受了连接

3.客户端接收到之后,再往服务器发送一个分组,且附加一个ACK = SYNC + 1的标志位,服务端接收到之后连接建立。

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

三次握手的原则设计是防止旧复用链接的初始化导致问题,为了解决此问题,我们设计了reset这个特别的控制信号来处理。如果接收中的 TCP 在一个未同步状态如 SYN-SENT, SYN-RECEIVED,它会返回 reset 给对方。如果 TCP 是同步状态中如(ESTABLISHED, FIN-WAIT-1, FIN-WAIT-2, CLOSE-WAIT, CLOSING, LAST-ACK, TIME-WAIT),他会终止此连接并通知用户。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

TCP A TCP B

1. CLOSED LISTEN

2. SYN-SENT --> <SEQ=100><CTL=SYN> ...

3. (duplicate) ... <SEQ=90><CTL=SYN> --> SYN-RECEIVED

4. SYN-SENT <-- <SEQ=300><ACK=91><CTL=SYN,ACK> <-- SYN-RECEIVED

5. SYN-SENT --> <SEQ=91><CTL=RST> --> LISTEN


6. ... <SEQ=100><CTL=SYN> --> SYN-RECEIVED

7. SYN-SENT <-- <SEQ=400><ACK=101><CTL=SYN,ACK> <-- SYN-RECEIVED

8. ESTABLISHED --> <SEQ=101><ACK=401><CTL=ACK> --> ESTABLISHED

Recovery from Old Duplicate SYN

这是 复用连接时,旧在途包发往新连接中的例子。

1
2
3
4
5
6
7
8
9
3中,一个旧的重复的 SYN到达 B。

4中, B分别不出是否旧的,照样子正常回包。

5中,A检测到 B 返回的ACK不正确,所以返回 RST(reset)

6中,B接收到 RST(reset)信号,于是变成 LISTEN 状态。

7中,新连接正常的 SYN终于到达了,三次握手正常进行。

对上面来个总结: 首先,如果A要跟B通信,A先向B发送一个序列号 seq 以及 SYN 标志,如果在B收到A的这个seq 之前有别的连接请求到达,比如说B收到了一个旧的的SYN信号以及 seq 序列号,那么此时B是不知道这个请求是A新发出的还是以前的,所以B会正常的往A回复响应,即发一个SYNC 标志与ACK = seq + 1,当A收到这个ACK时发现这个回复与自己当前的Seq不匹配,那么此时A会发送一个RST消息使B进入一个listen状态,只监听A当前发送给B的分组,当A刚刚发送的seq = 100 的分组到达时,B才向A返回确认,当A收到确认之后再往B发送一个确认,当B再次收到A的确认时,双方连接建立。如图
image.png

下面来聊一聊http请求慢的问题

请求慢的分析主要有以下几个方面:

1
2
3
4
5
6
7
8
9
当一个http请求发出之后会经历以下几个阶段:

1.如果不是直接通过IP连接,那么会有DNS寻址过程,这个过程相对耗时

2.TCP三次握手连接建立

3.服务端解析请求,返回结果数据(服务端事务处理)

4.客户端解析返回结果

那么正对上面的过程会有如下解决方案:

1.如果在服务器IP地址已知的情况下,使用IP访问,去除DNS寻址的过程

2.一般来说服务端的事务处理都比较块,真正耗时的地方在于握手建立连接,此时可以使用连接池复用连接

3.请求体优化,如果请求体过大,那么这个在传输过程中也时比较耗时的

4.并行的服务端事务处理

http在Android中的具体实现

在Android有几个鼎鼎大名的网络请求 框架,一个是 Volly,一个是 Okhttp

个人对Okhttp相对来说比较熟,在接下来的文章中会有对Okhttp的具体分析

下面的一篇我们来说说https : https 加密解密以及验证与签名

-------------本文结束感谢您的阅读-------------