1,浏览器的缓存策略(协商缓存与强缓存)
-
缓存策略:
- 1,浏览器访问某个资源,首先会在本地缓存资源中检查是否有当前缓存资源,无该缓存资源则向服务器发送请求获取该资源(获取该资源后会把该资源缓存下来)
- 2,检查是否命中强缓存:如果本地缓存资源中有该资源,会向本地缓存资源中检查该资源的header头信息(Expires与Cache-Control字段)判断是否命中强缓存,如果命中,直接使用本地缓存资源,不再向服务器发起请求。
- 3,检查是否命中协商缓存:如果未命中强缓存,则会携带本地缓存资源响应头中的协商缓存字段(If-Modify-Since与If-None-Match字段),向服务器发起请求,服务器根据协商缓存字段判断是否命中协商缓存,命中则返回304告知浏览器当前资源未更新,使用本地缓存。浏览器接收该信息使用本地缓存。
- 4,如果协商缓存依然未命中,浏览器将直接从服务器加载资源数据。
-
强缓存:了解缓存策略之后我们知道强缓存就是浏览器检查当前缓存资源头部信息(Expires与Cache-Control字段),根据头部信息判断使用当前缓存信息还是继续向服务器发送请求进行协商缓存判定,所以强缓存的关键就是Expires字段与Cache-Control字段的校验。
- Expires:中译有效期,当服务器返回浏览器资源时,浏览器会将该资源缓存。该资源的响应头上会有服务器设置的Expires字段,该字段内容是个时间戳,含义是在该时间之前该资源都有效,浏览器如果需要再次请求该资源,只要在Expires时间之内都可以直接从本地缓存取出该资源使用。所以浏览器在检查是否命中强缓存的时候其实就是在检查当前系统时间是否在Expires之前
- Expires缺点:Expires依赖于本地时间戳,如果本地时间戳不准确,那么该方式就可能会出现问题。
- Cache-Control:中译缓存控制,同Expires一样,当服务器返回浏览器资源时,浏览器会将该资源缓存。该资源的响应头上会有服务器设置的Cache-Control字段,该字段内容是几个不同的值,不同值代表不同的缓存行为,下面看这几个不同的值。
-
Cache-Control:Max-age = x
:比如Max-Age = 1000,意味着该缓存将在1000秒之后失效,如果在1000秒内浏览器希望再次获取该资源,浏览器都不会发送请求而是从本地缓存中获取该资源,如果不在1000秒内,浏览器将未命中该强缓存,从而进行之后的协商缓存流程- Max-age = 0:相当于no-cache,既去使用协商缓存判定缓存是否命中
-
Cache-Control:no-store
:该字段含义是禁止使用缓存,每一次都需要重新获取最新数据,所以命中强缓存该字段后浏览器将向服务器发送请求获取该资源。 -
Cache-Control:no-cache
:该字段含义为需要进行协商缓存,所以当命中强缓存该字段会进行之后的协商缓存流程 -
Cache-Control:public
:该字段含义为可被所有用户(终端用户/cdn等中间代理服务器)缓存。 -
Cache-Control:private
:只能被终端用户缓存。
-
- Expires与Cache-Control可以同时使用,同时启用Cache-Control优先级更高,以Cache-Control为准。
- Expires:中译有效期,当服务器返回浏览器资源时,浏览器会将该资源缓存。该资源的响应头上会有服务器设置的Expires字段,该字段内容是个时间戳,含义是在该时间之前该资源都有效,浏览器如果需要再次请求该资源,只要在Expires时间之内都可以直接从本地缓存取出该资源使用。所以浏览器在检查是否命中强缓存的时候其实就是在检查当前系统时间是否在Expires之前
-
协商缓存:了解了缓存策略之后我们知道协商缓存是在强缓存未命中之后进行,浏览器会携带本地缓存资源响应头中的协商缓存字段(If-Modify-Since与If-None-Match字段)向服务器发起请求,服务器根据请求头携带的协商缓存字段判断浏览器是否使用本地缓存资源,是则返回304,否则直接返回最新当前资源。
- If-Modify-Since/Last-Modify:当服务器返回浏览器资源时,浏览器会将该资源缓存。该资源的响应头上会有服务器设置的Last-Modify字段,该值含义为当前服务器该资源的最后修改时间。当我们进行协商缓存时,浏览器会在请求头中加入If-Modify-Since字段,If-Modify-Since的值就是Last-Modify的值,服务器接收到协商缓存的请求时会获取到请求头中的If-Modify-Since,将之与当前资源最后修改时间进行对比判断是否命中,比如请求头中的If-Modify-Since为2021-1-1,服务器对比一下当前资源最后更新时间发现是2021-1-2说明该资源被修改过,理所应当返回浏览器最新资源,也就是未命中协商缓存。如果命中就是时间对上了,服务端返回304状态码,浏览器接收到304,直接使用缓存的资源,如果协商缓存未命中则直接返回最新当前资源给浏览器。
- If-Modify-Since缺点:
- 如过短时间内资源发生变化(一秒之内,比如间隔小于一秒的请求)Last-Modify不会发生变化,因为该值只能精确到秒。
- 如果这个资源发生过变化,但是之后又变回去了,其实该资源是没有发生变化的,可以使用缓存,但是Last-Modify不这么认为,依旧返回资源。
- If-Modify-Since缺点:
- If-Modify-Since/Last-Modify:当服务器返回浏览器资源时,浏览器会将该资源缓存。该资源的响应头上会有服务器设置的Last-Modify字段,该值含义为当前服务器该资源的最后修改时间。当我们进行协商缓存时,浏览器会在请求头中加入If-Modify-Since字段,If-Modify-Since的值就是Last-Modify的值,服务器接收到协商缓存的请求时会获取到请求头中的If-Modify-Since,将之与当前资源最后修改时间进行对比判断是否命中,比如请求头中的If-Modify-Since为2021-1-1,服务器对比一下当前资源最后更新时间发现是2021-1-2说明该资源被修改过,理所应当返回浏览器最新资源,也就是未命中协商缓存。如果命中就是时间对上了,服务端返回304状态码,浏览器接收到304,直接使用缓存的资源,如果协商缓存未命中则直接返回最新当前资源给浏览器。
-
If-None-Match/Etag:当服务器返回浏览器资源时,浏览器会将该资源缓存。该资源的响应头上会有服务器设置的Etag字段,该值是根据文件内容进行编码的,意味着只要文件内容一样Etag值就一样。当我们进行协商缓存时,浏览器会在请求头中加入If-None-Macth字段,If-None-Match的值就是Etag的值,服务器接收到协商缓存的请求时会获取到请求头中的If-None-Match,对比当前文件的Etag,一样返回304,不一样服务器直接返回该资源
-
If-Modify-Since与If-None-Match可以同时使用,服务器会优先验证If-None-Match,与Etag一样才会继续对比If-Modify-Since,二者同时出现则二者同时满足才返回304。
-
启发式缓存:当强缓存响应头中没有Expires/Cache-Control:max-age/Cache-Control:s-maxage出现,但是有Last-Modify协商缓存字段,此时会有一段启发式缓存的时间,即响应头的(Date的值 - Last-Modify的值)* 10%,该时间内重新获取该数据,都会从缓存中获取,超过该时间才会走协商缓存。
- s-maxage:一般用于中间服务器(cdn服务器)缓存最大存活时间,仅在public下生效
-
缓存位置:
- from memory cache:缓存存放在内存中,读写速度快,浏览器关闭后缓存消失,
- from disk cache:缓存存放在硬盘中,读写速度慢,关闭浏览器进程不会消失
- 读缓存顺序:先内存查找,再硬盘查找
- 缓存存放:大文件优先存放硬盘,小文件优先内存,内存占用率高优先存硬盘
2,XSS攻击与预防
-
XSS(cross-site scripting):跨站脚本攻击,就是攻击者想尽一切办法将可执行代码(主要是恶意代码)注入网页,打开网页将执行这些恶意代码(比如获取用户cookie,token)。常见三种XSS,存储型XSS ,反射型XSS,DOM型XSS。
- 存储型XSS:
- 1,攻击者将恶意代码提交到网站数据库中
- 2,用户打开目标网站,服务器将恶意代码从数据库中取出,拼接在html返回给用户
- 3,浏览器响应解析该html,恶意代码被执行,用户中招。
- 存储型XSS例子:比如论坛回复中包含恶意代码,服务端未对回复进行校验直接存储进数据库,当人们访问回复列表页面,回复资源来源于数据库,且包含恶意代码导致用户中招。
- 反射型XSS:
- 1,攻击者构建特殊的url,其中url参数中包含恶意代码
- 2,用户被诱使访问该url,网站服务端将恶意代码从url取出,拼接在html上返回给用户。
- 3,浏览器响应解析该html,恶意代码被执行,用户中招。
- 反射型XSS例子:比如下图搜索框输入恶意代码,点击百度一下,服务器未对恶意代码进行处理,返回的数据直接拼接搜索内容,浏览器接收解析这些返回数据就会中招。
- DOM型XSS:
- 1,攻击者构建特殊url,url包含恶意代码
- 2,用户被诱使访问该url,前端获取url中的恶意代码并执行,比如取出url参数使用innerHtml展示在页面上,由于url参数包含恶意代码,导致恶意代码被执行。
- DOM型XSS例子:比如前端操作页面的输入框的输入内容会放入url参数中且当前页面会获取url参数展示输入框内容,此时只要攻击者构建包含恶意代码的url诱使用户点击即可。
- 存储型XSS:
-
XSS预防:
- 输入检查:针对于不同场景限制允许输入的字符,比如对输入电话号码限制电话号码纯数字11位,避免了该输入框输入js脚本的可能。当然如果攻击者直接绕过前端过滤,直接伪造请求输入检查的意义就不大了。
- 输出检查:严格的输出编码,html编码,js编码,css编码,url编码等。比如将
&<>"'/
转义掉。<div><</div>
=><div><</div>
,浏览器解析到的是<
而不是<
。 - 设置CSP(Content-Security-Policy) http header:通过这种自建白名单的方式增加XSS攻击难度,由浏览器进行拦截。原理是设置信任的域名,只有信任域名的脚本资源才可以请求执行(比如a.com被注入了
<script src = 'http://danger.com'></script>
,那么http://danger.com
如果不再CSP白名单中那么就不会加载该资源。)。 - cookie设置httpOnly属性:
response.setHeader('Set-Cookie', 'token=xx;HttpOnly');
,该属性能够阻止js读取cookie。 - 验证码操作,防止脚本冒充用户提交危险操作。
3,CSRF攻击与预防
- CSRF攻击(cross site request forgery):跨站请求伪造,攻击者借助受害者的凭证(cookie)伪装成用户向服务器发送伪造请求。
- 1,攻击者诱使用户进入第三方网站
- 2,在第三方网站中向被攻击网站发出请求
- 3,利用用户在被攻击网站先前已经获取的注册凭证,绕过网站对请求的验证,从而冒充用户进行某项操作
- 预防:
- 对响应头的Cookie设置SameSite属性
SameSite:Strict
:cookie不允许跨站发送,只能是同站发送SameSite:Lax
:cookie允许跨站发送,但只能是get请求
- 验证Referer:服务器验证请求头中的Referer(服务器当前请求来源url,包括协议域名端口以及参数)或者Origin(服务器当前请求来源url,包括协议域名端口),判断是否是第三方网站发起的请求。(refer信息可以伪造不安全)
- 使用token:放弃使用cookie对用户校验,而是使用token进行校验,服务器对客户端发放token,客户端保存token,每次请求都会将token放入请求头中,服务器对token进行校验。(crsf只是依靠浏览器自己携带cookie,并不可以进行取出token,将token 放入请求头中的操作。)
- 对响应头的Cookie设置SameSite属性
3,中间人攻击及攻击手段
- 中间人攻击(MITM:MAN IN THE MIDDLE):当数据离开一个端点前往另一个端点时,数据传输的过程便是对数据失去控制的过程,此时当一个中间人位于两个端点之间对数据进行劫持转发,称为中间人攻击。
- 攻击手段
- 非HTTPS时:当我们使用http进行传输数据,由于数据为明文传输,此时如果DNS遭到劫持,客户端的数据发送到中间人的服务器上,中间人继续转发客户端的数据至目标服务器,同理,目标服务器的数据回到中间人服务器,中间人继续转发目标服务器的数据给客户端,对于客户端与目标服务器是无感知的,其实数据已经被中间人劫持导致数据泄露。
- SSL劫持:SSL劫持即SSL证书欺骗,使用HTTPS时,由于SSL协议需要进行握手处理,此时数据依旧是明文传输,如果此时中间人接入到客户端与目标服务器之间,在传输过程中伪造目标服务器发过来的证书,同时将伪造证书发送给客户端,正常客户端是会提示证书错误(因为证书一般由权威机构颁发),如果客户端忽略该错误继续发送数据,那么客户端使用的将是伪造证书中的公钥加密SSL协议第三个随机数,中间人通过私钥解析出客户端发送的第三个随机数,加上传输过程中明文发送的客户端随机数以及服务端随机数还有客户端与目标服务器指定的加密方法生成之后客户端与目标服务端对称加密的密钥,即可获得客户端与目标服务端的通讯信息。
- SSL剥离:该方式为中间人将服务器返回的https范文替换成http返回给客户端,而中间人与服务端保持https进行沟通,这样客户端的http明文将被中间人获取,同时中间人将该http明文替换成https发送给服务端,伪装成客户端通讯,这样导致客户端与服务端的通讯信息被中间人劫持。
- tips:关于CA证书被中间人解析出公钥的问题:其实中间人解析出公钥也没事,因为公钥最后还是要发到客户端手上,客户端利用公钥加密最后信息,回到中间人手中,中间人手中只有公钥,无私钥,所以无法解析出加密后的信息,所以CA证书即使中间人劫持了,也用不了其中的公钥。
4,HSTS(防止301/302重定向劫持)
- HSTS解决的问题:
- 1,有些网站开启了HTTPS,但是用户大多输入域名并不会加上协议,比如直接输入
baidu.com
访问百度服务器 - 2,那么这时候浏览器时先按照
http://www.baidu.com
进行访问 - 3,百度服务器发现是http请求,这是不安全的,于是响应一个302重定向response让客户端重定向至
https://www.baidu.com
- 4,客户端接收到重定向响应信息,于是重定向至
https://www.baidu.com
但是上述过程有个问题,比如重定向被劫持到钓鱼网站,或者中间人进行SSL剥离攻击,所以为了解决这个问题,我们最好的解决办法就是客户端一开始就发送HTTPS的请求,那么如何做到呢,这时候就需要HSTS帮助了。
- 1,有些网站开启了HTTPS,但是用户大多输入域名并不会加上协议,比如直接输入
- HSTS:HTTP安全传输协议(HTTP Secrect Transport Security) 是一种web安全协议,作用是强制客户端发送HTTPS请求(比如,我在浏览器地址栏输入baidu.com,如果是用了HSTS那么我就在浏览器端强制发送https请求
https://www.baidu.com
) - HSTS实现:前面HSTS解决问题第四步:4,客户端接收到重定向响应信息,于是重定向至
https://www.baidu.com
,在这一步,客户端重定向至https://www.baidu.com
,服务器端响应该请求,且该请求响应头会包含Strict-Transport-Security
字段,该字段会告诉客户端之后每次请求你都要使用https请求我。当下一次客户端访问baidu.com
就会直接浏览器内部307重定向至https://www.baidu.com
。 - Strict-Transport-Security响应头详解:
Strict-Transport-Security: max-age=31536000; includeSubDomains; preload
- max-age:必选参数,代表HSTS过期时间,秒为单位,这里是一年,只要未过期,之后的访问每次都会使用https请求进行。
- includeSubDomains:可选参数,意味着当前域名及其子域名均开启HSTS保护
- preload:可选参数,当前服务器如果将其地址添加到HSTS预加载列表中时,则每次都要加上该字段。(后面讲)
- HSTS缺陷:HSTS无法解决第一次访问时的HTTP请求,只能要求客户端第一次以后每次访问都使用HTTPS请求。
- 解决HSTS缺陷:浏览器发起请求前都会检查一个由浏览器维护的HSTS预加载列表,只要请求地址在该列表中,我们就会强制浏览器使用https请求,所以我们只需要将服务器地址加入到HSTS预加载列表中即可解决HSTS存在的缺陷。当我们在HSTS Preload List的官网申请加入将服务器地址加入HSTS预加载列表时,有诸多要求,其中一个要求就是在HTTP响应头加入
Strict-Transport-Security
,且必须包含includesSubDomains与preload参数。 - HSTS优点
- 1,强制使用HTTPS建立安全连接防止重定向劫持以及中间人SSL剥离攻击。
- 2,省略从前http访问服务器,服务器响应,客户端再重定向的步骤,节省时间与资源。
- 3,无HSTS浏览器显示证书错误时用户可以选择忽略警告继续访问,有了HSTS之后证书错误时无目标页链接入口,防止中间人使用SSL劫持攻击。
5,介绍http2特性
-
1,二进制分帧:http2把原先的http信息划分为颗粒度更小的帧,并对其进行二进制编码。比如http1中的头部信息现在位于header帧,body信息位于data帧。(header或者data也可以分多个帧存放,比如header:header帧1+header帧2...)
- 帧由帧头(frame header)与帧内容(frame payload)组成
- 帧头字段含义
- Length字段:24比特(3字节):标识帧内容大小
- Type字段:8bite,标识当前是header帧还是data帧还是其他帧
- Flags字段:8bite,服务于具体帧类型
- R字段:帧保留比特位,固定值0x0(十六进制0)
- Stream Identifier:标识当前帧属于哪个流
- 流:流代表一个完整的请求-响应的数据交互过程,流有以下特点。
- 双向性:同一个流内可以接收与发送数据
- 有序性:流中传输的数据就是上述二进制帧,帧的接受与发送都是按顺序进行的
- 并行性:虽说帧按顺序进行,但他们是并行传输的,无需按顺序等待,因为为了不导致顺序混乱每个帧都有顺序标号,最后会按照顺序合并。
- 流可以被客户端/服务器单方面的创建或者关闭
- 流与帧的关系:以前的报文被分割为用二进制编码的一帧一帧(header帧,data帧等),帧是流中的数据单位,这些帧在流中并行发送传输。
-
2,多路复用:http2中所有请求共用一个tcp连接。具体的说,客户端向服务器发送多个请求,这些请求会被分成多个帧在TCP链路中并行传输,当请求到达服务端时,因为同一个请求所在的流都是同一个,所以根据帧头中的Stream Identifier字段重新组合解析就能得到完整的请求。
-
多路复用解决的问题:http1.0中每个http请求都会新建一个TCP连接,意味着需要进行三次握手浪费很多时间资源,且浏览器会限制同一个域名下多个并发请求的个数,所以之前常规优化并发请求个数手段是将资源分布到不同域名下突破浏览器限制。
-
http2可以并行发出多个请求,但是http1.1请求是串行发出(线头阻塞)
-
-
3,头部压缩:http2提出了hpack压缩方式用于减少每次请求中header的数据量。
-
hpack:
- 在客户端与服务器端维护一份相同的静态表,像这样:当我们希望发送header中的
:method:GET
时只需要发送对应的index值(这里是2),对方接收index值在静态表中寻找对应的header name与header value即可。 - 维护一份动态表初始为空可以动态的添加内容:因为有些header字段是不定的,表中可能没有header value的值,如果碰到这种字段hpack将会采取下面方式进行处理。
- 1,header中包含
User-Agent:Mozilla/5.0 xxx
- 2,由于静态表中User-Agent无对应值,我们需要对当前User-Agent值进行霍夫曼编码
Huffman(Mozilla/5.0 xxx)
- 3,然后找到User-Agent在静态表中对应的index位58
- 4,将58与
Huffman(Mozilla/5.0 xxx)
一起发送出去User-Agent:Mozilla/5.0 xxx
=>58:Huffman(Mozilla/5.0 xxx)
- 5,对方接收到
58:Huffman(Mozilla/5.0 xxx)
会把该数据追加到静态表中,像这样: 这行新的对应关系叫做动态表,同时发出方也会维护一份这样的动态表,这样之后如需再发送该字段只需要发送62即可
- 1,header中包含
- 在客户端与服务器端维护一份相同的静态表,像这样:当我们希望发送header中的
-
头部压缩解决的问题:http1.x请求时每次都会携带header数据,但是很多请求数据基本都是不变的,比如User-Agent、Accept-Language等,那么这些信息每次请求就变成了浪费。
-
-
4,服务器端推送:服务器端推送除了可以响应原始请求外还可以将其他资源推送到客户端(推送的资源需要遵守同源策略)。这样客户端就不需要请求每个资源,减少资源请求响应时间。
- 举个例子:当前客户端请求一份html文件,正常情况下当我们请求接收完html文件对html解析发现css资源,将发送新的http请求获取,但是http2中服务器发现该html资源需要css资源,可以把css资源与html文件一起返回给客户端。
6,http2如何确保文件传输不会出错
根据前面介绍的http2帧概念流概念以及多路传输特性,我们知道虽然一个tcp连接内有多个很多帧在传输且属于不同的请求,但是帧头有Stream Identifier字段帮助我们判断当前帧属于哪个流,一个流属于一组请求-回复数据交互过程,所以再接收端按照帧头的Stream Identifier进行帧的组合即可得到原先的完整请求。
7,列举解释所有http请求方法
- GET:向特定资源发出请求(查)
- POST:向指定资源提交数据(增)
- DELETE:删除指定资源(删)
- PUT:更新数据(更),比如更新表中某字段,除了更新字段其他未更新字段也得携带
- OPTIONS:返回服务器支持的http请求方法(比如CORS跨域的预检请求)
- HEAD:与get方式相似,但仅返回响应头,主要用于测试连接。
- TRACE:返回服务器接收到的请求,主要用于测试诊断
- CONNECT:http1中预留给能将连接方式改为管道方式的代理服务器
- PATCH:对PUT方法的补充,对一直资源进行局部更新,比如更新表中某个字段需要传入该字段更新值
8,点击一个按钮浏览器会做些什么
- 1,创建当前event实例
- 2,当前事件经由事件循环放置于事件队列中
- 3,同步任务执行完毕执行该事件
- 4,事件捕获阶段(自dom顶层向目标节点)
- 5,目标阶段(执行事件,如果有默认行为,触发默认行为)
- 6,冒泡阶段(自目标节点向dom顶层冒泡)
9,列出http协议状态码:20x,30x,40x,50x区间含义
- 20x:功处理请求
- 30x:重定向请求
- 301:资源永久移动至新URL,浏览器自动定向到新url,今后任何请求使用新URL代替
- 302:资源临时移动,客户端任然使用原URL
- 304:资源未修改,客户端接收到该状态从缓存获取数据
- 40x:请求错误
- 403:服务端理解客户端请求,但拒绝执行
- 405:请求方法不对(比如使用post请求静态资源,一些服务器不允许,返回405)
- 50x:服务器端错误
10,http2中多路复用与http1中长连接复用以及区别
- http1长连接:http1默认开启长连接(Connection:keep-live),指的是在一个TCP连接上可以传送多个http请求与响应,减少了建立与关闭连接消耗的时间(而http1.0每次请求都需要建立一个tcp连接)
- http2多路复用:http2中一个TCP连接可以存在多条流,也就是多个请求响应的数据交互过程,服务器通过帧头部Stream Identifier标记判断当前帧属于哪个流将其还原成完整的请求。
- 区别:
- http1.1长连接存在的问题是虽然一个tcp连接可以传送多个请求,但是单个tcp只能一次只能处理一个请求,这样就会造成线头阻塞的问题,即多个请求中某个请求花费了大量时间,那么后续的请求都需要等着该请求处理完毕才能继续响应。造成这种现象的原因是http1.1是文本协议,如果同时处理多个请求同时返回多个响应,响应并不能知道对应哪个请求,所以必须按顺序执行。
- http2的多路复用就不存在线头阻塞的问题,因为在http2的tcp连接中,每个请求响应过程对应一个流,且同时存在多个流,流中的请求响应被分成了很多帧进行并行传输,服务器在接收到并行传输过来帧,会根据帧头Stream Identifier判断当前帧属于哪个流,然后将其组装成完整的请求,即使流a中的帧先发出,流b中的帧先到,我们可以先处理流b请求在流b中响应回去,浏览器在流b中接收到响应就能知道这对应的事流b中的请求。相对于http1.1的长连接以及1.0的每个请求都得建立一个tcp连接,http2多路复用无疑极大地提高了传输性能。
11, http请求由什么组成
- 请求行:包含请求方法,url以及协议版本:
POST /article/news.html http/1.1
- 请求头部:key:value的形式,描述当前请求的一些信息,比如:
Content-Length:112
:表示请求消息的正文长度112If-Modify-Since:XXX
:协商缓存的缓存过期时间为XXX时间Host:localhost:8080
:想访问的主机名为localhost:8080Referer:http://www.BCD.com?code=1
:告诉服务器当前请求资源来自http://www.BCD.com?code=1
- 请求体:发送请求的内容:
name=tom&age=18
- 总的来说http请求报文如下图
12,介绍一下网络的五层模型
-
应用层:应用程序根据其使用的不同协议进行网络通信的地方(向传输层发起连接请求)
- 应用层协议主要包括DNS(域名系统)、http(超文本传输协议)、FTP(文件传输协议)、TFTP(简单文件传输协议)以及TELNET(远程终端协议)。*
-
传输层:根据应用程序的连接请求建立网络连接的地方,使用端口号区分进程。主要协议有TCP与UDP,TCP建立可靠连接,UDP建立快速连接,且在应用层数据基础上添加TCP头与UDP头区分。
- TCP协议特点:TCP协议面向连接的运输层协议,安全可靠,面向字节流,使用全双工通信,即可以客户端服务端可以同时发送响应数据,比如网站资源获取使用的就是TCP协议。
- UDP协议特点:UDP协议面向无连接的传输层协议,不可靠,面向数据报,但其头部开销小,支持一对一,一对多,多对多,多对一数据传输,比如电话会议视频直播使用的就是UDP协议
-
网络层:负责将传输层的数据添加消息头,包括源与目标的IP地址,这时的数据称为数据包。
- 网络是由无数个子网(局域网)构成,消息通过广播形式发送给局域网,局域网每个计算机接受到消息进行对比舍弃,为了区分目标计算机是否在同一个子网内,这时需要IP协议辅助。
- IP协议定义的IP地址由32位二进制组成(0.0.0.0~255.255.255.255),IP地址前一部分代表网络,后一部分代表主机,比如如果网络部分一样说明两台计算机位于同一个子网内,比如178.1.2.1与178.1.3.2网络部分为178.1,则他们位于同一个子网内。但是我们如何知道网络部分是那一部分呢,这时就引入了子网掩码用来判断是否在同一子网内。
- 子网掩码与IP地址一样也是32位二进制数,但它网络部分规定全为1,主机部分全为0,所以如果子网掩码时178.8.0.0,只需要将其与源IP地址,与目标IP地址进行and运算,对比运算结果是否一样就可以知道是否在同一子网内。
- 数据传输依靠的是MAC地址(就是设备物理地址),IP地址对应的MAC地址,每台计算机都有自己独一无二的MAC地址,所以我们进行通信还需要知道将目标计算机IP地址转化成MAC地址,如何获取目标计算机MAC地址,这时我们就用到ARP协议
- ARP协议也是通过广播形式向子网内每一个计算机发送数据包,数据包包含目标计算机IP地址,所有计算机接受到对比,如果不是自己IP地址则舍弃,是则将自己MAC地址返回。(发送数据与询问MAC地址都是通过广播形式,为了区分用途会在数据包的MAC地址位填上一个特殊MAC地址,这样接收放就知道是传送数据还是询问MAC地址)
- 如果两台计算机不在同一个子网内,则会将数据包发送给网关,由网关进行转发。
-
数据链路层:根据以太网协议将网络层数据包分割成帧形式,帧头部包含源与接收方MAC地址。帧以比特流形式发送给物理层。
- 以太网协议:因为如果传输一堆
0101010101101010101
数据计算机也不知道什么意思,所以需要以太网协议对01信号进行分组,规定每一组含义从而双方计算机都能识别。
- 以太网协议:因为如果传输一堆
-
物理层:通过物理手段将设备连接在一起,对数据链路层的比特流处理成信号发送。
13,DNS以及DNS解析过程
- DNS:DNS(domain name system)域名系统,将特定域名解析成IP地址的互联网服务,本质是存储着域名与IP地址映射关系的分布式数据库。
- DNS解析:以浏览器输入
www.newsTV.com
举例。- 1,首先在DNS缓存中查找
www.newsTV.com
,获取对应的IP地址。(以下三步均位客户端DNS缓存)- 1.1,浏览器DNS缓存中查找
- 1.2,操作系统host文件DNS缓存中查找
- 1.3,进入路由器DNS缓存中查找
- 2,DNS缓存未找到,浏览器通过操作系统的DNS客户端向本地DNS服务器查询,本地DNS服务器会带着域名
www.newsTV.com
向根域名服务器进行查找。(本地DNS服务器并没有保存域名对照关系,它只负责响应接受到的dns查询请求,然后在dns分层系统中递归查询到有该域名记录的dns服务器,获取到有该记录的dns服务器解析结果返回) - 3,根域名服务器接收到域名
www.newsTV.com
发现顶级域名为.com
,返回.com
的顶级域名服务器的IP地址给本地DNS服务器 - 4,本地DNS服务器接收到根域名服务器的IP地址,继续向顶级域名服务器发送DNS解析请求。
- 5,顶级DNS服务器发现二级域名为
newsTV
,返回注册这个顶级域名.com
之下的权威DNS服务器(就是二级域名.newsTV
的DNS服务器) 的IP地址给本地DNS服务器。 - 6,本地DNS服务器接收到顶级域名服务器的返回的权威DNS服务器IP地址,向权威DNS服务器发起DNS查询请求,权威DNS服务器有该域名
www.newsTV.com
的所有信息,返回www.newsTV.com
的IP地址给本地DNS服务器 - 7,本地DNS服务器接受到权威DNS服务器返回的IP地址,将IP地址返回给DNS客户端,DNS客户端最后将IP地址返回给浏览器。
- 1,首先在DNS缓存中查找
14,TCP连接三次握手与四次挥手原因以及他们的过程
- 原因:TCP连接能够进行数据可靠传输,为了保证数据可靠传输,我们需要确认服务器与客户端的收发正常,因此需要三次握手四次挥手。
- TCP头几个参与握手挥手的重要字段介绍:TCP传输数据会在数据前面加上TCP头部,头部字段供TCP传输使用,下图中Data上面的部分为TCP头部。
- Source Port(中译源端口):发送方端口号
- Destination Port(中译目标端口):接收方端口号
- Sequence Number(中译序列号):我们叫它分包序号,它有两个作用。
- 1,如果TCP包数据过大,我们需要对其进行分包处理,为了保证接收方收到这些切分后的包重新整合,需要提供该序列号标明,辅助接收方整合。
- 2,配合接收方返回的Acknowledge Number确认该包被成功接收
- Acknowledge Number(中译告知已收到号,Acknowledge:告知已收到):我们叫它回应序号,与Sequence Number配合使用。
- 比如当前服务端接受到客户端发送过来的TCP包,发现它的Sequence Number为10,服务端返回的数据包中的Acknowledge Number就为Sequence Number + 1,也就是11,客户端发现服务端响应TCP包的Sequence Number为11就知道刚才发送过去的Sequence Number为10的包服务端接收到了。
- code:Control Flag,控制标识码,他有6个bit,代表6个句柄,可同时出现多个,下面是几个重要的句柄。
- ACK(Acknowledge):
ACK=1
表示发送过来的数据被正确接收。 - SYN(Synchronous):
SYN=1
表示希望建立同步连接,也就是请求建立连接。 - FIN(Finish):
FIN=1
表示传送完毕,请求断开连接,此时进入等待对方响应断开连接的请求(并未断开连接)。
- ACK(Acknowledge):
- 三次握手:防止已失效的请求连接传输到服务端导致错误。比如客户端发出的第一个请求连接并没有丢失而是在某个地方滞留了,以至于延误到连接释放之后,此时服务端以为客户端又新建一个连接请求于是返回响应报文等客户端发送数据过来,于是服务端就在等浪费了服务端资源。
- 1,客户端发送TCP表头(只是个确认过程,还没正式进行数据交互,所以不需要发送内容)
- 包的表头控制标识码
SYN=1
表示希望建立连接 - 包的表头
Sequence Number = x(x随机分配一个数)
,表明当前包的序列号为x(如果服务器接受到该包那么返回TCP表头的Acknowledge Number肯定为x+1)
- 包的表头控制标识码
- 2,服务器接受到客户端发送的TCP表头,表明服务器的接受能力正常,服务器发送响应TCP表头。
- 包的表头控制标识码
ACK=1
表明发送过来的数据被正确接收 - 包的表头
Acknowledge Number = x+1
表明刚才客户端发送的TCP包被正确的接收了 - 包的表头控制标识码
SYN=1
表明服务器端也期望与客户端建立连接 - 包的表头
Sequence Number=y
表明当前服务器发回的TCP包序列号为y(如果客户端接受到该包那么再次返回的TCP表头的Acknowledge Number肯定为y+1)
- 包的表头控制标识码
- 3,客户端接收到服务器端响应的TCP表头,知道自己的发送接收能力均正常,继续向服务器发送TCP表头(用来告知服务器的发送能力也正常)
- 包的表头
ACK=1
表明发送过来的数据被正确接收 - 包的表头
Acknowledge Number = y+1
表明刚才服务器的响应包也被成功接收了
- 包的表头
- 4,服务器端接受到客户端返回的包发现
Acknowledge Number = y+1
知道自己的发送能力正常,此时三次握手结束。
- 1,客户端发送TCP表头(只是个确认过程,还没正式进行数据交互,所以不需要发送内容)
- 四次挥手:确保了客户端需要结束连接的时候,服务器端如果还有数据没发送完可以继续发送完,否则客户端说断开服务器还没把数据发完导致本该客户端接收的数据丢失。
- 1,客户端发送TCP表头希望断开连接
- 包的表头控制标识码
FIN=1
表明客户端希望断开连接 - 包的表头
Sequence Number = x(x随机分配)
,表明当前客户端期望断开连接的包的序列号为x(如果服务器接收到该包,那么响应的TCP表头Acknoledge Number肯定为x+1)
- 包的表头控制标识码
- 2,服务器端接收到客户端请求断开连接的TCP包,知道客户端期望断开当前连接,于是返回响应表头
- 包的表头
ACK=1
表明发送过来的数据被正确接收 - 包的表头
Acknowledge Number = x+1
表明刚才客户端发送的请求断开连接包正确接收了
- 包的表头
- 3,客户端知道服务器哪边知道自己期望断开连接了,等待服务器告知自己可以断开连接,在此期间,服务器如果有继续发送给客户端的数据将继续发送
- 4,服务器端发现自己没啥数据需要发送给客户端了,自己这边可以断开连接了,于是向客户端发送断开连接TCP表头
- 包的表头
FIN = 1
表明服务器端事情做完了,也可以断开连接了 - 包的表头
Sequence Number = y
表明当前服务器期望断开连接包的序列号为y(如果客户端接受到该包,那么客户端返回的包TCP表头Acknowledge Number一定是y+1)
- 包的表头
- 5,客户端接收到服务器发来的可以断开连接包,知道服务器那边处理完了,大家可以散了,于是向服务器发送一个咱们可以散了的TCP包告知服务器可以散了(为什么要有这一步,我觉得是因为如果此时客户端又想发送数据了,可以继续发送,否则断开连接之后又得再进行三次握手)
- 包的表头
ACK=1
表明发送过来的数据被正确接收 - 包的表头
Acknowledge Number = y+1
表明刚才服务器发过来的同意断开连接包正确接收了。
- 包的表头
- 至此,客户端服务端完成连接断开,四次挥手结束。
- 1,客户端发送TCP表头希望断开连接
19,CDN与CDN流程以及CDN缓存
- CDN:内容分发网络(Content Delivery Network),简而言之就是对用户请求通过中心平台服务器调度告诉用户当前请求资源距离用户最近,访问速度最快的缓存服务器地址,用户去请求该服务器获取资源。解决了源服务器服务过载网络拥塞(大量TCP连接过来)以及数据传输慢等问题。
- CDN流程:
- 1,浏览器访问
www.cdnNews.com/index.html
- 2,浏览器使用本地DNS服务对地址进行解析(DNS解析过程)
- 3,DNS解析最后将该地址的权威DNS服务器地址返回给本地DNS服务器
- 4,由于访问的地址做了CDN处理,此时的权威DNS服务器其实就是该网站的CDN专用DNS服务器,该服务器通过负载均衡系统解析请求资源,找到对用户响应最快的缓存服务器IP地址返回
- 5,本地DNS服务器接受到CDN专用DNS服务器返回的IP地址交给浏览器。(DNS解析结束)
- 6,浏览器向该IP地址发送请求。
- 7,缓存服务器接收到浏览器发来的请求,先检查本地有没有该资源,有的话直接将资源返回给浏览器。
- 8,无该资源则缓存服务器向源服务器请求资源,获取资源缓存,并将资源返回给浏览器
- 1,浏览器访问
- CDN缓存:
- 1,浏览器向CDN缓存服务器发送数据请求
- 2,CDN缓存服务器先检查本地数据有没有过期,没有直接返回改数据
- 3,CDN缓存服务器发现本地缓存过期,则向源服务器请求数据,获取请求数据,本地缓存数据,并返回数据给浏览器。
20,HTTP长连接
- 长连接:一个TCP连接可以接发多个http请求回复。进行一次http会话后并不会立即关闭该TCP连接,而是默认保持一段时间,下一次浏览器继续访问可以继续使用这个TCP连接,而短连接则是每一次http操作都需要建立一个TCP连接。
- 设置长连接:http1.1默认长连接,请求头默认加上
Connection: keep-alive
- 设置长连接:http1.1默认长连接,请求头默认加上
- 长连接优点:
- 降低拥塞机制(建立大量TCP连接)
- 减少后续请求延迟(无需进行握手)
- 如何断开长连接:
- 服务端响应头设置
keep-alive: timeout = x
:客户端保持此连接x秒,超时断开。 - 服务端响应设置
Connection close
告诉客户端此连接断开,客户端接收后销毁连接。
- 服务端响应头设置
21,什么是http协议,http工作流程
- http协议:超文本传输协议(HTTP Hypertext transfer protocol)是基于应用层的协议,规定了浏览器与服务器之间相互通讯的规则。
- http工作流程:
- 浏览器发出http请求报文
- 建立TCP连接(三次握手)
- 传输http请求
- 服务器响应http请求
- 释放TCP连接(四次挥手)
21,http1.1特性
- 连接复用(长连接):默认添加请求头
Connection: keep-alive
,之后客户端与服务器会保持TCP连接,之后的请求可以复用该连接 - http管线化:在长连接基础上支持将多个http请求一同发出,但是服务端处理响应还是需要逐个处理,这就会造成线头阻塞,如果批量请求某个请求某个响应耗时很长阻塞住,那么其他请求响应也会被阻塞。这个将在http2.0的多路复用优化掉
- 请求返回消息支持Host字段:服务端可能一个IP地址配置多个域名(服务器上多个虚拟主机对应多个域名),该字段支持对同一个IP下不同域名提供服务。缺少Host会报400状态码
- 比如服务器ip地址
128.1.2.3
下面配置两个域名www.a.com
与www.b.com
- 我们通过
www.b.com
访问时解析IP地址为128.1.2.3
- 请求发送到服务器,服务器根据
Host: www.b.com
选择对应虚拟主机处理请求
- 比如服务器ip地址
- 新增缓存策略字段:强缓存字段Cache-Control,协商缓存字段If-None-match/Etag,If-Unmodified-Since,If-Match。
- If-Unmodified-Since:与If-Modified-Since相反,告知服务器在该时间之内,文件未发生更新处理该请求,否则返回状态码412(预处理失败)
- If-Match:表示这时一个条件请求,head与get请求时,该字段值为Etag值,当服务器资源满足该Etag值才返回该资源。
- 对于get/head方法,搭配range进行范围请求,确定新请求与旧请求为同一份资源。如果失败Etag无法匹配,返回416(范围请求未满足)
- 新增24个错误状态码:比如401请求资源与当前资源状态冲突,410服务器资源永久删除
- 引入头字段Range:允许请求部分资源,返回码206(局部资源),这样就可以进行断点续传。
22,cookie
- cookie:cookie是服务端发送给客户端一段文本信息,客户端保存起来,下一次在访问该服务端,客户端自动带上这段文本信息发送给服务端。
- cookie用途:http协议是无状态的,第一次请求与第二次请求服务器并不知道是同一个客户端发送的请求,但如果第二次请求带上了第一次请求服务器端返回的cookie那么服务端就知道这两次请求是来自同一个客户端。(比如某个网站的第二次登录就不需要手动登录,直接携带cookie发送给服务器,服务器验证cookie信息正确即可)
- cookie特性
- 请求自动携带cookie:简而言之cookie在浏览器发出请求会自动携带一并发出
- 不可跨域名性:该特性由浏览器维护,浏览器会根据请求地址确定携带哪一个域名下的cookie。
- 二级域名问题:比如
a.com
与b.a.com
,如果期望一级域名a下二级域名b也可以携带a.com
的cookie,可以设置cookie.SetDomain('.a.com')
,这样a.com
下的二级域名就可以使用该cookie - 路径问题:比如
www.a.com/file
路径访问是无法携带www.a.com
的cookie的,如果我们期望www.a.com/file
可以携带www.a.com
的cookie,可以设置cookie.setPath(/file/)
,如果www.a.com
+任意路径都可以携带该cookie,则设置cookie.setPath(/)
- 二级域名问题:比如
- 服务端设置cookie:
ctx.set('Set-cookie', 'name=value;path=/user;domain=localhost;max-age=30;HttpOnly;SameSite=Strict')
- cookie属性:
- path&domain:指定域名domain+路径path,浏览器请求url与该指定url相同时才携带cookie。
- expires:expires服务器指定cookie过期时间戳,到期浏览器不再保存该cookie。
- max-age:max-age指定cookie存活多少秒,过期浏览器不再保存该cookie。与expires同时出现以max-age为准。如果二者都没出现,那么本次cookie仅在本次对话存在,浏览器窗口关闭,则浏览器不再保存此cookie
- HttpOnly:服务器指定该字段,则浏览器不能使用
document.cookie
读取cookie。 - secure:该属性是个标记没有值,包含secure的cookie只有当https或其他安全协议时浏览器才会携带cookie,如果希望在浏览器端设置该字段,需确保网页为https协议,http是无法设置该字段的。
- sameSite:该字段有两个值strict与lax,默认strict。
- strict:不允许跨站发送cookie,只允许当前网站发送cookie。
- lax:允许跨站请求携带,但只能是get,如果是post,则不会携带cookie。
- 浏览器操作cookie:
- 读cookie:
var cookie_ = document.cookie
- 新建cookie:
document.cookie = 'name1 = value1;path:/;domain:localhost;max-age=30'
- 替换cookie:使用新建cookie方式保持同名即可替换原有cookie。
document.cookie = 'name1 = value2;path:/;domain:localhost;max-age=30'
- 删除cookie:删除name3的cookie:
document.cookie = 'name3=value3;max-age=0'
,设置cookie的max-age为0即可删除,设置max-age<0则cookie仅存于当前会话,浏览器窗口关闭,cookie消失。
- 读cookie:
- cookie预防XSS与CRSF
- cookie设置HttpOnly,禁止客户端使用document.cookie访问cookie,预防xss攻击获取cookie。
- cookie设置sameSite:strict,禁止所有第三方网站携带cookie访问服务器,预防CRSF攻击。
23,cookie与token都放在请求头中,为什么只能劫持前者。
因为浏览器向服务器发送请求会自动带上cookie,而CRSF正式利用这一点假冒登陆者,但是CRSF攻击依靠的是cookie机制访问服务器自动携带,而不能获取到用户cookie。而token即使存在cookie中,自动携带发送到服务端,服务端验证的是请求头中的token而不是cookie,所以只要没设置token就无法验证通过,token需要客户端设置请求头发送给客户端,CRSF无法做到这一点,所以不能劫持token。
25,session
- session:保存在服务端用来记录客户端信息的一块数据。与cookie一样也是为了解决http无状态的特点。
- session生成流程:
- 1,客户端第一次访问服务端,服务端为客户端生成一个session与sessionID,session保存客户端信息,sessionID标识session存储位置,并将sessionID返回给客户端(一般放在cookie中)
- 2,客户端下一次访问该服务端会携带cookie
- 3,服务端接受到客户端再一次访问,获取cookie中的sessionID,查找存储中对应的session,获取当前客户端信息。
- 与cookie区别:
- session依靠cookie实现,sessionID存放在cookie中(禁用可以使用url重写完成,放在url中)
- cookie保存在客户端,session保存在服务端
- cookie一般4kb大小,session无大小限制
26,webStorage(localStorage与sessionStorage)
- localStorage:存储共享数据
- localStorage特点:
- 同源页面都可以访问使用,即使不在同一个浏览器窗口
- 非人为删除则一直保存
- 保存在浏览器,大小5M
- localStorage特点:
- sessionStorage:存储共享数据
- sessionStorage特点:
- 同源页面且同一个窗口且同一个tab页内才可以共享访问
- 必须是同一tab页,既然同一地址两个tab页,那么它们sessionStorage也不会共享
- 比如a页面设置sessionStorage,当前tab页跳b,再于当前tab页从b页跳到a页,那么a页先前设置的sessionStorage依然存在
- 生存周期为当前会话期间,会话结束删除,即浏览器打开该tab页至该tab页关闭
- 保存在浏览器,大小5M
- 同源页面且同一个窗口且同一个tab页内才可以共享访问
- sessionStorage特点:
- webStorage操作方法(sessionStorage与localStorage操作方法相同)
- xxxStorage.setItem(key,value):存储数据:key-value形式
- xxxStorage.getItem(key):获取数据:获取key对应的value
- xxxStorage.removeItem(key):删除指定key数据
- xxxStorage.clear():删除全部数据
27,简述V8引擎
V8引擎是用来专门处理JS脚本的虚拟机,是谷歌浏览器、node.js使用的js处理引擎。JS作为一门解释型语言,再程序运行时进行编译,处理成中间代码再交由解释器对中间代码处理成机器语言运行,效率比较低,V8才用了混合编译执行,解释执行两种方式以及内联缓存等方法提高性能,有了这些,JS在V8引擎运行速度媲美二进制程序。
- 1,V8引擎启动时初始化执行环境,比如堆栈,执行上下文,内置函数等
- 2,V8对源代码解析结构化,生成AST(抽象语法树)
- 3,生成AST同时,V8还会生成作用域,字节码,解释器可以直接执行字节码
- 4,解释器执行字节码发现某段代码多次执行,就会将其丢给编译器编译成二进制代码执行,提高执行速度。
28,简述PWA
- PWA:渐进式Web应用(Progressive Web App),实际上就是使用web技术编写一个网页应用,添加App Manifest 与Service worker实现PWA的安装与离线功能。
- 优点:
- 本质是个网页,没有原生APP各种启动条件,快速响应启动
- 实现消息推送通知
- 实现离线缓存,即使手机没有网络也可以使用一些离线功能
- manifest:manifest.json文件规定添加PWA到主屏幕细节,比如图表背景颜色,大小等
- html头部引入manifest.json:
<link rel="menifest" href="manifest.json"/>
- html头部引入manifest.json:
- service worker:service worker类似于介于网页(pwa本质就是个网页应用)与服务器之间的拦截器,拦截进出的http请求,从而控制当前网页。
- 特点:
- 注册安装成功后,运行浏览器后台,不受页面刷新影响,可监听拦截作用域内页面所有http请求
- 网站必须使用HTTPS(localhost例外)
- 单独运行环境与执行线程
- 不能操作页面dom,但可以通过事件驱动处理。
- 特点:
29,encoding头部编码方式
- ASCII:建立英文与二进制值对应关系编码(ASCII一个字节也就是8位二进制就可以表示)
- Unicode:所有字符与二进制值对应关系编码
- UTF-8:对Unicode编码的一种实现方式(通常1-4个字节,兼容ASCII编码)
- GBK/GB2312/GB18030:GB2312与GBK针对简体字编码,GB2312支持六千多个汉字,GBK支持一万多个汉字编码,GB18030针对繁体字编码(汉字存储通常使用两个字节存储)
30,ASCII,Unicode,UTF-8区别
- ASCII:ASCII采用一个(byte)字节(8二进制位也就是8bit:0000 0000)表示英文字母,数字,符号,比如字符
A
对应的字节表示为0100 0001
,这个编码表就是ASCII编码。简而言之ASCII就是通过一个字节来表示字符(主要是英文)以及数字编码表, - Unicode:世界上有那么多文字,仅靠单字节的ASCII是无法表示完的,所以为了统一所有文字编码,Unicode编码方式出现。Unicode编码使用两个字节表示一个字符(两个字符就要四个字节),比如字符
A
再Unicode中表示为00000000 01000001
。 - UTF-8:针对于Unicode的可变字符长度编码规则,他可以表示Unicode编码方式中任何字符。使用Unicode编码方式从对
A
的编码上可以看出来是有浪费的,前8位全为0,所以针对于此,出现了UTF-8:它有两种表示情况。- UTF-8表示单字节字符(比如ASCII编码表示的所有字符):占用一个字节空间,与ASCII表示相同。
- UTF-8表示多字节字符(字符>=2):对于n字节字符,第1个字节前n位全是1,n+1位为0,第一个字节往后的字节(不包括第一个字节)前两位全为10,剩下未提及的二进制位放置Unicode码。
- 比如
严
的Unicode编码为01001110 00100101
根据Unicode与UTF-8对照表知道UTF-8表示严
需要三个字节(该对照表及对照规则此处不展开),那么按照UTF-8表示多字节字符方式换算UTF-8编码过程就是:- 1,第1个字节前3位全是1,第四位为0(加粗字体位)
- 2,第2个字节与第3个字节前两位为10(加粗字体位)
- 3,11100100 10111000 10100101
- 比如
31,TCP连接如何保证数据正确性
- 检验和机制:TCP数据包发送前加上12字节伪头部,然后对其分16位每段进行所有段反码求和,求和值放置TCP表头Checksum字段中,接收方接受到TCP包数据进行同样操作,最终求和值位全为1表示数据正确。
-
TCP20字节表头
-
TCP20字节表头+12字节伪头部
-
TCP检验和(UDP也是)需要在计算之前在原TCP包基础上+12字节(byte)伪头部(上图)。然后该TCP整体分割每16bit(2字节)一段
-
然后将所有段(加上伪头部的TCP包分割为很多段,每段16位)进行反码求和,结果放在校验和字段内
- 反码求和注意:发送方会将TCP包中TCP表头Checksum(校验和字段)二进制位全置为0,反码阶段就会变成全1,然后进行求和计算,而接收方不会对Checksum(校验和字段)二进制位全置为0操作,直接获取Checksum字段反码进行求和
-
接收方用相同方式计算,如果接收方计算结果最终校验和每一位全是1,则代表数据正确(UDP是全0正确)。
-
- 确认应答
ACK=1
+序列号机制ack=x+1
机制:- 确认应答
ACK=1
:三次握手中讲到控制标识码ACK(Acknowledge):ACK=1
标识这是一个响应回来的TCP包,所以接受到TCP包只要表头ACK=1
表示之前发送的TCP数据被对方接收到了 - 序列号机制
ack=x+1
:三次握手中介绍序列号(Sequence Number:seq = x)配合 回应序号(Acknowledge Number:ack = x+1) 可以确认消息正确接收,比如这里序列号x包被正确的接收所以返回ack=x+1
(ACK=1是ack=x+1的前提)
- 确认应答
- 连接断开机制:三次握手四次挥手保证数据传输完整以及收发正常
- 重传机制:超时重传机制(重传定时器),快输重传机制,选择重传机制,冗余选择重传机制
- 流量控制:发送端根据接收端实际接收能力控制发送数据量
- 拥塞机制:当网络拥塞,减少数据发送,发送方有拥塞窗口,发送数据前比对接收方发过来的计时窗口,取小。
32,TCP的四个定时器
- 重传定时器(RTO):发送端向接收端发送TCP数据包,如果定时器时间计时结束还未接受到接收端返回的确认TCP数据包,则发送端重新发送该TCP数据包且同时重启拥塞机制(后面会具体说该机制)
- 重传定时器应用场景:重传定时器为了应对报文丢失或阻塞的情况:丢失就是发过去的报文没了,阻塞就是接收端处理不过来大量接收到的数据导致很久没响应发送端的数据包
- 重传定时器具体流程:
- 1,发送端向接收端发送数据时会开启重传定时器
- 2,如果重传定时器计时结束还没接受到响应数据(报文丢失或阻塞)
- 3,重传定时器重置且重新发送该数据
- 坚持定时器:该定时器应用场景为滑动窗口,在发送端接受到零窗口通知之后,保证发送端能够收到之后接收端返回的窗口更新数据包
- 坚持定时器具体流程:
- 1,当发送端接收到接收端返回的零窗口通知(返回TCP表头中
Window=0
) - 2,发送端知道接收端缓冲区满了,暂时不向发送端发送数据,等待接收端处理完缓冲区数据发回窗口更新数据包(Window=大于0) 再发送数据
- 3,过了会接收端发回窗口更新数据包,于是发送端继续发送数据
- 4,但是如果迟迟接收端未发回窗口更新数据包,发送端不能干等着,因为可能时接收端发回了窗口更新数据包,但丢失了导致发送端未接受到窗口更新数据包
- 5,所以在发送端接收到接收端返回的零窗口通知的时候也会同时开启坚持定时器
- 6,当坚持定时器计时结束还未收到接收端发回窗口更新数据包,发送端将发送窗口探测数据包向接收端请求窗口更新数据包,同时重置坚持定时器
- 1,当发送端接收到接收端返回的零窗口通知(返回TCP表头中
- 坚持定时器具体流程:
- 保活定时器:客户端与服务端建立TCP连接,如果服务端很久未接受到客户端发送的数据,在保活定时器计时结束的时候开始连续向客户端发送探测报文判断客户端是否发生故障,如果没有收到客户端响应,那么服务端认为客户端故障断开当前TCP连接,以节省资源。
- tips:保活定时器每次接受到客户端发送的数据都会重置,一般为2小时,如果保活定时器计时结束,就会发送探测报文(连续发送10次,每次间隔75s)
- 时间等待计时器:应用场景为四次挥手时启动,确保服务器能接收到最后一次挥手,即下图报文段7。
- 时间等待计时器具体流程:客户端收到服务器端第三次握手回应第四次握手的时候,会开启2MSL定时器,等待2MSL时间(1MSL即报文段最大存活时间),如果没有再次收到服务端第三次握手包即可以断开连接,如果再次收到了服务端第三次握手,说明客户端第四次握手数据丢失,服务端迟迟接收不到该确认,于是服务端重发第三次握手,客户端再次接收到第三次握手重置2MSL定时器,并重发第四次握手数据,所以等待2MSL就是防止第四次握手数据的丢失,导致客户端断开连接,服务端没断开,一直重发第三次握手。
33,滑动窗口协议
- 滑动窗口协议:滑动窗口协议通常用于实现流量控制与差错控制,保证数据传输的可靠性有效性。我个人理解滑动窗口协议就是流量控制与差错控制的结合,流量控制通过确认报文中的Window字段控制窗口大小,从而达到控制流量,差错控制使用ARQ协议实现
- 滑动窗口协议的流量控制:
- 发送窗口内的序列号代表已经发送但尚未确认的数据帧,一旦窗口达到最大值,发送方数据链路层必须强行关闭网络层,直到有一帧缓冲区可用
- 接收方数据链路层窗口对应允许接受的帧,任何落在窗口外的帧都会被丢弃
- 滑动窗口协议的差错控制:使用连续ARQ协议进行差错控制,当发送方窗口与接收方窗口都为1时,差错控制就退化成停等式ARQ协议
34,ARQ协议(停止等待ARQ协议,连续ARQ协议)
- ARQ协议:ARQ(Auto Repeat Request)协议即自动重传协议,它是数据链路层的纠错协议之一,注意ARQ协议不是传输层协议也不属于TCP协议,所以ARQ协议的数据单位为帧,ARP协议与传输层的TCP协议共同维护了数据可靠性。ARQ协议分为两种,停等式(stop and wait)ARQ协议与连续ARQ协议,其中连续ARQ协议分为**回退N帧(go back N)ARQ协议* 与 选择重传(selective repeat)ARQ协议
- 停等式ARQ协议:停等式ARQ协议发送窗口与接收窗口均为1,发送方每发送一帧数据就必须需等待接收方处理完该数据的确认返回,才可以进行下一帧数据发送,该方式优点为操作简单,使用内存小,缺点为信道利用率低。下面为停等式ARQ协议四种情况
- 无差错:发送数据帧,等待数据帧确认,确认后再继续发送下一个数据帧
- 超时重传:当发送数据帧丢失,接收方接收不到该帧,自然也无法返回该帧的确认帧,当发送方的重传定时器超时还没收到则重发该帧
- 确认帧丢失:发送方发送数据帧,接收方返回确认数据帧丢失,当发送方重传定时器超时重发该数据帧,接收方接收到该重复数据帧,丢弃(因为当前窗口已经接收到该帧),继续返回该帧确认帧。
- 确认帧迟到:发送方发送数据帧,接收方返回该帧确认帧,由于某种原因,确认帧迟迟未返回,发送方重传定时器超时还没接收到确认帧,则重发该帧,接收方接收到该帧,丢弃,继续返回该帧确认帧,接收方接收到该确认帧继续发送下一个帧,过了会最先发送的帧的确认帧回来了,发送方忽略。
- 连续ARQ协议:连续ARQ协议分为回退N帧ARQ协议与选择重传ARQ协议,连续ARQ协议是滑动窗口技术与请求重发技术的结合,窗口尺寸开的够大,帧在线路上可以连续流动,因此称为连续ARQ协议。通常TCP协议依靠回退N帧ARQ协议实现传输可靠性,如果接收方与发送方都使用SACK(选择确认),那么TCP协议使用选择重传ARQ协议保证数据传输可靠,SACK是选择重传的实现方式
- 回退N帧ARQ协议:回退N帧ARQ协议中发送方窗口大于1,接收方窗口等于1,所以允许发送方连续发送多个数据,因为接收方窗口为1,所以接收方一次只能处理一个数据,一旦该数据发生错误,比如重传定时器超时,发送方就得重发该帧以及该帧后面的数据,因为接收方窗口为1,一个出错,后面的帧接收方记不住,全部都丢弃,所以出现了选择重传ARQ协议,所以允许发送过来的有待确认帧越多,可能退回来重发的帧也就越多,在信道数据传输状态很差情况下,回退N帧ARQ协议效率不一定由停等式ARQ协议高。
- 选择重传ARQ协议:选择重传ARQ协议发送方与接收方窗口都大于1,选择重传ARQ协议中,发送方仅重传错误帧,接收方缓存错误帧之后发送的帧,收到重传错误帧合并之前缓存的帧,与回退N相比,减少出错帧之后正确的帧都要重传的开销
- 混合ARQ:混合ARQ中,数据报文传送到接收方之后,即使出错也不会被丢弃,接收方指示发送方重传出错的报文或者全部信息,将再次收到的报文信息与上次收到的报文信息合并
35,累计确认机制
- 累计确认机制:即不必对发送方连续发送过来的数据逐个返回确认,而是对按序到达最后一个数据返回确认,这样就表示到这个数据之前所有数据都接收了。(不按序到达或者丢包接收端就会发送未按序到达的ack,连续三次冗余ack就会触发发送端快速重传机制)
- 累计确认机制优点:节省资源(多个包只返回一个确认ack),健壮性高,即使确认丢失也不必重传
- 累计确认缺点:不能及时向发送方反应已收到的数据(因为接收方会等着按序到达的数据最后一个才返回确认ack)
- 关于捎带确认:个人认为捎带确认时将将要返回的确认ack与即将返回的数据包合并成一个数据返回,这样对方即可以使用该数据确认之前发送的数据全部被接受,也能接收到对方发来的数据。
36,TCP快速重传
- 快速重传与快速恢复算法:当接收方接收到失序的报文段(可能是中间报文段丢失或者时延导致),接收方立即返回一个冗余ACK给发送方,让对方知道自己接收到一个失序的报文段,并告诉对方自己期望收到的序列号,即那个丢失或者时延的中间报文段。如果这样的冗余ACK连续出现3次或者3次以上,那么很可能是这个中间报文段丢失了,而不是时延造成的,那么发送方不必等到该中间报文段定时器超时就可以立即重传该报文段,这就是快速重传,快速重传后执行拥塞控制中的拥塞避免算法
- 冗余ACK:接收方收到失序报文段返回的确认ACK叫做冗余ACK
- 快速重传过程(如图,这里是发生一次丢包情况):为了方便,seq=x包返回确认包的ack=x+1,正常情况是发送端随机选取序列号x(
seq=x
),且此时包1000字节每个,该包确认号应该是ack = x+1000+1。
- 1,发送端连续发送TCP包
seq=1,2,3,4,5
(序列号分别是1,2,3,4,5五个包),接收端正常是按照1,2,3,4,5包顺序接受到,但此处2包丢失。 - 2,接收端接受到
seq=1
包返回ack=2
确认包,表明seq=1
包收到,期望下一次传输序列号2的包 - 3,
seq=2
包丢失,后面seq=3,4,5
包都收到但失序,所以seq=3,4,5
包都返回ack=2
确认包 - 4,发送端连续接受到3个
ack=2
确认包,这三个包就是冗余ACK,此时发送端知道seq=2
包丢了,于是重发seq=2
包 - 5,接收端收到
seq=2
包,此时接收端缓冲区还有seq=3,4,5
3个包,所以下一个包应该是seq=6
,于是返回ack=6
确认包给发送端。(注意,此处返回ack=6说明数据链路层使用的是选择重传ARQ协议,缓存了seq=3,4,5
包,所以直接返回ack=6)
- 1,发送端连续发送TCP包
- 为什么是3次冗余ACK才发生快速重传:
- 首先即使发送端是按顺序发送TCP包,但是TCP包是封装在IP包内,IP包传输时乱序,意味着TCP包到达接收端也是乱序的,即使不发生丢包,但是由于乱序也会导致发送冗余ACK,有概率出现3次冗余ACK
- 当然在乱序基础上发生丢包,那么一定会出现3次冗余ACK
- 所以将3次冗余ACK作为快速重传阈值就是权衡乱序不丢包与乱序丢包两种情况估算得出的
37,选择性确认机制(SACK:Selective Ackownledgement)
- SACK:TCP传输数据时,如果中间某个数据包丢失,发送端会重传该包以及该包的后续包,即使后续包可能对方已经收到(对应的是回退N帧ARQ协议),为了改善这种情况发展出了SACK(选择确认),即接收端在接收异常的时候返回确认包的TCP表头携带SACK字段,用于标注哪些TCP包已经存在缓冲区中,发送端重传数据的时候根据SACK字段就知道哪些数据包丢失,只重传丢失数据包。
- SACK选项:SACK信息是TCP表头中选项部分提供的,SACK信息分两种,一种标识是否支持SACK,另一种标识具体SACK信息
- 标识是否支持SACK:该选项只允许出现在SYN包中,即TCP握手前两个包,分别标识各自是否支持SACK
- 标识具体SACK信息:该参数告诉发送方,接收方已接受并缓存的不连续数据块,发送方根据这些数据块检查是哪个块丢失并重发对应数据
- Left Edge of 1st Block:不连续块的第一个数据序列号
- Right Edge of 1st Block:不连续块的最后一个数据序列号
- 标识Left Edge -1 与 Right Age中间的数据没有收到,这种边界值实际不会超过四组
- 接收方对于丢包或者延迟时的SACK处理:
- 如果接收方接收到的包序列号小于期待序列号,意味着这个包是网络复制或者重发的包,可以丢弃
- 如果接收方接收到的包序列号大于期待序列号说明中间包丢失或者延迟,此时发送SACK数据包通知发送端(SACK中第一个块必须描述是哪一个数据块激发此SACK选项,接收方在SACK选项中尽可能填写SACK信息(即使空间有限不能填写完),让发送方了解当前网络传输最新信息)
- 发送方对SACK的响应:发送方维护一个发送未确认的数据队列,未确认之前,这些数据是不能释放的,这个发送未确认队列每个数据包块都有一个标志位SACKed(类似true/false的感觉,默认false),标识当前数据是否被SACK过,当接收到来自接收方的SACK信息,根据SACK信息标识发送未确认队列每个数据块的SACKed标志位(如果在SACK信息中,SACKed就变成true),重发未被SACKed标识的数据(即重发SACKed为false的数据)
- tips:如果需要使用SACK,需要双方都支持SACK,其中linux2.4默认开启该功能,开启SACK时TCP协议会使用数据链路层的选择性重传ARQ协议保证数据传输传输可靠性
38,冗余选择确认机制(D-SACK:Duplicate Selective Acknowledgement)
-
D-SACK:D-SACK是对SACK的扩展,D-SACK中的SACK描述除了之前正常的SACK信息描述(哪些数据没收到,那些数据收到了),还添加了重复发送的数据信息的描述,在第一块SACK数据中描述重复接收的数据,后面其他SACK数据描述正常SACK信息, 通过这种方法,发送方可以更仔细判断当前网络传输情况。
-
下图返回SACK信息描述,因为第一次3000-3499数据的确认ACK丢失,发送方以为丢包超时重发3000-3499数据,接收方收到3000-3499数据,返回SACK信息,第一块SACK信息告诉发送方3000-3499数据被重复接收了,ACK=4000告诉发送方期待下一次传输序列号4000的数据
39,TCP流量控制
-
流量控制:发送端根据接收端的接收能力控制发送端的数据量,防止发送速率过快接收端来不及接收导致数据丢失。TCP流量控制的具体实现方式为滑动窗口机制,滑动窗口使用Window字段进行流量控制,使用ARQ协议对数据进行差错控制
-
窗口字段:即TCP表头Window字段,Window代表当前接收端缓冲区剩余处理空间大小,通过Window字段告知发送端当前接收端能够处理的数据量,比如接收端返回确认ack的表头中Window=3000意味着可以处理3000字节数据,那么发送端接收到该确认ack,假设每个tcp包1000字节,那么意味着发送端这次就可以发送3个TCP包给接收端。
-
流量控制原理:
- 通过确认报文中窗口字段控制发送方的发送速率
- 发送方的发送窗口大小不能超过确认报文中窗口大小
- 确认报文中窗口大小是可以调整的,通过窗口大小调整改变发送端的发送速率
- 当发送端的发送窗口中全是发送待确认的数据,则发送端停止数据发送
- 当接收到的确认报文中窗口大小为0时停止发送数据,等待对方发送窗口更新数据包或者自己发送窗口探测数据包获得响应,且响应报文窗口大于0
-
滑动窗口具体流程:
-
死锁问题:
- 即接收端向发送端发送零窗口通知,即报文Window为0,发送端停止发送数据。
- 当接收端处理完缓冲区数据向发送端发送窗口更新报文,通知发送端继续发送数据,但该报文丢失,发送端收不到窗口更新报文,出现死锁。
- 发送端不发送数据,一直等待窗口更新报文
- 接收端等待发送端发送数据
-
死锁问题解决:
- 当发送端接收到零窗口通知,会开启持续定时器
- 持续定时器计时结束未收到窗口更新报文,则向接收端发送窗口探测报文
- 接收端收到窗口探测报文给出确认报文包含当前窗口值,发送端接收到确认报文。
- 确认报文中窗口仍然为0,重置持续定时器
- 确认报文窗口大于0,解除死锁,发送数据
40,TCP拥塞控制
- 拥塞原因:指分组交换设备中(比如路由器)某条链路的流量超过该链路传输能力,使输出队列中报文越来越多以至于发生输出队列溢出,报文丢失情况。简而言之就是对资源的请求超出存储转发节点的能力。
- 分组交换设备的存储能力不足:网络中若干数据流向统一分组交换设备输出端口,如果该设备没有足够存储空间,就会丢弃数据包。
- 带宽容量不足:高速的数据流进入低速的数据链路,低速链路不足以应对高速数据,导致大量数据堆积,形成拥塞。
- 分组交换设备处理能力不足:比如路由器输入端数据输入速度大于分组交换设备处理速度,或者分组交换设备处理速度大于输出端口输出速度都会造成拥塞。
- 拥塞控制与流量控制区别:
- 流量控制针对于点到点通讯,作用的对象是两个主机,通过滑动窗口机制控制两个主机间的数据发送速率
- 拥塞控制针对于端到端的通讯,涉及到所有主机路由器及其他分组交换设备,拥塞控制是全局性的控制,解决网络拥塞大家共同努力的结果
- 如何确定网络发生拥塞:发送报文丢失即判定网络拥塞,重传报文时控制发送端发送流量大小
- 拥塞控制:找出目前网络发送端到接收端能够承载的流量,这种流量使用发送端维护的拥塞窗口(CWND)表示,实际能够发送的流量窗口(SWND)为拥塞窗口与流量控制中接收端返回确认报文的窗口(RWND)取小 ,即实际发送窗口值= MIN(拥塞窗口,接收端公告窗口字段值),即SWND = MIN(CWND,RWND)
- 拥塞控制四种处理方案:慢启动,拥塞避免,快速重传
- 拥塞控制的过程:
- 先使用慢启动,
- 当到达ssthresh(慢启动门限,或者叫慢启动阈值,该字段也是由发送方维护的字段) 时,使用 拥塞避免算法,
- 当发生超时重传或者快速重传(一般是由于丢包导致,也有可能使乱序造成,但这里发生超时重传或者快速重传均视为丢包)时,先调整慢启动门限(ssthresh) 与 拥塞窗口(CWND) 的值,如果是发生的是快速重传则接下来启用快速恢复,如果是发生的是超时重传则接下来重新进入慢启动,
- 慢启动过程:
- 初始化:发送方维护字段拥塞窗口(CWND)字段,假设拥塞窗口(CWND)初始为1个最大报文段的值(MSS),就是拥塞窗口(CWND)初始值为1个数据包。慢启动门限ssthresh假设为16,即16个数据包,为了方便描述此处暂不考虑:实际发送窗口值= MIN(拥塞窗口,接收端公告窗口字段值),即实际发送窗口值=拥塞窗口
- 1,首先发送1个数据包(因为拥塞窗口(CWND)初始值为1)
- 2,收到1个数据包确认时拥塞窗口(CWND) = 拥塞窗口(CWND)+ 1 = 1 + 1 = 2,发送2个数据包
- 3,收到2个数据包确认时拥塞窗口(CWND) = 拥塞窗口(CWND)+ 2 = 2 + 2 = 4,发送4个数据包
- 4,收到4个数据包确认时拥塞窗口(CWND) = 拥塞窗口(CWND)+ 4 = 4 + 4 = 8,发送8个数据包
- 可以看出每经过一个数据传输轮次,拥塞窗口就会加倍(1,2,4,8,指数型增加)
- 5,收到8个数据包确认时拥塞窗口(CWND) = 拥塞窗口(CWND)+ 8 = 8 + 8 = 16,发送16个数据包,此时拥塞窗口(CWND)>= 慢启动门限ssthresh(ssthresh=16),开始拥塞避免算法
- 拥塞避免过程:拥塞避免开始时,拥塞窗口(CWND)不再以指数型增长,而是按照线性增长,即每次数据传输过程之后 拥塞窗口(CWND) = 拥塞窗口(CWND) + 1
- 6,收到16个数据包确认时拥塞窗口(CWND) = 拥塞窗口(CWND)+ 1 = 16 + 1 = 17,发送17个数据包
- 7,收到17个数据包确认时拥塞窗口(CWND) = 拥塞窗口(CWND)+ 1 = 17 + 1 = 18,发送18个数据包
- 8,此时18个包的某个或多个确认返回发生了拥塞,即发生了超时重传或者快速重传(这种现象本质一般是因为丢包)
- 拥塞避免发生丢包,如果发生的是超时重传:我们认为网络糟糕,处理方式为调整慢启动门限(ssthresh) 与 拥塞窗口(CWND) 的值,并重新进入慢启动
- 慢启动门限(ssthresh) = 拥塞窗口(CWND)/ 2
- 拥塞窗口(CWND) = 1
- 重新进入慢启动
- 拥塞避免发生丢包,如果发生的是快速重传:即接收到3个冗余ACK,我们认为网络并不是那么糟糕,处理方式为调整慢启动门限(ssthresh) 与 拥塞窗口(CWND) 的值
- 慢启动门限(ssthresh) = 拥塞窗口(CWND)/ 2
- 拥塞窗口(CWND) = 拥塞窗口(CWND) /2
- 调整完慢启动门限(ssthresh)与拥塞窗口(CWND)值之后,重新进入拥塞避免算法
41,什么是同源策略,什么是跨域,跨域解决方式
- 同源策略:同源策略是浏览器的安全策略,限制运行在浏览器的脚本未经授权无法访问不同源的资源(cookie,localstorage,ajax数据请求资源),所谓不同源指协议域名端口号任一不同则为不同源。
- 同源策略特点:
- 同源策略是浏览器安全策略,非浏览器不受此策略限制,比如postman,
- 其次同源策略拦截的是响应的数据,防止返回数据污染
- 表单提交不受同源策略影响,因为同源策略主要限制运行在浏览器的脚本未经授权无法访问不同源的资源
- img、link与script 标签不受同源策略影响
<img src=xxx />
<link href=xxx />
<script src=xxx />
- 同源策略限制的行为:
- 请求的响应被拦截
- 非同源DOM无法获得
- 非同源 Cookie,Localstorage,IndexDB无法读取
- 跨域:非同源资源尝试通信,但由于同源策略限制,无法进行正常通信
- 跨域解决方案:
- CORS:通常服务端做配置即可,如果需要使用cookie,客户端也得配置
- JSONP:利用script标签可以跨域的特性
- 服务器转发:同源策略是浏览器限制,我们将请求发送给同源服务器,由同源服务器转发代理即可
- postMessage解决跨域问题:h5提供了window属性postMessage,该属性可以进行跨域数据传递,所以我们当前页面内嵌入非同源的iframe标签,使用该属性即可进行跨域数据传递
42,CORS跨域资源共享
- CORS:跨域资源共享(cross-origin-resource-sharing),2014年作为http协议扩充部分正式发布,定义客户端与服务端的沟通方式,允许浏览器向跨域服务器发送XMLHttpRequest请求,但仅靠浏览器并不能实现跨域访问资源,还需要服务端配合。所以CORS就是浏览器与服务端相互配合实现跨域访问资源的机制
- CORS实现方式:当浏览器发生跨域请求时,浏览器会将请求分为简单请求与非简单请求,同时请求头字段设置Origin(当前请求协议域名端口),服务端设置响应头字段Access-Control-Allow-Origin,浏览器根据该字段与Origin判断是否拦截该响应数据。注意CORS是服务端做配置,客户端配置依靠浏览器自动完成
- 简单请求与非简单请求判定方式:只要满足一下两个条件就是简单请求,反之非简单请求。至于为什么要区分简单与非简单请求,是因为简单请求其实本质都是Form表单提交时的请求,Form表单提交本身就不受跨域限制,不需要进行非简单请求的预检操作,所以向下兼容为了不对从前的表达请求进行预检判断,就将其分成简单请求与非简单请求,当然了,使用脚本模仿简单请求也不需要进行预检操作
- 请求方式为GET/POST/HEAD其中一个
- 请求头信息不超过以下几种字段
- Accept
- Accept-Language
- Content-Language
- Last-Event-ID
- Content-Type(该字段仅限3个值)
- text/plain:数据内容以纯文本形式进行编码
- multipart/form-data:数据内容为表单数据
- application/x-www-form-urlencoded:数据内容为键值对形式(?key1=value1&key2=value2)
- 简单请求CORS处理:
- 1,浏览器发送简单请求会自动加上Origin请求头字段,告知服务器请求来源
- 2,服务器端设置响应头字段Access-Control-Allow-Origin告知浏览器允许哪些Origin,浏览器接收到这些字段发现当前Origin通过对方的Access-Control-Allow-Origin,就不拦截响应返回数据:
// 这里使用express搭建服务器: // 服务端要做的事 *代表任意Origin,如果需要特定Origin,则需要将*替换成对应Origin,注意如果需要携带cookie,不可设置该字段为* app.all('*', function (req, res, next) { res.setHeader('Access-Control-Allow-Origin', '*'); next(); });
- 非简单请求CORS处理:
- 1,浏览器首先发送预检请求(OPTIONS请求),该请求作用为告诉服务器即将发送的非简单请求的请求方式,请求头字段,Origin字段
- 2,服务端设置Access-Control-Allow-Origin,Access-Control-Allow-Methods(允许跨域资源请求方式),Access-Control-Allow-Headers(允许跨域资源的请求header)'响应头字段
app.all('*', function (req, res, next) { res.setHeader('Access-Control-Allow-Origin', '*'); res.setHeader('Access-Control-Allow-Methods', 'GET, POST, DELETE, PUT'); res.setHeader('Access-Control-Allow-Headers', 'X-Requested-With,content-type, Authorization,Authation'); // res.setHeader("Content-Type", "application/json;charset=utf-8"); next(); });
- 3,浏览器接收到预检请求响应将对比请求方式,请求头字段,Origin字段与服务端返回的响应头字段是否满足,不满足的话不允许发送该请求,满足的话发送非简单请求获取服务器数据
43,JSONP处理跨域
- JSONP处理跨域原理:script标签是可以跨域获取数据的,利用这个特性,我们可以客户端新建一个回调函数,该函数用来接收服务端传回的数据,同时创建一个script标签传入服务端jsonp.js请求地址带上客户端创建的回调函数名称,该标签返回该回调函数执行的js代码并传入服务端数据,这样客户端就可以获取服务端资源了。
- 客户端代码
<script> function getData(data) { console.log('服务端返回的JSONP数据:', data); } </script> <script src="http://127.0.0.1:3001/script?callbackName = getData "></script>
- 服务端代码
app.get('/script', (req, res) => { const fs = require('fs'); // 获取JSONP回调函数名称 const callbackName = req.query[Object.keys(req.query)[0]] // 返回给客户端JSON数据 const jsonData = JSON.stringify('this is jsonp data') // 创建jsonp.js 写入回调函数执行代码 传入参数:返回给客户端JSON数据 fs.writeFile(__dirname + "/public/jsonp.js", `${callbackName}(${jsonData})`, function(err) { if(err) return res.send(err) // 返回jsonp.js文件 res.sendFile(__dirname+'/public/jsonp.js') }); })
- 服务端生成的JSONP.js文件内容
getData("this is jsonp data")
- 客户端代码
- tips:
- 为什么叫JSONP:因为JSONP方式传入的是JSON数据,JSON数据左右有padding,因叫JSONP
- 为什么JSONP不能用POST请求:因为script标签只能发起GET请求
44,服务器转发解决跨域问题
同源策略是浏览器的安全策略,服务器与服务器之间没有该策略限制,因此可以本地搭建服务器转发浏览器的请求,简而言之当前浏览器跨域资源请求本地服务器,本地服务器将该请求转发给目标服务器,获取目标服务器结果再返回给浏览器。(当然本地服务器也要处理CORS,毕竟端口不同)
45,postMessage解决跨域
- postMessage:postMessage是h5提供的可以进行跨域数据传递的api,可以进行以下几种方式的数据传递
- 页面以及页面打开的页面之间数据传递
- 页面以及页面内嵌iframe数据传递
- 多窗口之间数据传递
- postMessage解决跨域:主要利用当前页面内嵌非同源iframe,当前页面向iframe页面使用postMessage发送数据,并在当前页面iframe返回消息的监听
- 当前页面:
<iframe id="iframe" src="http://127.0.0.1:3001"></iframe> <script> // 添加监听,监听非同源iframe页面返回的数据 window.onmessage = function (e) { // 判断当前消息来自iframe页面 if (e.origin === "http://127.0.0.1:3001") console.log('demo1 :::', e.data); } // 当iframe加载完毕使用postMessage向iframe发送消息(iframe 未加载完毕发送消息时,iframe无法获取数据) iframe.onload = function () { iframe.contentWindow.postMessage('demo1 data ', 'http://127.0.0.1:3001'); }; </script>
- 非同源iframe页面
<script> // iframe页面监听接收到发送过来的数据 window.addEventListener('message', function (e) { // 判断数据来源,通过使用source向来源发送数据 if(e.origin === 'http://127.0.0.1:3000') e.source.postMessage('demo2 返回 demo1 数据 ', '*'); }, false); </script>
- 当前页面:
46,浏览器有哪些进程,渲染进程中都有什么线程
- 浏览器为什么是多进程:保证浏览器运行健壮性,如果是单进程,任何一个web应用奔溃都会导致整个浏览器奔溃。
- 浏览器有哪些进程
- 浏览器主进程:
- 1,负责浏览器页面展示(前进后退书签栏等),
- 2,各个页面的管理,创建销毁其他进程,
- 3,将渲染进程最终获得的bitmap绘制到用户界面
- 4,网络资源管理下载
- 插件进程:每种类型插件对应一个进程,使用该插件时才创建该进程
- GPU进程:最多只存在一个,用于3D绘制
- 渲染进程:多线程,每个tab页为一个进程,互不影响(所以我们打开浏览器会发现浏览器主进程与渲染进程),主要负责页面渲染,脚本执行,时间处理
- 浏览器主进程:
- 渲染进程中的线程
- GUI渲染线程:
- 1,负责渲染页面,解析dom,css文件,构建dom树与css树合成render树
- 2,GUI渲染线程最终结果位图交给浏览器主进程显示
- 3,页面重绘回流时该线程执行
- 4,GUI线程与JS引擎线程互斥,当执行js引擎线程执行,GUI线程挂起,其更新保存在队列中,等JS引擎线程空闲再执行(主要因为JS也可以操作dom,避免与JS操作dom冲突),所以js执行时间过长会阻塞页面渲染
- js引擎线程:
- 1,负责解析处理js代码,执行js代码(V8引擎)
- 2,一个tab页面无论什么时候都只有一个js引擎线程在运行js程序
- 事件触发线程
- 1,主要用来控制事件循环(js引擎线程一个人忙不过来,需要浏览器开启该线程辅助)
- 2,当有异步事件时会将异步事件加入到事件触发线程中去
- 3,当事件触发线程条件满足(定时器任务,点击事件,ajax请求),事件触发线程会将该事件的回调函数放到任务队列之中,等待JS引擎线程调用(主线程代码运行完毕,检查任务队列,取出任务队列代码执行)
- 定时器线程
- 1,setInterval与setTimeout所在线程
- 2,负责计时,计时结束将回调函数添加到任务队列,等待JS引擎调用
- 3,js引擎是单线程,可能会出现js代码阻塞,所以不能由js引擎线程维护定时器
- 异步请求线程
- 1,XMLHttpRequest连接是通过浏览器新开一个线程请求
- 2,检测到状态变更时,如果有回调函数,将回调函数放到任务队列中等待js引擎线程调用
- GUI渲染线程:
47,从输入URL到最终页面渲染
当前文章字数超出限制,该部分移步我的另一篇文章从输入URL到页面渲染及页面渲染优化(必知必会)
48,页面渲染优化
当前文章字数超出限制,该部分移步我的另一篇文章从输入URL到页面渲染及页面渲染优化(必知必会)
49,GET/POST区别
- 1,get/post语义化来看,get方式属于向获取数据,post属于将数据交给服务端。
- 2,get请求参数通常跟在url后面,post请求参数一般放在body中
- 3,因为浏览器对url长度有限制,所以get参数长度是存在限制的,一般整个url+参数应该在2kb内,而post参数放在body中,没有参数长度限制
- 4,get请求参数放在url中,url是可见的,所以不适合包含敏感信息参数的请求,post参数放在body中,相较于get更加安全,但也不是绝对的,传输过程中如果是http的方式,那么二只都是明文传输,都不安全。
- 5,get请求参数编码方式只允许ASCII(比如?a=卡萨帝将转成?a=%E5%8D%A1%E8%90%A8%E5%B8%9D),而post请求参数无编码限制。
- 6,post请求会发送两次数据,先发送header,返回100,继续发送body(即post参数),get请求不会,只会发送一次数据。(个人在谷歌浏览器测试,post请求只会发送一次数据...,可能是不同浏览器中有不同的表现形式吧)
- 7,还有说后退刷新缓存上去别的,我没遇到过,就不写了,需要详细了解的可以看下面感谢参考文章。
- ps:关于post/get方式其实并没有本质区别,都是基于TCP协议进行数据传输,只不过我们规定了二者报文格式区别,比如get参数放在url后面,post放在body中,不过我们只要与服务端协商好数据在哪,get参数放在body中也是没问题的。
感谢参考文章
- http2 简介
- 揭秘http2
- HTTP请求/响应报文结构
- 计网之从运输层到应用层
- DNS浅析
- 前端进阶之路_网络协议之传输层(transport layer)
- TLS 详解握手流程
- 一文带你了解中间人攻击和HSTS
- 浅析CDN原理
- cookie 简介
- 浅谈V8引擎
- Unicode和UTF-8/16究竟是什么关系
- TCP的检验和
- TCP协议如何保证数据可靠性
- TCP协议可靠性是如何保证之 流量控制和拥塞控制
- 一篇带你读懂TCP之“滑动窗口”协议
- TCP的四种计时器
- TCP的快速重传机制
- 你还在为 TCP 重传、滑动窗口、流量控制、拥塞控制发愁吗?
- 选择性重传arq协议
- TCP SACK选项描述
- 快速重传与快速恢复算法
- 第10章 滑动窗口协议-1
- CH5-L13 TCP-差错控制-流量控制
- tcp的半连接与完全连接队列
- 浏览器渲染简述
- 层叠上下文 渲染图层 复合图层(硬件加速)区别与联系
- GET 与 POST 的区别
- 详解HTTP协议
- expire和etag,last-modified的优先级问题
- HTTP1.0和HTTP1.1和HTTP2.0的区别