模型
应用层、表示层、会话层
HTTP协议
HTTP请求报文和响应报文
请求报文
方法(METHOD)
方法 | 用途 |
---|---|
GET | 向服务器发送某个资源,通过query拼在请求地址后,受url长度限制(比如IE8对url长度限制是2048个字符),传送的数据量较小。 |
POST | 通常支持表单数据(formData),传输内容在body中,传输的内容没有限制,header是multipart/form-data格式,更高效的传输二进制数据 |
HEAD | 不获取资源的情况下,对资源首部的检察 |
PUT | 向服务器写入文档,允许用户创建WEB页面 |
DELETE | 请求服务器删除URL中指定的资源 |
通用首部字段
字段名 | 说明 |
---|---|
Connection | 指定连接方式,默认为keep-alive长连接 |
Date | 报文创建时间 |
Cache-Control | 控制缓存行为 |
请求首部字段
字段名 | 说明 |
---|---|
Host | 接受请求的主机名和端口号 |
Range | 如果服务器支持范围请求,就请求资源的指定范围 |
Cookie | 客户端用他向服务器传送数据 |
Accept | 用户代理可处理的媒体类型 |
响应报文
状态码
状态码 | 对应内容 |
---|---|
1xx | 请求已接受,需要继续处理 |
2xx | 成功,请求已经被服务器接收 |
3xx | 重定向,重定向目的地址在本次响应的Location域中指明 |
4xx | 请求错误 |
5xx | 服务器无法响应 |
响应报文首部字段(常用)
字段名 | 描述 |
---|---|
Age | 资源创建经过实践 |
ETag | 资源匹配信息 |
Location | (当重定向时)高质客户端实体实际位于何处 |
Server | HTTP服务器安装信息 |
Last-Modified | 实体最后一次被修改的日期和时间 |
缓存机制
在客户端向服务器请求资源时,会先抵达浏览器缓存,如果浏览器有请求资源的副本,就可以直接获取。
常见的http缓存只能缓存get请求的数据。
强缓存
HTTP状态码:200
直接从浏览器的缓存数据库获得数据
协商缓存
HTTP状态码:304
请求头:ETag,Last-Modified
首部字段 | 作用 | 值 |
---|---|---|
eTag | 在请求的响应报文中返回的一串内容 | W/"a-QFZ79AprHeNlMfPMKXyEUV+lyOg" |
if-none-match | 第二次请求,请求头中带有该字段,值和eTag值相同 | W/"a-QFZ79AprHeNlMfPMKXyEUV+lyOg" |
last-modified | 第一次请求一个地址,响应报文标记文件在服务端最后一次被修改的时间 | Fri , 12 May 2006 18:53:33 GMT |
if-Modified-Since | 第二次请求一个地址,浏览器向服务器发送请求报文,值是第一次请求的last-Modified的时间,询问在这个时间后,资源是否修改过。 | Fri , 12 May 2006 18:53:33 GMT |
缓存过程
- 检查cache-control
cache-control属性名 | 解释 |
---|---|
max-age | 响应返回后一定时间内,可以使用缓存 |
private | 只有浏览器能缓存,中间代理服务器不能缓存 |
no-cache | 跳过强缓存,发哦是那个HTTP请求,进入协商缓存 |
no-store | 不进行缓存 |
s-maxage | 代理服务器的缓存时间 |
如果再缓存时间内,直接返回缓存,没有发起http请求。
- 如果不在缓存时间内, 进入协商缓存,发起HTTP请求
- 如果使用Last-Modified,即第一次请求地址的时候,响应返回资源上一次修改时间,第二次请求该资源,将这个时间作为Last-Modified的值传入,服务器将比对在这个时间之后是否有更新,并把这个值作为响应报文的if-Modified-Since返回
- 如果使用ETag,即第一次请求的时候,响应报文返回ETag,第二次请求该资源,将这个值作为If-None-Match的值发送,服务器将会把这个值和服务器上的ETag比对,如果一致,返回304,如果不一致,返回新的资源,状态码200
同源策略
即:协议+域名+端口号都相同,例如:
地址一、www.domain.com
地址二、www.domain.com
地址三、child.domain.com
以上这些都不属于相同的地址
地址五、www.domain.com?username=name
和地址一属于相同地址
CORS
IE10以上支持CORS。
-
简单请求:
- 请求方法为GET,POST,HEAD
- 请求头取值范围
- ACCEPT
- ACCEPT-LANGUAGE
- CONTENT-LANGUAGE
- CONTENT-TYPE
- 对于简单请求,浏览器在发送请求之前,自动添加origin字段,说明请求来自哪个源,响应报文添加Access-Control-Allow-Origin字段,返回这个源,如果浏览器发现这个origin不在这个字段内,浏览器会拦截这个响应。
- Access-Xontrol-Allow-Credentails是bool值,是否允许Cookie,默认false
- Access-Control-Expose-Headers能拿到这个字段声明的响应头字段。
-
非简单请求
- 会先进性预检,将请求头用OPTIONS方法发送
OPTIONS /HTTP/1.1 Origin:当前IP Host:xxx.com Access-Control-Request-Method:PUT // cors请求要用的类型 Access-Control-Request-Header:X-Custom-Header // cors请求要加什么请求头
- 预检的响应字段
HTTP/1.1 200 OK Access-Control-Allow-Origin: * //允许请求的的源 Access-Control-Allow-Methods: GET, POST, PUT // 允许的方法 Access-Control-Allow-Headers: X-Custom-Header Access-Control-Allow-Credentials: true // 是否允许cookie
keep-alive和多路复用
keep-alive
建立一次长连接,多次请求都复用该连接。
keep-alive有一个 timout时间设置参数,如果在这个等待时间里没有受到浏览器发来的请求,就关闭长连接。
同时可以根据Content-Length字段来判断数据是否已经全部接受。
但是在并行的情况下,并发的请求在连接上的响应是顺序的,这样才不会乱套
- 请求资源A
- 相应资源A
- 请求资源B
- 相应资源B
如果只能知道每个response对应的request,并发的请求可以只需要在一次TCP连接里了,这就是HTTP2.0的多路复用。
多路复用
每个request都有单独的id,用id去区分请求,因此请求不需要等前一个获得响应之后再发送。
- 请求资源A,id:1
- 请求资源B,id:2
- 返回id:1的响应,对应获得资源A的响应
- 返回id:2的响应,对应获得资源B的响应
1.0/1.1/2.0
1.0 | 1.1 |
---|---|
只支持GET POST HEAD | 支持包括PUT、DELETE等其他方法 |
无状态,无连接 | 增加了缓存机制 |
不支持断点续传 | 增加Range请求头,支持断点续传 |
1.1 | 2.0 |
---|---|
keep-alive | 多路复用,允许出现两个并行的响应 |
重复发送头部 | 头部压缩,建立head-cache,无需重复发送头部数据 |
字节流 | 基于二进制分帧,更稳健 |
使用XMLHTTPRequest封装基础的ajax请求
const $ajax = (options)=>{
options = Object.asign({
url:'http://localhost:3000',
method:"GET",
},options)
return new Promise((resolve,reject)=>{
let xhr = new XMLHTTPRequest();
xhr.open(options.method,options.url);
xhr.onreadystatechange = ()=>{
// readyState: 0->请求未初始化 1->连接已建立 2->请求已接受 3->请求处理中 4->请求已完成
if(xhr.readyState===4){
// 状态码是3或者4开头都认定是成功的
if(/^(3|4)[0-9]{2}/.test(xhr.status)){
resolve(JSON.parseInt(xhr.responseText))
}else{
reject(xhr)
}
}
}
xhr.send();
})
}
HTTPS协议
加密
在HTTP协议总,数据是以明文传输的,因此有一定的安全风险,HTTPS协议在发送请求之前先建立SSL连接,用于数据加密和身份验证。
对称加密
加密和解密用的是同一个密钥,没有非对称加密安全,但是效率高。例如DES,AES。
非对称加密
加密和解密用的不是同一个密钥,有一对公钥和私钥,一般用公钥加密,用私钥解密,安全性高,但是效率低。
例如RSA。
了解了对称加密和非对称加密的特点,我们就不难理解HTTP加密的机制,先生成对称加密密钥用于加密通讯,提升传输效率,再用非对称加密保证对称加密密钥在传输时的安全,可能有点绕,我们一步一步看。
- 浏览器和服务器建立SSL连接
- 服务器接受SSL连接,发送
公钥A
给浏览器 - 浏览器接收
公钥A
,并生成生成用于通讯的对称加密密钥B
- 浏览器用
公钥A
对密钥B
进行加密,生成密钥C
,发送给服务器 - 服务器接受到
密钥C
,用私钥A
解密获得密钥B
- 双方用
密钥B
进行通信
但是这个时候又有一个问题,浏览器不知道,接受到的公钥是不是正确的服务器发来的,因此需要数字签名进行身份验证。
- 服务器将个人信息(认证主体信息)通过摘要算法生成
摘要A
,再通过CA(数字证书颁发机构)申请的私钥A
生成数字签名
- 服务器将
个人信息
和数字签名
一起发送给浏览器 - 浏览器用CA拿到的
公钥A
解密数字签名
获得摘要A
,然后将服务器发送的个人信息生成摘要B
,如果身份正确,两个摘要的内容应该是一样的。
DNS寻址
DNS协议通过域名解析服务ip地址,以下讨论非CDN资源寻址过程
- 客户端发起查询请求,在本地缓存中查找,如果找到,返回ip地址
- 向根域名服务器发送请求,返回包含下一级dns服务器的地址返回到客户机的dns服务器地址
- 客户机dns服务器分局返回的信息继续向下查找,直达获得ip地址
- 本地dns服务器将结果返回客户机,同时建立本地映射
CDN
-
当域名解析系统遇到CDN子域名,将域名解析权发给CNAME指向的CDN专用DNS服务器
-
CDN专用DNS服务器将全局负载均衡设备IP返回给用户,浏览器向负载均衡设备发起请求
-
全局负载均衡设备通过IP地址和URL,返回一个局部负载均衡设备,浏览器向局部负载均衡设备发起请求
-
局部负载均衡设备根据:
- IP地址:寻找最近的节点
- url:寻找含有目标资源的节点
- 服务器负载:寻找还有能力的服务器
返回一个ip地址,让浏览器发起请求
-
缓存服务器响应请求,如果缓存服务器上没有内容,则向上一级缓存服务器请求,直到原网站把资源拉到本地
传输层(端对端)
TCP协议
报文格式(首部20字节)
累积确认机制
当报文分段,数据序号(seq)表示当前包的序号,确认序号(ack)表示下一个需要的包的序号。
- 服务器发送第一个包,seq=1
- 服务器发送第二个包,seq=1000
- 服务器发送第三个包,seq=2000,这个包因为某种原因丢失了
- 浏览器收到前两个包,返回ACK=2000
- 服务器收到前两个包已经收到了,把这两个包移除重传队列
- 服务器发送第四个包,seq=3000
- 浏览器收到第四个包,把它放入缓存,不返回ACK,等待第三个包
- 服务器没有收到第三个包的ACK,因此重传报文三
- 浏览器成功接收第三个包,现在包一二三四都到达浏览器,返回ACK
- 服务器把报文三、四移出重传队列,此时队列为空,结束发送。
三次握手
-
客户端 -> 服务器: SYN, seq=x
客户端发送后状态:SYN-SENT 此时服务器状态LISTEN
-
服务器 -> 客户端:SYN,ACK,seq=y,ack = x+1
服务器接收到SYN后状态:SYN-RCVD 客户端收到ACK之后状态变为:ESTABLISHED
-
客户端 -> 服务器:ACK, seq = x +1, ack = y+1
服务器收到ACK之后状态变为:ESTABLISHED
为什么是三次握手?两次不可以吗?四次不可以吗
三次握手是为了确认客户端和服务端都有发送和接收消息的能力,因此:
第一次握手:检验客户端发送消息的能力
第二次握手:检验服务器发送消息和接收消息的能力
到这里为止,我们还不知道客户机是否有能力接受消息。
如果碰到消息发送超时,重新发送请求的状况,对于前一个请求客户机是不会再产生响应的,这样通道一直打开,服务器一直等待浏览器响应,非常耗费性能。
因此我们需要第三次握手,检验客户端是否可以接收消息。
至于为什么不要第四次握手,因为三次已经足够了,再多的握手只能延长连接建立的时间。
快速建立TCP连接TFP
第一轮三次握手
- 客户端发送SYN
- 服务器端接收SYN,计算SYN Cookie,放到Fast Open选项中,然后返回SYN和ACK
- 客户端收到,发送ACK,缓存SYN Cookie
第二轮三次握手
- 客户端发送,SYN,SYN Cookie和HTTP请求。
- 服务器返回ACK,SYN
- 服务器验证cookie合法性,如果合法,发送HTTP响应
- 客户端发送ACK
这样的好处是节省了一个RTT,可以提前获得响应。
四次挥手
挥手可以由任意一方开始,以下例子以客户端先发起挥手请求为例:
-
客户端 -> 服务器: FIN,seq=p
客户端状态:ESTABLISHED->FIN-WAIT-1
-
服务器 -> 客户端:ACK,ack=p+1
服务器状态:ESTABLISHED-> CLOSED-WAIT 客户端状态: CLOSED-WAIT-1 -> CLOSED-WAIT-2
-
服务器 -> 客户端:FIN,ACK,seq=q,ack=p+1
服务器状态:CLOSED-WAIT -> LAST-ACK
-
客户端 -> 服务器:ACK,ack=q+1
客户端状态: CLOSED-WAIT-2 ->TIME-WAIT
为什么两次FIN要分开发送
发送FIN是表示某一方的数据已经发送完了,如果服务器接收到FIN立即也发送FIN,这样就会导致服务器数据没发送完,连接就关闭了。
拥塞机制
就像泳池希望出水速度和入水速度相同,这样水池的水量会保持不变。
网络中也希望输入负载和网络吞吐量达到平衡,避免负载过多导致的丢包和负载过小导致的浪费
TCP有四种拥塞控制的算法:
-
慢启动
发送方初始拥塞窗口cwnd为1,当发送方接收到响应,此时cwnd+1,这样一开始向网络注入的报文段少,每经过一个RTT,cwnd翻倍,直到到达阈值
-
拥塞避免
到达阈值之后,每一个RTT,cwnd只增加1,从慢启动的指数增加变成了线性增加
-
快重传
如果再传输过程中,碰到丢包的情况,像之前提到的,收到了1,2,4,但是没有收到3,这时返回的一律是ACK=3,当服务器收到三个相同的ACK时,就意识到丢包了,将立即重传
-
快恢复
当服务器发现丢包了,将意识到现在的网络已经有点拥塞了,此时:
- 拥塞阈值 = cwnd/2
- cwnd = 拥塞阈值
- cwnd 线性增长(拥塞避免)
TCP和UDP的比较
TCP | UDP |
---|---|
面向连接的(需要三次握手建立连接) | 无连接的 |
可靠的(有超时重传、拥塞机制,如果网络不佳TCP会自己调整wcnd) | 不可靠 |
字节流的(将IP包变成字节流) | 基于数据报的 |
网络层(IP,点到点)
IP
报文格式(首部20字节)
标志位(三位)
MF = 1:表示后面还有分片
DF = 0:表示不允许分片
子网划分
internet组织机构定义了五种IP地址。全1是广播地址,全0表示主机地址
类别 | 范围 | 掩码 |
---|---|---|
A类 | 1.0.0.1-126.255.255.254 | 255.0.0.0(8位网络号,后24位主机号) |
B类 | 128.1.0.1-191.255.255.254 | 255.255.0.0 |
C类 | 192.0.1.1-223.255.255.254 | 255.255.255.0 |
D类 | 224.0.0.1-239.255.255.254 | 网络号224-239,用于多路广播 |
E类 | 1111开头,网络号240-255 |
私有IP地址
私有IP是本地局域网上的IP地址,保留下来用于企业内部网络分配所使用的地址:
类别 | 范围 |
---|---|
A | 10.0.0.0-10.255.255.255 |
B | 172.16.0.0-172.168.255.255 |
C | 192.168.0.0-192.168.255.255 |
NAT协议
私有地址->全局地址的映射。
- 私有地址为10.1.0.10的主机想要访问web服务器162.105.129.12
- 消息发送给NAT路由器
- 路由器记录他的私有地址和端口号,分配他一个全局地址125.1.2.3,把请求发送出去
- web服务器响应请求,通过125.1.2.3,找到私有地址10.1.0.10返回给他应答。
DHCP协议
动态主机配置协议。
- 保证同一IP地址同一时刻只被一台主机使用
- 自动分配:可以给用户分配永久固定的IP地址
- 动态分配:DHCP服务器给主机指定一个具有时限的IP地址,时间到期或者主机放弃地址之后,改地址可以给其他主机使用(只有动态分配可以使用主机废弃的IP地址)
- 手工分配:DHCP只是返回分配结果
ARP协议
IP地址->MAC地址的映射,因为MAC地址工作在数据链路层,IP地址工作在网络层因此不能直接打交道,当以太网发送IP数据包的时候,需要先封装MAC地址包头,再向上封装IP地址。
同时RARP协议用于MAC地址->IP地址的映射。
数据链路层(帧)、物理层(比特流)
以太网的帧格式(14字节)
-
前同步码:8字节,固定的,为了使接收端适配器接受MAC帧时快速调整时钟频率
-
定界符:(六位)表示后面时以太网MAC帧
-
目的地址:目的MAC地址,6位,网卡接收一个帧的时候会检查目的地址和适配器物理地址是否相同,如果不同,直接丢弃
-
类型:上层协议
-
数据:IP数据包,最大传输单元(MTU)时1500字节,最小时46字节,如果没有到46字节将自动填充