title: 面试汇总 date: 2020-06-11 21:40:33 categories: 技术博客 tags: - IT,Web toc: true thumbnail:
面试第二篇,计算机基础与后端
https是http协议+ssl/tls协议,就是在http的基础上加上ssl的保护壳,信息加密的过程在ssl中完成。https占用443端口通讯。
https内部可以使用非对称加密和对称加密。客户端发送字符串给另一客户端,进行信息传输前使用加密算法a进行加密传输,传输到另一客户端后同样用加密算法a进行解密,这种通过同一个密钥进行的加密称为对称加密。
与对称加密相对应的使用最广的是非对称加密,采用一对密钥私钥和公钥。私钥加密后的秘文,所有公钥可以解开,但是公钥加密后的密文,只能由私钥解密,其他客户端的公钥不能解密。私钥和公钥都由服务器生成,私钥只保存在服务器端,公钥在网络上传输及客户端。
https加密过程(传统RSA过程):
证书验证阶段:客户端请求服务器获取证书公钥--->客户端解析证书--->数据传输阶段:生成随机---->用公钥加密随机值发送给服务器
非对称加密的传输过程(https加密过程)
客户端先用公钥进行加密--->服务端收到后用私钥解密--->服务器将响应用私钥加密传给客户端--->客户端收到后用公钥解密
HTTPS 在内容传输的加密上使用的是对称加密,非对称加密只作用在证书验证阶段。非对称加密的加解密效率是非常低的,而 http 的应用场景中通常端与端之间存在大量的交互,非对称加密的效率是无法接受的。在 HTTPS 的场景中只有服务端保存了私钥,一对公私钥只能实现单向的加解密
ssl证书:ssl证书包含网站的域名、证书有效期、证书颁发机构以及传输用的公钥等信息。网站与浏览器建立联系前会享浏览器发送ssl证书,浏览器收到后会验证ssl证书中的信息,验证失败就会出现证书错误的提示。
除了ssl证书,还有代码签名证书、客户端证书(加密邮件)、双因素证书等,有CA机构颁发,也可以自己制作,在此不进行探讨。
Tls1.2版本:
1.浏览器发送 clientrandom、TLS版本、加密套件列表。clientrandom 是用来最终 secret 的一个参数。加密套件,通常包含很多信息
如
TLS_ECDHE_WITH_AES_128_GCM_SHA256
意思是TLS握手过程中,使用ECDHE算法生成pre_random(这个数后面会介绍),128位的AES算法进行对称加密,在对称加密的过程中使用主流的GCM分组模式,因为对称加密中很重要的一个问题就是如何分组。最后一个是哈希摘要算法,采用SHA256算法。
其中值得解释一下的是这个哈希摘要算法,试想一个这样的场景,服务端现在给客户端发消息来了,客户端并不知道此时的消息到底是服务端发的,还是中间人伪造的消息呢?现在引入这个哈希摘要算法,将服务端的证书信息通过这个算法生成一个摘要(可以理解为比较短的字符串),用来标识这个服务端的身份,用私钥加密后把加密后的标识和自己的公钥传给客户端。客户端拿到这个公钥来解密,生成另外一份摘要。两个摘要进行对比,如果相同则能确认服务端的身份。这也就是所谓数字签名的原理。其中除了哈希算法,最重要的过程是私钥加密,公钥解密。
在2018年就推出了 TLS1.3,对于TLS1.2做了一系列的改进,主要分为这几个部分:强化安全、提高性能。
强化安全:在 TLS1.3 中废除了非常多的加密算法,最后只保留五个加密套件:
可以看到,最后剩下的对称加密算法只有 AES 和 CHACHA20,之前主流的也会这两种。分组模式也只剩下 GCM 和 POLY1305, 哈希摘要算法只剩下了 SHA256 和 SHA384 了。
提升性能:
握手流程
大体的方式和 TLS1.2 差不多,不过和 TLS 1.2 相比少了一个 RTT, 服务端不必等待对方验证证书之后才拿到client_params,而是直接在第一次握手的时候就能够拿到, 拿到之后立即计算secret,节省了之前不必要的等待时间。同时,这也意味着在第一次握手的时候客户端需要传送更多的信息,一口气给传完。
这种 TLS 1.3 握手方式也被叫做1-RTT握手。但其实这种1-RTT的握手方式还是有一些优化的空间的,接下来我们来一一介绍这些优化方式。
会话复用
会话复用有两种方式: Session ID和Session Ticket。 先说说最早出现的Seesion ID,具体做法是客户端和服务器首次连接后各自保存会话的 ID,并存储会话密钥,当再次连接时,客户端发送ID过来,服务器查找这个 ID 是否存在,如果找到了就直接复用之前的会话状态,会话密钥不用重新生成,直接用原来的那份。 但这种方式也存在一个弊端,就是当客户端数量庞大的时候,对服务端的存储压力非常大。 因而出现了第二种方式——Session Ticket。它的思路就是: 服务端的压力大,那就把压力分摊给客户端呗。具体来说,双方连接成功后,服务器加密会话信息,用Session Ticket消息发给客户端,让客户端保存下来。下次重连的时候,就把这个 Ticket 进行解密,验证它过没过期,如果没过期那就直接恢复之前的会话状态。 这种方式虽然减小了服务端的存储压力,但与带来了安全问题,即每次用一个固定的密钥来解密 Ticket 数据,一旦黑客拿到这个密钥,之前所有的历史记录也被破解了。因此为了尽量避免这样的问题,密钥需要定期进行更换。 总的来说,这些会话复用的技术在保证1-RTT的同时,也节省了生成会话密钥这些算法所消耗的时间,是一笔可观的性能提升。
PSK
刚刚说的都是1-RTT情况下的优化,那能不能优化到0-RTT呢? 答案是可以的。做法其实也很简单,在发送Session Ticket的同时带上应用数据,不用等到服务端确认,这种方式被称为Pre-Shared Key,即 PSK。 这种方式虽然方便,但也带来了安全问题。中间人截获PSK的数据,不断向服务器重复发,类似于 TCP 第一次握手携带数据,增加了服务器被攻击的风险。
对称加密常用算法:DES、3DES、AES
非对称加密常用算法:RSA、DSA、ECC
https缺点:
慢、贵
https优化:
访问速度优化:
复用session,session cache和session ticket
设置HSTS
使用http2
Nginx设置在线证书状态查询协议
false start
计算性能优化:
硬件加速方案
tls远程代理计算
使用dns 的目的是为了解决http劫持的问题。http被劫持有两种情况,dns劫持和内容劫持
内容劫持是对运行时的缓存池中的内容做修改,这样就得到错误的数据
指攻击者与通讯的两端分别创建独立的联系,并交换其所收到的数据,使通讯的两端认为他们正在通过一个私密的连接与对方直接对话,但事实上整个会话都被攻击者完全控制。在中间人攻击中,攻击者可以拦截通讯双方的通话并插入新的内容。在许多情况下这是很简单的(例如,在一个未加密的Wi-Fi 无线接入点的接受范围内的中间人攻击者,可以将自己作为一个中间人插入这个网络)
https://www.cnblogs.com/lulianqi/p/10558719.html
浏览器默认不允许在https里面请求http资源,一般都会弹出提示框询问
而且,如果在一个https页面中动态引入http资源,比如引入一个js文件,会被直接block掉。
解决方式:
1.使用相对协议:
2.如果是nginx,可以用正则表达式判断
如果网页
10*信息
20*成功
30*重定向,需进一步操作
40*客户端错误,无法完成请求
50*服务器错误
200(成功) 服务器已成功处理了请求。 通常,这表示服务器提供了请求的网页。
202服务器接受请求但尚未处理
204服务器成功处理请求但不需要返回任何实体内容
301(永久移动) 请求的网页已永久移动到新位置。 服务器返回此响应(对 GET 或 HEAD 请求的响应)时,会自动将请求者转到新位置。
302(临时移动) 服务器目前从不同位置的网页响应请求,但请求者应继续使用原有位置来进行以后的请求。
304(未修改) 自从上次请求后,请求的网页未修改过。 服务器返回此响应时,不会返回网页内容。
401 (未授权) 请求要求身份验证。 对于需要登录的网页,服务器可能返回此响应。
404 (未找到) 服务器找不到请求的网页。
403(禁止) 服务器拒绝请求。
500服务端代码错误
502(错误网关) 服务器作为网关或代理,从上游服务器收到无效响应。
503代理错误
301 302和307的区别
301:永久重定向
302:临时重定向
307:也是临时重定向,只是不允许将原本post的请求重定向到get请求上
缓存规则
1.浏览器请求某资源,通过header判断是否强缓存,若是强缓存,则从本地直接获取缓存文件,不发请求到浏览器 2.若不是强缓存,发送请求到服务器,服务器通过一些request header确定是否是协商缓存,如果是,服务器将请求返回,但不返回资源,而是让客户端从本地缓存获取资源 3.强缓存和协商缓存,资源都是本地,只是强缓存不会发送请求到服务器,协商缓存会发送请求到服务器。 4.不是协商缓存,则浏览器将资源发送客户端。 强缓存:
服务端第一次响应请求时,告知浏览器还存在本地,设定时间,时间之内还获取该资源就从本地获取。在 Chrome 中,强缓存又分为 Disk Cache (存放在硬盘中)和 Memory Cache (存放在内存中),存放的位置是由浏览器控制的。 Expires和Cache-Control、Progma三个响应首部字段共同来控制。
Expires 的值是一个 HTTP 日期,告知过期时间和最大生命周期。再次请求从缓存中找资源,若请求时间比Expires设定时间早则可用本地资源。Expires 的优先级在三个 Header 属性中是最低的。 Cache-Control缓存资源最大生命周期,秒为单位。一起使用以Cache-Control为主。常用的属性值如有:
Pragma 只有一个属性值,就是 no-cache ,效果和 Cache-Control 中的 no-cache 一致,不使用强缓存,需要与服务器验证缓存是否新鲜,在 3 个头部属性中的优先级最高。
协商缓存:
当浏览器的强缓存失效的时候或者请求头中设置了不走强缓存,并且在请求头中设置了If-Modified-Since 或者 If-None-Match 的时候,会将这两个属性值到服务端去验证是否命中协商缓存,如果命中了协商缓存,会返回 304 状态,加载浏览器缓存,并且响应头会设置 Last-Modified 或者 ETag 属性。
ETag/If-None-Match 的值是一串 hash 码,代表的是一个资源的标识符,当服务端的文件变化的时候,它的 hash码会随之改变,通过请求头中的 If-None-Match 和当前文件的 hash 值进行比较,如果相等则表示命中协商缓存。ETag 又有强弱校验之分,如果 hash 码是以 "W/" 开头的一串字符串,说明此时协商缓存的校验是弱校验的,只有服务器上的文件差异(根据 ETag 计算方式来决定)达到能够触发 hash 值后缀变化的时候,才会真正地请求资源,否则返回 304 并加载浏览器缓存。 Last-Modified/If-Modified-Since 的值代表的是文件的最后修改时间,第一次请求服务端会把资源的最后修改时间放到 Last-Modified 响应头中,第二次发起请求的时候,请求头会带上上一次响应头中的 Last-Modified 的时间,并放到 If-Modified-Since 请求头属性中,服务端根据文件最后一次修改时间和 If-Modified-Since 的值进行比较,如果相等,返回 304 ,并加载浏览器缓存。
ETag/If-None-Match 的出现主要解决了 Last-Modified/If-Modified-Since 所解决不了的问题:
如果文件被修改了,但是内容没有任何变化的时候,Last-Modified/If-Modified-Since 会错误地返回 304 ,上面的例子就说明了这个问题
1.浏览器第一次请求资源,服务器响应,返回资源,响应头加Last-Modified。 2.浏览器再次请求资源,在请求头加上if modified since,该值为上次Last-Modified的值 3.服务器接受请求,将ifmodifiedsince值和资源最后修改值做对比,若一致则返回304,协商缓存。 Etag周期性重写资源,但资源没变化,加注释等无关紧要信息。用Etag区分两个资源是否一致,随response返回和请求头的if-none-match相比较,判断资源在两次请求中是否修改,未修改则协商缓存。
以MDN为准:渲染页面—浏览器的工作原理
导航:用户通过在地址栏输入一个url,点击一个链接、提交表单或者其他行为
1.域名解析。对于一个web页面来说,导航的第一步就是要寻找页面资源的位置。浏览器通过服务器名称请求DNS进行查找,最终返回一个ip地址,第一次请求初始化后,这个IP地址可能会被缓存一段时间,这样可以通过从缓存中检索IP地址,而不是通过域名服务器查找。DNS需要对不同页面指向的主机名进行查找。
2.TCP三次握手。一旦获取到IP地址,浏览器就会通过TCP三次握手与服务器建立连接,这个机制是用来让两端尝试通信,浏览器和服务器在发送数据之前,通过上层协议https。tcp的三次握手通常被称为SYN-SYNACK-ACK。
3.发起http请求。请求时如果是https会进行TLS协商。它决定密码会被用于加密通信,验证服务器,从而在进行真实的数据传输之前建立安全连接,,因此在发送真正的请求内容之前还需要三次往返服务器
前三个步骤总共8次访问服务器。
4.服务器收到http请求并响应http请求。初始请求的响应包含所接收数据的第一个字节。Time to First Bytes是用户通过点击链接进行请求与收到第一个HTML包之间的时间,这一块内容通常是14kb的数据。
第一个响应包是14kb大小,这是TCP慢开始的一部分,慢开始是一种均衡网络连接速度的算法,慢开始逐渐增加发送数据的数量直到达到网络最大带宽。
收到初始包之后,服务器会将下一个包的大小加倍到大约28kb,后续包依次是前一个包大小的二倍直到达到预定的阈值,或者遇到拥塞。
拥塞控制:当服务器用TCP包发送数据时,客户端通过返回确认帧来确认传输,由于网络和硬件条件,连接的容量是有限的,如果服务器太快地发送太多的包,它们可能会被丢弃。服务器把它们当作确认帧丢失,拥塞控制算法使用这个发送包和确认帧流来确定发送速率。
5.浏览器解析html,请求图片、样式等资源。一旦浏览器收到数据的第一块,它就开始解析收到的信息,
6.断开http连接
7.浏览器渲染页面
其中第五部渲染的流程为
1.HTML 被 HTML 解析器解析成 DOM 树;
2.CSS 被 CSS 解析器解析成 CSSOM 树;
3.结合 DOM 树和 CSSOM 树,生成一棵渲染树(Render Tree),这一过程称为 Attachment;
4.生成布局(flow),浏览器在屏幕上“画”出渲染树中的所有节点;
5.将布局绘制(paint)在屏幕上,显示出整个页面。
由于 HTTP 协议本身是无状态的,上一个请求与下一个请求无任何关联,为此我们引入 Session 来存储用户请求信息以解决特定场景下无状态导致的问题(比如登录、购物)。
Session是存储在服务器端的,而Cookie是服务器发送到客户端浏览器的数据,它会在浏览器下一次访问同一服务器时会发送到服务器上,告知服务器两次请求是否来源于同一服务器,进而保持用户登录状态 等等。现在基本上都是通过cookie存储简历客户端和服务端的关联,一次建立客户端与服务端的关联和用户认证状态的维护。Cookie和Session通过一个sessionId建立起浏览器和服务器之间的连接。
cookie不是很安全,别人可以分析本地的cookie进行cookie欺骗
session保存的是对象,cookie保存的是字符串
session会在一定时间内保存在服务器,当访问增多时会占用服务器性能
单个cookie在客户端的限制是4k,一个站点的cookie浏览器允许保存20个,cookie只在设置的cookie过期之前有效,即使窗口关闭或者浏览器关闭。
session需要借助cookie才能工作,如果用户禁用cookie,session也将失效。
cookie的完整工作流程:
用户在页面输入用户名和密码并传递给后端,后端接收到后验证通过,创建session,session可以把用户名、权限、首选项设置等信息存起来(这就是所谓的状态,内存里在会话期间维持一堆信息),同时通过设置Cookie返回sessionId给浏览器端,浏览器拿到Cookie,至此登录完成,浏览器之后的请求都自动带着Cookie(内含sessionId),服务器会根据sessionId拿到session的信息,验明正身,区分下VIP什么的。
cookie的位置
在浏览器的开发者工具中--Application-cookies查看当前页面的cookie
尽管我们在浏览器中查看cookie但是并不意味着cookie只存在于浏览器中,实际上,cookie的相关内容还可以存放在本地文件中,比如mac下的chrome,存放目录在~/Library/Application Support/Google/Chrome/Default,里面会有一个名为cookies的数据库文件,可以使用sqlite打开
cookie的设置方法
简单来说分为4步:
1.客户端发送http请求到服务器
2.服务器收到http请求,在响应头里加一个set-cookie字段
3.浏览器收到响应后保存下cookie
4.之后对该服务器的每一次请求中都通过cookie字段将cookie信息发给服务器
cookie的属性
Name/Value:用JavaScript操作cookie时要对Value进行编码处理
Expires/Max-Age:都是设置cookie的过期时间,expires是设置具体的某一个日期,Max—Age是设置cookie失效前经过的秒数。
当Expires属性缺省时,表示是会话性cookie。当为会话性cookie时,值保存在客户端内存中,并在用户关闭浏览器时失效。有些浏览器会提供会话恢复功能,即使关掉浏览器再打开也有保存
与会话性cookie对应的是永久性cookie,在到达过期时间后自动消失或被主动清除。Expires设定的日期只与客户端有关,与服务端无关
Max-Age可以是正数、负数、0。为正数时为持久性cookie,到达过期时间后消失。为负数时表示会话性cookie,为0时立即删除cookie
如果Max-Age和Expires同时存在,Max-Age的优先级更高。
HTTPonly
设置Httponly可以防止客户端脚本通过document.cookie等形式访问,有助于避免XSS攻击。
Domain
Domain指定了可以送达的主机名,如果没有指定,默认为当前文档中的主机不问
Path
Path指定了一个URL路径,这个路径必须出现在要请求的资源的路径中才可以发送cookie首部。
比如设置 Path=/docs,/docs/web会带cookie首部,而/test则不会
Secure属性
标记为Secure的cookie只应通过被https协议加密过的请求发送给服务端,使用https协议能够保护cookie在浏览器和web服务器间的查孙处
SameSite
SameSite可以让cookie在跨站请求时不会被发送,从而可以阻止跨站请求伪造攻击(CSRF)
属性:
Strict仅允许一方请求携带cookie,即当前网页URL与请求目标URL完全一致。
Lax允许部分第三方请求携带cookie,
None无论是否跨站都会发送cookie,
html5中新加入localstorage特性,解决cookie存储空间不足的问题,webstorage是本地存储,存储在客户端,包括localstorage和sessionstorage
localstorage的生命周期是永久,存放数据大小为5M,可以在所有同源窗口中共享。只要在相同的协议、相同的主机名、相同的端口下,就能读取/修改到同一份localStorage数据。
sessionstorage仅在当前对话下有效,关闭页面或者浏览器后被清除,不可以在不同浏览器窗口中共享。由于sessionStorage
的生存期太短,因此应用场景很有限,但从另一方面来看,不容易出现异常情况,比较可靠。
webstorage拥有setItem、getItem、removeItem、clear等方法,cookie需要前端开发者自己封装setcookie、getcookie
但是cookie可以与服务器交互,作为http规范的一部分存在在浏览器与服务器之间来回传递,而webstorage仅仅是为了在本地存储,不会自动发送给服务器
webstorage的好处:
减少网络流量:数据存储在本地后就不必再向服务器请求数据,减少不必要的数据请求
快速显示数据:性能好,从本地获取数据比网络上快,
临时存储:很多时候数据只在用户浏览期间使用,关闭页面数据就可以丢弃,这时候用sessionstorage非常方便
sessionstorage与页面js数据对象
页面的js数据对象生存期只在当前页面有效,刷新页面或者转到另一页面,数据就不存在了
sessionstorage只用同源是窗口刷新页面或者进入同源的不同页面,只要浏览器不关闭数据仍然存在
localstorage为标准的键值对(Key-Value,简称KV)数据类型,简单但也易扩展,只要以某种编码方式把想要存储进localstorage的对象给转化成字符串,就能轻松支持。
cookie验证与token验证的区别
token验证是无状态的,服务器不记录哪些用户登录了或者哪些 JWT 被发布了,而是每个请求都带上了服务器需要验证的 token。用户输入登录信息,服务器判断登录信息正确,返回一个 token。token 存储在客户端,大多数通常在 local storage,但是也可以存储在 session storage 或者 cookie 中。
token验证的优势:
document.cookie
属性用于读写当前网页的 Cookie。
读取的时候,它会返回当前网页的所有 Cookie,前提是该 Cookie 不能有HTTPOnly
属性。
document.cookie
一次性读出两个 Cookie,它们之间使用分号分隔。必须手动还原,才能取出每一个 Cookie 的值。
同时document.cookie
属性是可写的,可以通过它为当前网站添加 Cookie。
写入的时候,Cookie 的值必须写成key=value
的形式。注意,等号两边不能有空格。另外,写入 Cookie 的时候,必须对分号、逗号和空格进行转义(它们都不允许作为 Cookie 的值),这可以用encodeURIComponent
方法达到。
但是,document.cookie
一次只能写入一个 Cookie,而且写入并不是覆盖,而是添加。
document.cookie
读写行为的差异(一次可以读出全部 Cookie,但是只能写入一个 Cookie),与 HTTP 协议的 Cookie 通信格式有关。浏览器向服务器发送 Cookie 的时候,Cookie
字段是使用一行将所有 Cookie 全部发送;服务器向浏览器设置 Cookie 的时候,Set-Cookie
字段是一行设置一个 Cookie。
document.cookie // "foo=bar;baz=bar"
var cookies = document.cookie.split(';');
for (var i = 0; i < cookies.length; i++) {
console.log(cookies[i]);
}
// foo=bar
// baz=bar
document.cookie = 'fontSize=14';
在服务器端是不能直接删除客户端的cookie的。通常采取在服务端设置cookie的过期时间为负,然后用添加cookie的方式返回给客户端,客户端就知道自己的cookie没用了,从而删除它
cookie.setMaxAge(0);
response.addCookie(cookie);
编译型语言是指首先将源代码编译生成机器指令,再由机器运行机器码(二进制)
解释型语言是指不直接编译出机器指令,而是先翻译成中间代码,由解释器对中间代码解释运行
动态类型语言是指数据类型的检查是在运行时做的,不需要给变量指定数据类型,该语言会在第一次赋值给变量是在内部记录数据类型。
静态类型语言是指数据类型的检查是在运行前(编译阶段)做的。
Get与Post本质上都是浏览器向服务器请求数据并接受,get获取资源,post创建资源,GET不会改变服务器上的资源,而POST会对服务器资源进行改变。
get提交的数据会在地址栏中显示,post提交的数据在http包中,因此post更安全,同时GET请求的长度受限于浏览器或服务器对URL长度的限制,允许发送的数据量比较小,
Get是通过URL请求,一次发送接收,同时由于URL是地址栏,因此服务器对Get请求字节(长度)
Post是先请求,服务器响应请求后再进行请求和接收,对请求大小不做限制
使用PUT时,必须明确知道要操作的对象,如果对象不存在,创建对象;如果对象存在,则全部替换目标对象。同样POST既可以创建对象,也可以修改对象。但用POST创建对象时,之前并不知道要操作的对象,由HTTP服务器为新创建的对象生成一个唯一的URI;使用POST修改已存在的对象时,一般只是修改目标对象的部分内容。
PUT是“idempotent”(幂等),意味着相同的PUT请求不管执行多少次,结果都是一样的。但POST则不是。就类似于"x=1"这条语句是幂等的,因为无论执行多少次,变量x的值都是1;但"x++"就不是幂等的,因为每执行一次,变量x的值都不一样。
简单请求的 HTTP 方法只能是 GET、HEAD 或 POST
简单请求的 HTTP 头只能是 Accept/Accept-Language/Conent-Language/Content-Type 等
简单请求的 Content-Type 头只能是 text/plain、multipart/form-data 或 application/x-www-form-urlencoded
理解:
简单请求就是普通 HTML Form 在不依赖脚本的情况下可以发出的请求,比如表单的 method 如果指定为 POST ,可以用 enctype 属性指定用什么方式对表单内容进行编码,合法的值就是前述这三种。
非简单请求就是普通 HTML Form 无法实现的请求。比如 PUT 方法、需要其他的内容编码方式、自定义头之类的。
对于服务器来说,第一,许多服务器压根没打算给跨源用。当然你不给 CORS 响应头,浏览器也不会使用响应结果,但是请求本身可能已经造成了后果。所以最好是默认禁止跨源请求。
第二,要回答某个请求是否接受跨源,可能涉及额外的计算逻辑。这个逻辑可能很简单,比如一律放行。也可能比较复杂,结果可能取决于哪个资源哪种操作来自哪个 origin。对浏览器来说,就是某个资源是否允许跨源这么简单;对服务器来说,计算成本却可大可小。所以我们希望最好不用每次请求都让服务器劳神计算。
CORS-preflight 就是这样一种机制,浏览器先单独请求一次,询问服务器某个资源是否可以跨源,如果不允许的话就不发实际的请求。注意先许可再请求等于默认禁止了跨源请求。如果允许的话,浏览器会记住,然后发实际请求,且之后每次就都直接请求而不用再询问服务器否可以跨源了。于是,服务器想支持跨源,就只要针对 preflight 进行跨源许可计算。本身真正的响应代码则完全不管这个事情。并且因为 preflight 是许可式的,也就是说如果服务器不打算接受跨源,什么事情都不用做。
但是这机制只能限于非简单请求。在处理简单请求的时候,如果服务器不打算接受跨源请求,不能依赖 CORS-preflight 机制。因为不通过 CORS,普通表单也能发起简单请求,所以默认禁止跨源是做不到的。
一般发送post请求前先发送一个Option请求,然后再发送post请求
发送Options请求的用途:
1.获取服务器支持的http请求方法
2.用来检查服务器的性能,例如AJAX进行跨域请求时的预检,需要向另一个域名的资源发送一个http options请求头,以判断实际发送的请求是否安全
前端通过Ajax,后端设置CORS允许跨域请求时
当请求为复杂请求:PUT、DELETE、CONNECT等时发送OPTIONS请求
因为OPTIONS请求也是占带宽和时间的,从上图也可以看到,每个请求都带一个OPTIONS,非常难受。
后端在请求的返回头部添加:
Access-Control-Max-Age:(number) 。数值代表preflight request (预检请求)的返回结果(即 Access-Control-Allow-Methods 和Access-Control-Allow-Headers 两个请求头重提供的信息) 可以被缓存多久,单位是秒。
Access-Control-Max-Age: 600 表示将预检请求的结果缓存10分钟
不同浏览器有不同的上限。在Firefox中,上限是24h(即86400秒),而在Chromium 中则是10min(即600秒)。Chromium 同时规定了一个默认值 5 秒。 如果值为 -1,则表示禁用缓存,每一次请求都需要提供预检请求,即用OPTIONS请求进行检测。
Access-Control-Max-Age方法对完全一样的url的缓存设置生效,多一个参数也视为不同url。也就是说,如果设置了10分钟的缓存,在10分钟内,所有请求第一次会产生options请求,第二次以及第二次以后就只发送真正的请求了。
可以的,
但是往GET里加body会导致缓存机制失效。“GET 被设计来用 URI 来识别资源,如果让它的请求体中携带数据,那么通常的缓存服务便失效了,URI 不能作为缓存的 Key。
缓存,大概是指那种预加载和后存储,会涉及到一个网络请求的“安全性”。如果不安全,显然是不能缓存的。
通常可以用url中是否包含? = & php asp等字符来区分url,换句话说,没有任何参数的URL就是静态url
静态URL的特点:
1.与动态URL相比,静态URL更有利于搜索引擎的收集和用户体验的改善,静态URL更短,URL显示模式可以定制
2.当用户加载静态页的时候不操作数据库,而是直接提取文件
3.把动态URL转换为静态URL的过程称为URL重写,也称为URL重定向,我们看到的URL并不是真正静态的,而是伪静态的,即动态URL通过技术手段显示为静态URL
4.静态页面都是独立存在于服务器的文件
优点:
加载速度快,减小服务器压力,
动态url的优点:
只改变参数使得页面的管理更加容易,
动态url的缺点:
Http协议是无状态的,只能由客户端主动发起,服务端再被动响应,服务端无法向客户端主动推送内容,并且一旦服务器响应结束,链接就会断开(见注解部分),所以无法进行实时通信。WebSocket协议正是为解决客户端与服务端实时通信而产生的技术
WebSocket协议本质上是一个基于tcp的协议,它是先通过HTTP协议发起一条特殊的http请求进行握手后,如果服务端支持WebSocket协议,则会进行协议升级。WebSocket会使用http协议握手后创建的tcp链接,和http协议不同的是,WebSocket的tcp链接是个长链接(不会断开),所以服务端与客户端就可以通过此TCP连接进行实时通信。
WebSocket是html5规范中的一个部分,它借鉴了socket这种思想,为web应用程序客户端和服务端之间(注意是客户端服务端)提供了一种全双工通信机制。同时,它又是一种新的应用层协议,WebSocket协议是为了提供web应用程序和服务端全双工通信而专门制定的一种应用层协议,
为什么WebSocket连接可以实现全双工通信而HTTP连接不行呢
实际上HTTP协议是建立在TCP协议之上的,TCP协议本身就实现了全双工通信,但是HTTP协议的请求-应答机制限制了全双工通信。WebSocket连接建立以后,其实只是简单规定了一下:接下来,咱们通信就不使用HTTP协议了,直接互相发数据吧。
WebSocket 使用 ws 或 wss 的统一资源标志符(URI),其中 wss 表示使用了 TLS 的 WebSocket。
ws:// 数据不是加密的,对于任何中间人来说其数据都是可见的。
wss:// 是基于 TLS 的 WebSocket,类似于 HTTPS 是基于 TLS 的 HTTP),传输安全层在发送方对数据进行了加密,在接收方进行解密
websocket的特点:
当web程序执行到new WebSocket(url)接口时,browser就开始与url对应的webserver建立握手连接过程
首先建立http连接,也就是Browser与Websocket服务器通过tcp三次握手建立连接,如果这个过程失败就后面的过程就不会执行,直接收到错误。
在tcp建立成功之后,Browser通过http协议发送websocket支持的版本号,协议的版本号、主机等信息发送给服务器。
服务器收到之后确认信息,正确的话就接受本次握手连接,并给出响应的数据恢复,回复的数据也采用http传输
浏览器收到服务器回复的数据包之后数据包和信息都没有问题,就表示连接成功,触发onopen消息。即升级为websocket连接
升级时发送请求头
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: sN9cRrP/n9NdMgdcy2VJFQ==
Sec-WebSocket-Version: 13
前两行表示升级为websocket请求
Sec-WebSocket-Key
是由浏览器随机生成的,提供基本的防护,防止恶意或者无意的连接。
Sec-WebSocket-Version
表示 WebSocket 的版本,最初 WebSocket 协议太多,不同厂商都有自己的协议版本,不过现在已经定下来了。如果服务端不支持该版本,需要返回一个 Sec-WebSocket-Versionheader
,里面包含服务端支持的版本号。
Sec-WebSocket-Extensions
:用于协商本次连接要使用的 WebSocket 扩展
Sec-WebSocket-Protocol
:协议
响应信息
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: HSmrc0sMlYUkAGmm5OPpG2HaGWk=
Sec-WebSocket-Protocol: chat
Sec-WebSocket-Accept
这个则是经过服务器确认,并且加密过后的 Sec-WebSocket-Key
;Sec-WebSocket-Protocol
则是表示最终使用的协议。连接代码
let socket = new WebSocket('wss://')
socket.onopen = function(e) {
socket.send('a')
}
socket.onmessage = function(event) {
console.log(event.data)
}
socket.onclose = function(event) {
// ...
}
socket.onerror = function(error) {
console.log(`[error] ${error.message}`)
}
一个程序至少有一个进程,一个进程至少有一个线程
一个进程可以包含多个线程,进程的内存空间是共享的,每个进程可以共享内存,但是有的共享内存每次只能给一个线程使用,使用完才能给下一个线程使用。为了防止多个线程同时读写同一块内存,可以对这种内存采用互斥锁
有的内存可以同时供给固定数目的线程使用,同时为了防止过多线程同时读写,使用信号量保证线程数
进程与线程最主要的区别是不同的操作系统资源管理方式。进程有独立的地址空间,一个进程崩溃后不会影响其他进程,而线程之间没有单独的地址空间,一个线程死掉等于整个进程死掉。所以多进程的程序比多线程的程序健壮,但是进程切换时耗费资源较大,效率较差,进程还包含一个私有的虚拟地址空间,该空间仅能被其中的进程访问
进程是具有系统进行资源分配和调度的独立单位,线程是cpu调度和分派的基本单位,也是独立运行,但线程只拥有一点运行时必不可少的资源(寄存器、栈、技术器等),操作系统没有将线程看作独立的应用,来实现资源的调配和进程的调度和管理。
线程只能归属于一个进程并访问该进程的资源,当操作系统创建进程后,该进程会自动申请一个名为主线程或首要线程的线程。
一个线程可以创建和撤销另一个线程,同一个进程中的线程可以并发执行。
进程间通信
主要分为:管道、系统IPC(包括消息队列、信号量、共享存储)、SOCKET
管道主要分为:普通管道PIPE 、流管道(s_pipe)、命名管道(name_pipe)
线程间通信
进程同步
原子操作、信号量机制、自旋锁管程、会合、
线程同步
互斥量:采用互斥对象机制,只有拥有互斥对象的线程才有访问公共资源的权限。因为互斥对象只有一个,所以可以保证公共资源不会被多个线程同时访问。
信号量:它允许同一时刻多个线程访问同一资源,但是需要控制同一时刻访问此资源的最大线程数量。
事件(信号):通过通知操作的方式来保持多线程同步,还可以方便的实现多线程优先级的比较操作。
就绪状态:进程已获得除处理机以外的所需资源,等待分配处理机资源
运行状态:占用处理机资源运行,处于此状态的进程数小于等于CPU数
阻塞状态: 进程等待某种条件,在条件满足之前无法执行
转换
就绪→执行
处于就绪状态的进程,当进程调度程序为之分配了处理机后,该进程便由就绪状态转变成执行状态。
执行→就绪
处于执行状态的进程在其执行过程中,因分配给它的一个时间片已用完而不得不让出处理机,于是进程从执行状态转变成就绪状态。
执行→阻塞
正在执行的进程因等待某种事件发生而无法继续执行时,便从执行状态变成阻塞状态。
阻塞→就绪
处于阻塞状态的进程,若其等待的事件已经发生,于是进程由阻塞状态转变为就绪状态。
FCFS(先来先服务),优先级调度算法,时间片轮转,多级反馈队列调度
先来先服务(FCFS)调度算法是一种最简单的调度算法,该算法既可用于作业调度,也可用于进程调度。当在作业调度中采用该算法时,每次调度都是从后备作业队列中选择一个或多个最先进入该队列的作业,将它们调入内存,为它们分配资源、创建进程,然后放入就绪队列。在进程调度中采用FCFS算法时,则每次调度是从就绪队列中选择一个最先进入该队列的进程,为之分配处理机,使之投入运行。该进程一直运行到完成或发生某事件而阻塞后才放弃处理机。
优先级调度算法
为了照顾紧迫型作业,使之在进入系统后便获得优先处理,引入了最高优先权优先(FPF)调度算法。此算法常被用于批处理系统中,作为作业调度算法,也作为多种操作系统中的进程调度算法,还可用于实时系统中。当把该算法用于作业调度时,系统将从后备队列中选择若干个优先权最高的作业装入内存。当用于进程调度时,该算法是把处理机分配给就绪队列中优先权最高的进程,
时间片轮转法
时间片轮转在早期的时间片轮转法中,系统将所有的就绪进程按先来先服务的原则排成一个队列,每次调度时,把CPU 分配给队首进程,并令其执行一个时间片。时间片的大小从几ms 到几百ms。当执行的时间片用完时,由一个计时器发出时钟中断请求,调度程序便据此信号来停止该进程的执行,并将它送往就绪队列的末尾;然后,再把处理机分配给就绪队列中新的队首进程,同时也让它执行一个时间片。这样就可以保证就绪队列中的所有进程在一给定的时间内均能获得一时间片的处理机执行时间。换言之,系统能在给定的时间内响应所有用户的请求。
多级反馈队列调度
前面介绍的各种用作进程调度的算法都有一定的局限性。如短进程优先的调度算法,仅照顾了短进程而忽略了长进程,而且如果并未指明进程的长度,则短进程优先和基于进程长度的抢占式调度算法都将无法使用。而多级反馈队列调度算法则不必事先知道各种进程所需的执行时间,而且还可以满足各种类型进程的需要,因而它是目前被公认的一种较好的进程调度算法。
(1) 应设置多个就绪队列,并为各个队列赋予不同的优先级。第一个队列的优先级最高,第二个队列次之,其余各队列的优先权逐个降低。该算法赋予各个队列中进程执行时间片的大小也各不相同,在优先权愈高的队列中,为每个进程所规定的执行时间片就愈小。例如,第二个队列的时间片要比第一个队列的时间片长一倍,……,第i+1个队列的时间片要比第i个队列的时间片长一倍。
(2) 当一个新进程进入内存后,首先将它放入第一队列的末尾,按FCFS原则排队等待调度。当轮到该进程执行时,如它能在该时间片内完成,便可准备撤离系统;如果它在一个时间片结束时尚未完成,调度程序便将该进程转入第二队列的末尾,再同样地按FCFS原则等待调度执行;如果它在第二队列中运行一个时间片后仍未完成,再依次将它放入第三队列,……,如此下去,当一个长作业(进程)从第一队列依次降到第n队列后,在第n 队列便采取按时间片轮转的方式运行。
(3) 仅当第一队列空闲时,调度程序才调度第二队列中的进程运行;仅当第1~(i-1)队列均空时,才会调度第i队列中的进程运行。如果处理机正在第i队列中为某进程服务时,又有新进程进入优先权较高的队列(第1~(i-1)中的任何一个队列),则此时新进程将抢占正在运行进程的处理机,即由调度程序把正在运行的进程放回到第i队列的末尾,把处理机分配给新到的高优先权进程。
在两个或者多个并发进程中,如果每个进程持有某种资源而又等待其它进程释放它或它们现在保持着的资源,在未改变这种状态之前都不能向前推进,称这一组进程产生了死锁。通俗的讲就是两个或多个进程无限期的阻塞、相互等待的一种状态。
死锁产生的四个条件(有一个条件不成立,则不会产生死锁)
解锁的方法
解决死锁的基本方法如下:
预防死锁、避免死锁、检测死锁、解除死锁
解决四多的常用策略如下:
鸵鸟策略、预防策略、避免策略、检测与解除死锁
缓冲区溢出是指当计算机向缓冲区填充数据时超出了缓冲区本身的容量,溢出的数据覆盖在合法数据上。
危害有以下两点:
造成缓冲区溢出的主要原因是程序中没有仔细检查用户输入。
阻塞调用是指调用结果返回之前当前线程会被挂起,调用线程只有在得到结果后才会返回
非阻塞调用是指在不能立刻得到结果之前该调用不会阻塞当前线程
同步与异步关注的是消息通信机制,一个异步过程调用发出后不会立即得到结果,调用之后通过状态、通知、回调通知调用者
同步调用是指没有得到结果前该调用不返回,调用返回就能得到返回值
优点:可以弥补物理内存大小的不足,可以提高反应速度,减少对物理内存的读取延长内存使用寿命
缺点:占用一定的物理硬盘空间,设置不当会影响整机的速度和稳定性
https://zhuanlan.zhihu.com/p/23755202
副作用:
FIFO、最短寻道时间优先、电梯算法
作为一种外部的输入输出设备,与 CPU 缓存和内存相比,硬盘极慢的读取和写入速度就显得比较合理了,然而几千倍甚至几十万倍的速度差异也确实让人很难想象或者接受
原因: