网络基础
网络
1.从用户输入网址到页面渲染,这个流程发生了什么?
- 输入网址,按下回车,浏览器进行网络请求
- 首先浏览器对输入的网址进行DNS解析,通过域名查询到具体的IP。
- 进行TCP三次握手建立连接
- TLS握手(TLS握手是HTTPS协议的流程,通过TLS握手,客户端和服务器确认使用的加密算法以及会话密钥)
- 浏览器获取到HTML文件
- 浏览器开始解析HTML文件
- 在浏览器解析文件的过程中,遇到网络请求(如src,link等)会再次发起网络请求
- 解析成功后会先根据HTML代码构建DOM树,根据CSS构建CSSOM树。
- 遇到script标签,会判断是否存在async或者defer,存在async时,会先下载并执行JS,存在defer会先下载文件,等HTML解析完成后按顺序执行JS代码。如果都不存在,则会阻塞渲染流程,等JS执行完毕。
- CSSOM树和DOM树构建完成后会开始生成Render树,然后浏览器调用GPU绘制,合成图层,将内容显示在屏幕上。
2.名词解释
-
DNS解析:DNS的作用是通过域名查询到具体的IP。因为IP存在数字和英文的组合,不利于记忆,所以出现了域名。域名相当于某个IP的别名,DNS就是去查询这个别名的真正名称。在TCP握手之前就进行了DNS查询,这个查询是操作系统自己做的。当浏览器想访问www.google.com时,会进行以下操作:
-
操作系统会首先在本地缓存中查询 IP -
没有的话会去系统配置的 DNS 服务器中查询. -
如果这时候还没得话,会直接去 DNS 根服务器查询,这一步查询会找出负责com这个一级域名的服务器 -
然后去该服务器查询google这个二级域名 -
接下来三级域名的查询其实是我们配置的,你可以给www这个域名配置一个 IP,然后还可以给别的三级域名配置一个 IP 以上介绍的是 DNS 迭代查询,还有种是递归查询,区别就是前者是由客户端去做请求,后者是由系统配置的 DNS 服务器做请求,得到结果后将数据返回给客户端。 -
递归查询:客户机向本地DNS服务器发起请求,然后等待一个确定的结果。本地DNS服务器向其他DNS服务器发起请求,直到查询到结果,再转交给客户端。 -
迭代查询:客户机向DNS服务器发起请求,若该DNS服务器没有查询到结果,则向客户机返回另一台DNS服务器的地址,客户机向新的DNS服务器发起新的请求。直到得到结果。 -
什么是TCP/IP协议:不同硬件、操作系统之间的通信,所有的一切都需要一种规则,我们就把这种规则称为协议(protocol)。协议中存在各种各样的内容。从电缆的规格到IP地址的选定方式、寻找异地用户的方法、双方建立通信的顺序,以及Web页面显示需要处理的步骤等。像这样把与互联网相关联的协议集合起来总称为 TCP/IP 。也有说法认为,TCP/IP是指TCP和IP这两种协议。还有一种说法认为,TCP/IP是在IP协议的通信过程中,使用到的协议族的统称。 -
TCP/IP协议的层级:TCP/IP协议族最重要的一点就是分层,TCP/IP协议族按层次分为了4层:应用层、传输层、网络层、数据链路层。 -
TCP协议的三次握手: 在这之前需要了解一个重要的性能指标 RTT。该指标表示发送端发送数据到接收到对端数据所需的往返时间。
建立连接三次握手
首先假设主动发起请求的一端称为客户端,被动连接的一端称为服务端。不管是客户端还是服务端,TCP 连接建立完后都能发送和接收数据,所以 TCP 是一个全双工的协议。
起初,两端都为 CLOSED 状态。在通信开始前,双方都会创建 TCB。 服务器创建完 TCB 后便进入 LISTEN 状态,此时开始等待客户端发送数据。
第一次握手
客户端向服务端发送连接请求报文段。该报文段中包含自身的数据通讯初始序号。请求发送后,客户端便进入 SYN-SENT 状态。
第二次握手
服务端收到连接请求报文段后,如果同意连接,则会发送一个应答,该应答中也会包含自身的数据通讯初始序号,发送完成后便进入 SYN-RECEIVED 状态。
第三次握手
当客户端收到连接同意的应答后,还要向服务端发送一个确认报文。客户端发完这个报文段后便进入 ESTABLISHED 状态,服务端收到这个应答后也进入 ESTABLISHED 状态,此时连接建立成功。
PS:第三次握手中可以包含数据,通过快速打开(TFO)技术就可以实现这一功能。其实只要涉及到握手的协议,都可以使用类似 TFO 的方式,客户端和服务端存储相同的 cookie,下次握手时发出 cookie 达到减少 RTT 的目的。
-
为什么TCP协议需要三次握手,两次握手就可以建立连接。
-
因为这是为了防止出现失效的连接请求报文段被服务端接收的情况,从而产生错误。 可以想象如下场景。客户端发送了一个连接请求 A,但是因为网络原因造成了超时,这时 TCP 会启动超时重传的机制再次发送一个连接请求 B。此时请求顺利到达服务端,服务端应答完就建立了请求,然后接收数据后释放了连接。 假设这时候连接请求 A 在两端关闭后终于抵达了服务端,那么此时服务端会认为客户端又需要建立 TCP 连接,从而应答了该请求并进入 ESTABLISHED 状态。但是客户端其实是 CLOSED 的状态,那么就会导致服务端一直等待,造成资源的浪费。 PS:在建立连接中,任意一端掉线,TCP 都会重发 SYN 包,一般会重试五次,在建立连接中可能会遇到 SYN Flood 攻击。遇到这种情况你可以选择调低重试次数或者干脆在不能处理的情况下拒绝请求。 -
断开连接,四次挥手: TCP 是全双工的,在断开连接时两端都需要发送 FIN 和 ACK。
第一次握手
若客户端 A 认为数据发送完成,则它需要向服务端 B 发送连接释放请求。
第二次握手
B 收到连接释放请求后,会告诉应用层要释放 TCP 链接。然后会发送 ACK 包,并进入 CLOSE_WAIT 状态,此时表明 A 到 B 的连接已经释放,不再接收 A 发的数据了。但是因为 TCP 连接是双向的,所以 B 仍旧可以发送数据给 A。
第三次握手
B 如果此时还有没发完的数据会继续发送,完毕后会向 A 发送连接释放请求,然后 B 便进入 LAST-ACK 状态。
PS:通过延迟确认的技术(通常有时间限制,否则对方会误认为需要重传),可以将第二次和第三次握手合并,延迟 ACK 包的发送。
第四次握手
A 收到释放请求后,向 B 发送确认应答,此时 A 进入 TIME-WAIT 状态。该状态会持续 2MSL(最大段生存期,指报文段在网络中生存的时间,超时会被抛弃) 时间,若该时间段内没有 B 的重发请求的话,就进入 CLOSED 状态。当 B 收到确认应答后,也便进入 CLOSED 状态。
为什么 A 要进入 TIME-WAIT 状态,等待 2MSL 时间后才进入 CLOSED 状态?
为了保证 B 能收到 A 的确认应答。若 A 发完确认应答后直接进入 CLOSED 状态,如果确认应答因为网络问题一直没有到达,那么会造成 B 不能正常关闭。
-
TLS握手:SSL 和 TLS 协议可以为通信双方提供识别和认证通道,从而保证通信的机密性和数据完整性。TLS 协议是从Netscape SSL 3.0协议演变而来的,不过这两种协议并不兼容,SSL 已经被 TLS 取代,所以下文就以 TLS 指代安全层。 TLS 握手是启动 HTTPS 通信的过程,类似于 TCP 建立连接时的三次握手。 在 TLS 握手的过程中,通信双方交换消息以相互验证,相互确认,并确立它们所要使用的加密算法以及会话密钥 (用于对称加密的密钥)。可以说,TLS 握手是 HTTPS 通信的基础部分。 TLS握手的流程:
- 商定双方通信所使用的的 TLS 版本 (例如 TLS1.0, 1.2, 1.3等等);
- 确定双方所要使用的密码组合;
- 客户端通过服务器的公钥和数字证书 (上篇文章已有介绍)上的数字签名验证服务端的身份;
- 生成会话密钥,该密钥将用于握手结束后的对称加密。
3.HTTP状态码
2XX 成功
200 OK,表示从客户端发来的请求在服务器端被正确处理
204 No content,表示请求成功,但响应报文不含实体的主体部分
205 Reset Content,表示请求成功,但响应报文不含实体的主体部分,但是与 204 响应不同在于要求请求方重置内容
206 Partial Content,进行范围请求
3XX 重定向
301 moved permanently,永久性重定向,表示资源已被分配了新的 URL
302 found,临时性重定向,表示资源临时被分配了新的 URL
303 see other,表示资源存在着另一个 URL,应使用 GET 方法获取资源
304 not modified,表示服务器允许访问资源,但因发生请求未满足条件的情况
307 temporary redirect,临时重定向,和302含义类似,但是期望客户端保持请求方法不变向新的地址发出请求
4XX 客户端错误
400 bad request,请求报文存在语法错误
401 unauthorized,表示发送的请求需要有通过 HTTP 认证的认证信息
403 forbidden,表示对请求资源的访问被服务器拒绝
404 not found,表示在服务器上没有找到请求的资源
5XX 服务器错误
500 internal sever error,表示服务器端在执行请求时发生了错误
501 Not Implemented,表示服务器不支持当前请求所需要的某个功能
503 service unavailable,表明服务器暂时处于超负载或正在停机维护,无法处理请求
ajax传字段和传文件
//原生ajax请求
let f=new FormData()//就是表单格式的数据包
f.append("touxiang",inpu.files[0])
f.append("email","2733xxx@.com")
var xhr=new XMLHttpRequest()||new ActiveXObject("Micsorf.XMLHTTP")
xhr.open("POST","http://www.baidu.com/login",true)//网络连接配置,没有发网络请
xhr.send(JSON.stringify(f))//连接
xhr.onreadystatechange=function () {
if(xhr.readyState==4){
xhr.responseText
}
}
axios发送字段和文件:
//axios传字段
axios(url,{params:{pwd:123,username:"karen"}})//get请求
axios.post(url,{pwd:123,username:"karen"})//post请求
axios({
url:"",
})
//axios传文件
let f=new FormData()//就是表单格式的数据包
f.append("touxiang",inpu.files[0])
f.append("email","2733xxx@.com")
axios.post(url,f)
后端接收:
//后端接受文件
//node原生代码
// let total=""
// req.on("data",(str)=>{
// total+=str
// })
// req.on("end",()=>{
// console.log(total)
// })
// egg.js框架:
// this.ctx.request.files==>前提 配置文件中打开文件上传模式,csrf(配置跨域)
//后端接受字段
// egg:
//this.ctx.requet.body(post请求)
//this.ctx.requet.query(get请求)
//后端发送字段给前端
//node原生代码
//res.write(二进制或者字符串)
//res.end(二进制或者字符串)
// egg
// this.ctx.body=[]//{},string,number
//会转化为字符串给前端
前端所有能发送网络请求的方式:
// 浏览器地址栏: 只能发送get 得到的数据使用是用浏览器的工具去解析(字符串,html,文件编码,image,video)
//浏览器不能解析的数据包 浏览器就会下载到默认下载路径
//link-href get 得到的数据包按照标签的功能解析
//src get 网络请求资源 得到资源以后 根据标签 v8引擎运行或者图片渲染
//url get 网络请求资源
//a-href get 用户点了而且没阻止默认事件 网络请求资源 得到资源以后使用方式同浏览器地址栏
//form get/post/xx 用户点击表单内部的提交按钮或者触发提交事件
//打包表单内部的数据,key就是name value是用户交互的结果
//按照数据传输方式 由form标签的method属性指定
//发送给action后端
//返回的数据包使用方式同a标签
//ajax get/post/xx 收到数据以后放在ajax的对象的某个属性中的 让程序员自己操作
//jsonp==>src get
//script可以做网络请(因为跨域的阻止只有ajax)
//jsonp 'fn({"name":"karen"})' JSON: '{"name":"karen"}'
//得到的数据是一个fn函数,在script环境里,就可以执行。
// function fn(arg){
// }
// <script src="xxx">
//后端: request egg=>this.curl //代理服务器
promise
promise是ES5末期产生的一种数据容器的技术,它内部会自己产生数据,产生数据的过程可以是同步的也可以是异步的。也可以取出内部的数据使用,取数据的过程是异步的。
promise是JS的内置构造方法,传入该构造方法的回调函数会直接运行。
promise对象通过.then方法来使用其内部的数据。该方法是异步的,且该方法的返回值是一个新的promise对象
new Promise(function fn(n1,n2){
xhr.open(methd,url,true)
xhr.send()
xhr.onreadystatechange=()=>{
if(xhr.readyState==4){
n1(100)//100就是Promise数据容器内部产生的数据
}else{
n2(10)
}
}
}).then((res)=>{
console.log(res)//打印100
})
//调then函数传入的回调函数的返回值是Promise对象那么这个新的Promise对象就是它
//调then函数传入的回调函数的返回值不是Promise对象,那么就会把这个返回值封装为一个新的Promise对象返回
promise具有三种状态:pending、resolve、reject
它只能从pending转变为resolve(成功)
或者从pending转变为reject(失败)
并且状态不可逆
promise对象通过.catch方法抓取失败的数据,其底层是try{}catch(err){}
使用promise封装axios
function myaxios (url) {
let p1=new Promise((n1,n2)=>{
let xhr=new XMLHttpRequest()||ActiveXObject("Micsorf.XMLHTTP")
xhr.open("GET",url,true)
xhr.send()
xhr.onreadystatechange=function() {
if(xhr.readyState==4){
if(xhr.status==200){
n1(xhr.responseText)
}else if(xhr.status>=400){
n2(xhr.responseText)
}
}
}
})
return p1
}
任务队列
宏任务:script、setTimeout、setInterval、I/O、UI交互事件等等
微任务:promise.then、MutationObserver、process.nextTick(node.js环境)
宏任务比微任务先执行
事件循环:eventloop:
- 从宏任务的头部取出一个任务执行;
- 执行过程中若遇到微任务则将其添加到微任务的队列中;
- 宏任务执行完毕后,微任务的队列中是否存在任务,若存在,则按顺序依次执行,直到执行完毕
- GUI渲染;
- 回到步骤一,直到宏任务执行完毕。
例题:
console.log(6)
let p1=new Promise((n1,n2)=>{
console.log(1)
let p3=new Promise((n1,n2)=>{
console.log(22)
n1()
})
setTimeout(()=>{
console.log(3)
p3.then(()=>{console.log(33)})
},0)//2宏
console.log(7)
n1(4)
})
console.log(5)
p1.then((res)=>{
console.log(res)
setTimeout(()=>{
console.log(8)
},0)//3宏
return new Promise((n1,n2)=>{
console.log(2)
})
})
//6 1 7 5 4 2 3 8
promise的静态方法:
- Promise.then(()=>{});
- Promise.catch(()=>{});
- Promise.all([p1,p2,p3]); //当数组内promise对象都执行成功时,外层promise返回成功
- Promise.resolve(1000) //等价于==>new Promise((N1,N2)=>{N1(1000)}) //p1.then((res)=>{})
- Promise.reject(1000) //等价于==>new Promise((N1,N2)=>{N2(1000)}) //p2.catch((res)=>{})
- Promise.race([p1, p2, p3]); //外层promise的返回值是数组内第一个改变状态的promise对象的返回值
|