计算机网络
1、TCP和UDP的区别⭐
TCP | UDP |
---|---|
面向连接 | 无连接(发送数据前不需要先建立连接) |
可靠(无差错,不丢失,不重复,且按序到达,适合大数据量的交换) | 不保证可靠交付,尽最大努力交付 |
面向字节流(适用于对数据完整性和顺序有严格要求的应用,如网页浏览、电子邮件和文件传输) | 面向报文(适用于对速度要求高且允许偶尔数据丢失的应用,如直播流媒体、在线游戏和语音通话) |
首部20字节 | 首部8字节 |
1对1 | 1对1,1对多 |
传输速度较慢:由于需要建立连接和进行错误检查,TCP 的传输速度相对较慢。 | 传输速度快:由于没有错误检查和连接建立的过程,UDP 传输速度更快,延迟更低。 |
2、tcp拥塞机制
在某段时间,若对网络中某一资源的需求超过了该资源所能提供的可用部分,网络性能就要变坏。这种情况就叫做拥塞。
∑对资源的需求>可用资源
TCP进行拥塞控制的算法有四种,即慢开始、拥塞避免、快重传和快恢复。
拥塞控制也叫基于窗口的拥塞控制。发送方维持一个叫做拥塞窗口cwnd的状态变量。发送方让自己的发送窗口等于拥塞窗口。
发送方控制拥塞窗口的原则是:只要网络没有出现拥塞,拥塞窗口就可以再增大一些,以便把更多的分组发送出去,这样就可以提高网络的利用率。但只要网络出现拥塞或有可能出现拥塞,就必须把拥塞窗口减小一些,以减少注入到网络中的分组数,以便缓解网络出现的拥塞。
判断网络拥塞的依据就是出现了超时。
慢开始、拥塞避免
发送的最初执行慢开始,令 cwnd=1,发送方只能发送 1 个报文段;当收到确认后,将 cwnd 加倍,因此之后发送方能够发送的报文段数量为:2、4、8 ...
为了防止拥塞窗口cwnd增长过大引起网络拥塞,还需要设置一个慢开始门限ssthresh状态变量。
慢开始门限ssthresh的用法下:
- 当cwnd<ssthresh时,使用上述的慢开始算法(cwnd 加倍)。
- 当cwnd>sthresh时,停止使用慢开始算法而改用拥塞避免算法(每个轮次只将 cwnd 加 1)。
- 当cwnd= ssthresh时,既可使用慢开始算法,也可使用拥塞避免算法。
拥塞避免算法的思路是让拥塞窗口cwnd缓慢地增大,即每经过一个往返时间RTT就把发运方的拥塞窗口cwnd加1。因此在拥塞避免阶段就有”加法增大”的特点。这表明在拥塞避免阶段,拥塞窗口cwnd按线性规律缓慢增长,比慢开始算法的拥塞窗口增长速率缓慢得多。
快重传、快恢复
快重传算法规定,发送方只要一连收到3个重复确认,就知道接收方确实没有收到报文段M3,因而应当立即进行重传(即“快重传”),这样就不会出现超时,发送方也不就会误认为出现了网络拥塞。
因此执行快恢复,令 ssthresh = cwnd/2 ,cwnd = ssthresh,注意到此时直接进入拥塞避免。
PS:慢开始和快恢复的快慢指的是 cwnd 的设定值,而不是 cwnd 的增长速率。慢开始 cwnd 设定为 1,而快恢复 cwnd 设定为 ssthresh。
3、TCP三次握手
客户端和服务端都需要知道各自可收发,因此需要三次握手。
在TCP协议中,主动发起请求的一端为客户端,被动连接的一端称为服务端。不管是客户端还是服务端,TCP连接建立完后都能发送和接收数据,所以TCP也是一个全双工的协议。
1、客户端发起请求(SYN)连接服务器端。
2、服务器端接受请求,然后发送确认和请求(SYN+ACK)给客户端。
3、客户端接受请求,向服务器端发送确认连接(ACK),客户端和服务器端连接成功,完成三次握手。
简化三次握手:
从图片可以得到三次握手可以简化为:C发起请求连接S确认,也发起连接C确认我们再看看每次握手的作用:
第一次握手:S只可以确认:自己可以接受C发送的报文段
第二次握手:C可以确认:S收到了自己发送的报文段,并且可以确认:自己可以接受S发送的报文段
第三次握手:S可以确认:C收到了自己发送的报文段
明明两次握手就可以建立起连接,为什么还需要第三次应答?
为了防止失效的连接请求报文段被服务端接收,从而产生错误。
4、 为什么连接的时候是三次握手,关闭的时候却是四次握手?
因为当服务器端收到客户端的SYN连接请求报文后,可以直接发送SYN+ACK报文。
其中ACK报文是用来应答的,SYN报文是用来同步的。
但是关闭连接时,当服务器端收到FIN报文时,很可能并不会立即关闭SOCKET,所以只能先回复一个ACK报文,告诉客户端,"你发的FIN报文我收到了"。
只有等到我服务器端所有的报文都发送完了,我才能发送FIN报文,因此不能一起发送。
故需要四步握手。
5、TCP第三次握手失败后怎么办
当失败时服务器并不会重传ack报文,而是直接发送RTS报文段,进入CLOSED状态。
这样做的目的是为了防止SYN洪泛攻击。
SYN攻击利用的是TCP的三次握手机制,攻击端利用伪造的IP地址向被攻击端发出请求,而被攻击端发出的响应报文将永远发送不到目的地,那么被攻击端在等待关闭这个连接的过程中消耗了资源,如果有成千上万的这种连接,主机资源将被耗尽,从而达到攻击的目的。我们可以利用路由器的TCP拦截功能,使网络上的主机受到保护(以Cisco路由器为例)。
6、如果已经建立了连接,但是客户端突然出现故障了怎么办?
- TCP设有一个保活计时器
- 服务器每收到一次客户端的请求后都会重新复位这个计时器,时间通常是设置为2小时
- 若两小时还没有收到客户端的任何数据,服务器就会发送一个探测报文段,以后每隔75秒钟发送一次
- 若一连发送10个探测报文仍然没反应,服务器就认为客户端出了故障,接着就关闭连接。
7、同一进程中的线程究竟共享哪些资源
一般的评价标准是:如果某些资源不独享会导致线程运行错误,则该资源就由每个线程独享,而其他资源都由进程里面的所有线程共享。
线程共享资源 | 线程独享资源 |
---|---|
地址空间 | 程序计数器 |
全局变量 | 寄存器 |
打开的文件 | 栈 |
子进程 | 状态字 |
闹铃 | 线程ID |
信号及信号服务程序 | 线程优先级 |
8、进程间的通信
进程间通信(Inter-Process Communication,IPC)是指在操作系统中,不同进程之间交换信息和数据的过程,常见的进程通信方式包括:
- 管道:用于单向或双向通信
- 消息队列:允许进程通过信息传递进行通信
- 共享内存:多个进程可以访问同一块内存区域
- 信号量:用于进程间的同步
- 套接字:用于网络通信
线程间通信与进程间通信类似,但由于线程之间共享进程的资源,线程间通信通常比进程间通信更高效
- 应用场景:现代 JavaScript 引擎(如 V8 引擎)通常使用 JIT 编译来提高性能,包括 Java 虚拟机(JVM)也会使用 JIT 编译来提高性能
9、进程与线程区别⭐
进程是操作系统中一个正在运行的程序,每个进程都有自己的地址空间、内存、文件描述符等资源。
线程是进程中的一个执行单元,是 cpu 调度的最小单元,共享进程的资源,但有自己的独立执行流。
二者的区别
一个程序至少有一个进程,一个进程至少有一个线程.
线程的划分尺度小于进程,使得多线程程序的并发性高。
进程在执行过程中拥有独立的内存单元,而多个线程共享内存,从而极大地提高了程序的运行效率。
线程在执行过程中与进程还是有区别的。每个独立的进程有一个程序运行的入口、顺序执行序列和程序的出口。但是线程不能够独立执行,必须依存在应用程序中,由应用程序提供多个线程执行控制。
从逻辑角度来看,多线程的意义在于一个应用程序中,有多个执行部分可以同时执行。但操作系统并没有将多个线程看做多个独立的应用,来实现进程的调度和管理以及资源分配。这就是进程和线程的重要区别。
举例
- 浏览器中一个 tab 就是一个进程,进程中某个线程崩了,整个进程就会崩,但是这不会影响其他的进程,因此进程之间相对独立
- Nginx 或 Nodejs PM2 开启的多个 instance 是进程,每个 instance 之间不能共享内存数据
- JS 中 WebWorker 是一个线程,它可以和主线程共享内存数据,独立运行不阻塞 UI
10、get和post的区别
副作用指对服务器上的资源做改变,搜索是无副作用的,注册是副作用的。
幂等指发送M和N次请求(两者不相同且都大于1),服务器上的资源的状态一致,比如注册10个和11个账号是不幂等的,对文章进行更改10次和11次是幂等的。
在规范的应用场景上说,Get多用于无副作用,幂等的场景,例如搜索关键字。Post多用于副作用,不幂等的场景,例如注册。
get参数通过url传递,一般用于信息获取,post放在request body中,一般用于修改服务器上的资源。
get请求在url中传递的参数是有长度限制的,一般在2000个字符,而post没有。
get比post更不安全,因为参数直接暴露在url中,所以不能用来传递敏感信息。
get请求只能进行url编码,而post支持多种编码方式。
get请求会被浏览器主动cache,而post不会,除非手动设置。
get请求参数会被完整保留在浏览历史记录里,而post中的参数不会被保留。
get产生一个TCP数据包;post产生两个TCP数据包。
get在浏览器回退时是无害的,而post会再次提交请求。
对参数的数据类型,get只接受ASCII字符,而post没有限制。
get和post本质上就是TCP链接,并无差别。但是由于HTTP的规定和浏览器/服务器的限制,导致他们在应用过程中体现出一些不同。
get方式需要使用Request.QueryString来取得变量的值,而post方式通过Request.Form来获取变量的值,也就是说get是通过地址栏来传值,而post是通过提交表单来传值。
在以下情况中,请使用 POST 请求:
无法使用缓存文件(更新服务器上的文件或数据库)
向服务器发送大量数据(POST 没有数据量限制)
发送包含未知字符的用户输入时,POST 比 GET 更稳定也更可靠
11、cookie和session的区别
Cookie和session都可用来存储用户信息
cookie存放于客户端,session存放于服务器端
因为cookie存放于客户端有可能被窃取,所以cookie一般用来存放不敏感的信息,比如用户设置的网站主题,敏感的信息用session存储,比如用户的登陆信息,session可以存放于文件,数据库,内存中都可以
cookie可以服务器端响应的时候设置,也可以客户端通过JS设置
cookie会在请求时在http首部发送给客户端
cookie一般在客户端有大小限制,一般为4K
12、Cookie、localStorage和sessionStorage的区别和应用场景⭐
共同点:都是保存在浏览器端,并且是同源的
特性 | Cookie | LocalStorage | SessionStorage |
---|---|---|---|
写入方式 | 服务端和前端都可写入,不过http-only情况下只允许服务端写入 | 前端 | 前端 |
存储大小 | 4KB 左右 | 5~10MB | 5~10MB |
生命周期 | 手动设置,一般由服务器生成,可设置失效时间。如果在浏览器端生成cookie,默认关闭浏览器失效 | 长期保留,只能通过手动删除缓存来清除,不能设置失效时间 | 尽在当前会话下有小,关闭页面或浏览器后被清除 |
与服务器端通信 | 每次都会携带在http头中,如果使用cookie保存过多数据会带来性能问题 | 仅在客户端(浏览器)中保存,不参与和服务器的通信 | 仅在客户端(浏览器)中保存,不参与和服务器的通信 |
数据共享 | 同域下所有页面共享 | 同域下所有页面共享 | 当前页面及子页面共享 |
易用性 | 需要自己封装,源生的cookie接口不友好 | 源生接口可以接受,亦可再次封装对Object和Array有更好的支持 | 源生接口可以接受,亦可再次封装对Object和Array有更好的支持 |
应用场景:
- Cookie :小数据量、需与服务器交互的场景,如保存会话标识(如
token
)。 - LocalStorage :需持久化存储、跨页面共享的数据,如用户设置、主题偏好。
- SessionStorage :页面刷新或跳转时临时保存的数据,如表单填写进度。
手写设置cookie
1.设置cookie一天后过期
function setCookie(name,expireday){
var dayobject = new Date(); // Date()函数获取当前的日期和时间
// getTime()函数获取的事1970年1月1号至今的毫秒数
// 注意要多加8小时,我们位于东八区比标准时间相差8小时
var daynum = dayobject.getTime() + expireday*(24+8)*60*60*1000;
// 计算过期时间毫秒数
dayobject.setTime(daynum);
// 设置超时的时间
alert('name=' + name + ';' + 'expires=' + dayobject.toUTCString());
document.cookie = 'name=' + name + ';' + 'expires=' + dayobject.toUTCString();
}
setCookie('coco',1)
2.设置cookie马上过期
function delCookie(name){
var expires = new Date();
expires.setTime(expires.getTime() - 1);
document.cookie = 'name='+name+';'+'expires=' + expires.toGMTString();
}
// 设置cookie的过期时间是比当前时间提前一秒,也就是立马过期了。
设置localStorage/sessionStorage
localStorage.setItem(key, value);
localStorage.getItem(key);
sessionStorage.setItem(key, value);
sessionStorage.setItem(key, value);
cookie的使用场景⭐
**保存用户登录状态。**例如将用户id存储于一个cookie内,这样当用户下次访问该页面时就不需要重新登录了,现在很多论坛和社区都提供这样的功能。 cookie还可以设置过期时间,当超过时间期限后,cookie就会自动消失。因此,系统往往可以提示用户保持登录状态的时间:常见选项有一个月、三个月、一年等。
**跟踪用户行为。**例如一个天气预报网站,能够根据用户选择的地区显示当地的天气情况。如果每次都需要选择所在地是烦琐的,当利用了cookie后就会显得很人性化了,系统能够记住上一次访问的地区,当下次再打开该页面时,它就会自动显示上次用户所在地区的天气情况。因为一切都是在后台完成,所以这样的页面就像为某个用户所定制的一样,使用起来非常方便定制页面。如果网站提供了换肤或更换布局的功能,那么可以使用cookie来记录用户的选项,例如:背景色、分辨率等。当用户下次访问时,仍然可以保存上一次访问的界面风格。
个性化设置(用户偏好、主题等)
13、强缓存和协商缓存⭐
强缓存
- 不需要向服务器发送请求,直接使用本地缓存(这个“本地”一般就是来源于硬盘。这也就是我们在 Chrome DevTools 上经常看到的「disk cache」)
- 通过 HTTP 响应头控制:
- Cache-Control:
- max-age:缓存有效时间(秒)
- no-cache:需要和服务器协商验证
- no-store:不使用任何缓存
- private:仅浏览器可缓存
- public:中间代理/CDN 等也可缓存
- Expires:过期时间点(已被 Cache-Control 取代)
- Cache-Control:
如果同时出现Cache-Control:max-age
和Expires
,那么max-age
优先级更高。
协商缓存
需要向服务器发送请求验证资源是否有效
如果有效返回 304,使用本地缓存
通过以下响应头实现:
Last-Modified/If-Modified-Since:基于文件修改时间
服务器第一次响应时返回
Last-Modified
,而浏览器在后续请求时带上其值作为If-Modified-Since
,相当于问服务端:XX 时间点之后,这个资源更新了么?服务器根据实际情况回答即可:更新了(状态码 200)或没更新(状态码 304)。ETag/If-None-Match:基于文件内容哈希值
服务器第一次响应时返回
ETag
,而浏览器在后续请求时带上其值作为If-None-Match
,服务器通过该值来判断是否命中缓存。Last-Modified与ETag是可以一起使用的,服务器会优先验证ETag,一致的情况下,才会继续比对Last-Modified,最后才决定是否返回304。
缓存位置(优先级从高到低):
- Service Worker
- Memory Cache(内存缓存)
- Disk Cache(硬盘缓存)
- Push Cache(HTTP/2)
最佳实践:
- HTML:使用协商缓存
- CSS、JS、图片:使用强缓存,文件名带 hash
- API 请求:根据业务需求设置合适的缓存策略
实际应用场景:
强缓存应用:
静态资源版本控制:例如给文件名加hash(如app.a3f4.js),设置Cache-Control: max-age=31536000(一年)。这样在文件名不变时直接从本地缓存读取,文件名变化则请求新资源。
单页应用(SPA)的入口文件(index.html)通常设置Cache-Control: no-cache或max-age=0,避免强缓存,确保用户能获取最新版本。但其他静态资源(JS、CSS、图片)使用强缓存。
协商缓存应用:
频繁更新的资源:比如用户头像,虽然URL不变,但内容可能更新。使用ETag,每次请求携带If-None-Match,若未变化则返回304。
新闻网站的文章内容:文章内容可能更新,但URL固定。使用Last-Modified,当用户重新访问时,通过If-Modified-Since验证是否更新。
14、http和https
http和https的基本概念
http: 超文本传输协议,是互联网上应用最为广泛的一种网络协议。
https:是以安全为目标的HTTP通道,简单讲是HTTP的安全版,即HTTP下加入SSL层,HTTPS的安全基础是SSL,因此加密的详细内容就需要SSL。
https协议的主要作用是:建立一个信息安全通道,来确保数据的传输,确保网站的真实性。
http和https的区别⭐
HTTP 和 HTTPS 的核心区别在于安全性:
- HTTP(超文本传输协议):
- 数据以明文形式传输(未加密)。
- 不安全:容易被窃听(如窃取密码、信用卡号)、篡改(如插入广告或恶意代码)或冒充(如钓鱼网站)。
- 默认端口:80。
- HTTPS(安全超文本传输协议):
- 在 HTTP 基础上增加了 SSL/TLS 加密层。
- 数据在传输前进行加密,确保机密性和完整性(防止被窃听和篡改)。
- 提供身份认证(通过数字证书验证网站身份),防止冒充。
- 默认端口:443。
- 只要涉及敏感信息(如登录、支付),务必使用 HTTPS。现代网站普遍推荐甚至强制使用 HTTPS。
https协议的优点
- 使用HTTPS协议可认证用户和服务器,确保数据发送到正确的客户机和服务器;
- HTTPS协议是由SSL+HTTP协议构建的可进行加密传输、身份认证的网络协议,要比http协议安全,可防止数据在传输过程中被窃取、改变,确保数据的完整性。
https协议的缺点
- https握手阶段比较费时,会使页面加载时间延长50%,增加10%~20%的耗电。
- https缓存不如http高效,会增加数据开销。
- SSL证书也需要钱,功能越强大的证书费用越高。
- SSL证书需要绑定IP,不能再同一个ip上绑定多个域名,ipv4资源支持不了这种消耗。
15、简述 HTTPS 加密过程
HTTPS 使用 TLS/SSL 协议进行加密,主要包含以下步骤:
客户端发起请求
- 发送支持的加密算法列表
- 发送随机数 Client Random
服务器回应
- 选择加密算法
- 发送数字证书(包含公钥)
- 发送随机数 Server Random
客户端验证证书
- 验证证书是否由可信 CA 签发
- 验证证书域名是否匹配
- 验证证书是否在有效期内
生成会话密钥
- 客户端生成随机数 Pre-master secret
- 使用服务器公钥加密 Pre-master secret
- 客户端和服务器都通过三个随机数生成会话密钥 (Client Random + Server Random + Pre-master secret)
开始加密通信
- 双方使用会话密钥进行对称加密通信
- 保证通信内容的机密性和完整性
特点:
- 采用混合加密:非对称加密传输密钥,对称加密传输数据
- 数字证书保证服务器身份可信
- 具有防篡改和不可否认性
16、http状态码⭐
100 Continue 继续,一般在发送post请求时,已发送了http header之后服务端将返回此信息,表示确认,之后发送具体参数信息
200 OK 请求成功,正常返回信息
201 Created 请求成功并且服务器创建了新的资源
202 Accepted 服务器已接受请求,但尚未处理
204 No Content 请求处理成功,但没有资源可返回
206 Partial Content 对资源某一部分的请求
301 Moved Permanently 永久重定向,请求的网页已永久移动到新位置。
比较常用的场景是使用域名跳转。
比如,我们访问 http://www.baidu.com 会跳转到 https://www.baidu.com,发送请求之后,就会返回301状态码,然后返回一个location,提示新的地址,浏览器就会拿着这个新的地址去访问。
或者你把你的网页的名称从php修改为了html,这个过程中,也会发生永久重定向。
注意: 301请求是可以缓存的, 即通过看status code,可以发现后面写着from cache。
302 Found 临时性重定向,表示资源临时被分配到了新的URL。
比如未登陆的用户访问用户中心重定向到登录页面。
访问404页面会重新定向到首页。
303 See Other 临时性重定向,且总是使用 GET 请求新的 URL。
304 Not Modified 请求的资源没有修改,服务端不会返回任何资源,协商缓存
**400 Bad Request 请求无效,请求报文存在语法错误。**服务器无法理解请求的格式,客户端不应当尝试再次使用相同的内容发起请求。 请求无效
401 Unauthorized 身份认证失败 当前请求需要用户验证 请求未授权,比如 token 认证失败
403 Forbidden 服务器已经得到请求,但是拒绝执行。一般是因为传递的参数不符合后台的要求,而被拒绝访问
404 Not Found 资源请求失败
408 Request timeout 请求超时
500 Internal Server Error 服务器发生不可预知的错误
501 Not Implemented 服务未实现
502 bad gateway 网关错误
503 Service Unavailable 服务器宕机或过载
504 GatewayTime-out 网关超时
505 HTTP Version Not Supported HTTP版本不受支持
总结一下
1xx 临时响应,需要请求者继续执行操作
2xx 成功处理请求
3xx 重定向,需要进行附加操作以完成请求
4xx 客户端请求错误
5xx 服务器端错误
17、HTTP支持的方法
GET, POST, HEAD, OPTIONS, PUT, DELETE, TRACE, CONNECT
18、常见的HTTP的头部
可以将http首部分为通用首部,请求首部,响应首部,实体首部
通用首部表示一些通用信息,比如date表示报文创建时间
请求首部就是请求报文中独有的,如cookie,和缓存相关的如if-Modified-Since
响应首部就是响应报文中独有的,如set-cookie,和重定向相关的location
实体首部用来描述实体部分,如allow用来描述可执行的请求方法,content-type描述主题类型,content-Encoding描述主体的编码方式
Request Header:
- GET /sample.Jsp HTTP/1.1 //请求行
- Host: www.uuid.online/ //请求的目标域名和端口号
- Origin: http://localhost:8081/ //请求的来源域名和端口号 (跨域请求时,浏览器会自动带上这个头信息)
- Referer: https:/localhost:8081/link?query=xxxxx //请求资源的完整URI
- User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36 //浏览器信息
- Cookie: BAIDUID=FA89F036:FG=1; BD_HOME=1; sugstore=0 //当前域名下的Cookie
- Accept: text/html,image/apng //代表客户端希望接受的数据类型是html或者是png图片类型
- Accept-Encoding: gzip, deflate //代表客户端能支持gzip和deflate格式的压缩
- Accept-Language: zh-CN,zh;q=0.9 //代表客户端可以支持语言zh-CN或者zh(值得一提的是q(0~1)是优先级权重的意思,不写默认为1,这里zh-CN是1,zh是0.9)
- Connection: keep-alive //告诉服务器,客户端需要的tcp连接是一个长连接
Response Header:
- HTTP/1.1 200 OK // 响应状态行
- Date: Mon, 30 Jul 2018 02 : 50 : 55 GMT //服务端发送资源时的服务器时间
- Expires: Wed, 31 Dec 1969 23 : 59 : 59 GMT //比较过时的一种验证缓存的方式,与浏览器(客户端)的时间比较,超过这个时间就不用缓存(不和服务器进行验证),适合版本比较稳定的网页
- Cache-Control: no-cache // 现在最多使用的控制缓存的方式,会和服务器进行缓存验证,具体见博文”Cache-Control“
- etag: "fb8ba2f80b1d324bb997cbe188f28187-ssl-df" // 一般是Nginx静态服务器发来的静态文件签名,浏览在没有“Disabled cache”情况下,接收到etag后,同一个url第二次请求就会自动带上“If-None-Match”
- Last-Modified: Fri, 27 Jul 2018 11 : 04 : 55 GMT //是服务器发来的当前资源最后一次修改的时间,下次请求时,如果服务器上当前资源的修改时间大于这个时间,就返回新的资源内容
- Content-Type: text/html; charset=utf-8 //如果返回是流式的数据,我们就必须告诉浏览器这个头,不然浏览器会下载这个页面,同时告诉浏览器是utf8编码,否则可能出现乱码
- Content-Encoding: gzip //告诉客户端,应该采用gzip对资源进行解码
- Connection: keep-alive //告诉客户端服务器的tcp连接也是一个长连接
19、Cookie如何防范XSS攻击
XSS(跨站脚本攻击)是指攻击者在返回的HTML中嵌入javascript脚本,为了减轻这些攻击,需要在HTTP的头部配上:Set-Cookie=<cookie-value>
20、CSRF和XSS的网络攻击及防范⭐
CSRF / XSRF(Cross Site Request Forgery):跨站请求伪造。
攻击者盗用了用户的身份,以用户的名义发送恶意请求。
比如用户登录了一个网站后,立刻在另一个tab页面访问攻击者用来制造攻击的网站,这个网站要求访问刚刚登陆的网站,并发送了一个恶意请求,这时候CSRF就产生了,比如这个制造攻击的网站使用一张图片,但是这种图片的链接却是可以修改数据库的,这时候攻击者就可以以用户的名义操作这个数据库。
预防:
- 验证 HTTP Referer 字段
- 在请求地址中添加 token 并验证
- 在 HTTP 头中自定义属性并验证
- 增加验证流程,如输入密码,指纹,短信验证码
XSS(Cross Site Scripting):跨站脚本攻击
其特点是不对服务器端造成任何伤害,而是通过一些正常的站内交互途径,例如发布评论,提交含有JavaScript的内容文本。这是服务器端如果没有过滤掉或转移掉这些脚本,作为内容发布到了页面上,其他用户访问这个页面的时候就会运行这些脚本。
大多数XSS攻击的主要方式是嵌入一段远程或者第三方域上的JS代码。攻击者通过注入恶意的脚本,在用户浏览网页的时候进行攻击,比如获取cookie,或者其他用户身份信息,可以分为存储型和反射型,存储型是攻击者输入一些数据并且存储到了数据库中,其他浏览者看到的时候进行攻击,反射型的话不存储在数据库中,往往表现为将攻击代码放在url地址的请求参数中。
预防:
在HTTP头部配上,
Set-Cookie=<cookie-value>
- httponly-这个属性可以防止XSS,它会禁止javascript脚本来访问cookie。
- secure - 这个属性告诉浏览器仅在请求为https的时候发送cookie。
转义输入输出的内容,对于引号,尖括号,斜杠进行转义。例如替换
<
为<
,>
为>
CSP
CSP(内容安全策略)是一个额外的安全层,用于检测并削弱某些特定类型的攻击,包括跨站脚本(XSS)和数据注入攻击等。
CSP本质上是建立白名单,规定了浏览器只能够执行特定来源的代码。
通常可以通过HTTP Header中的Content-Security-Policy来开启CSP
只允许加载本站资源
Content-Security-Policy: default-src 'self'
只允许加载HTTPS协议图片
Content-Security-Policy: img-src https://*
允许加载任何来源框架
Content-Security-Policy: child-src 'none'
21、URL 包含哪些部分?
URL (Uniform Resource Locator) 包含以下部分:
- 协议 (protocol):如
http://
、https://
、ftp://
等 - 域名 (domain):如
www.example.com
- 子域名:
www
- 主域名:
example
- 顶级域名:
com
- 子域名:
- 端口号 (port):如
:80
、:443
(可选,HTTP 默认 80,HTTPS 默认 443) - 路径 (path):如
/blog/article
- 查询参数 (query string):如
?id=123&name=test
- 锚点/片段标识符 (fragment):如
#header
示例:https://www.example.com:80/blog/article?id=123&name=test#header
22、DNS的寻址过程(域名解析过程)⭐
DNS寻址过程通常分为以下几个步骤:
- 用户输入域名: 用户在浏览器地址栏中输入一个网址,如
www.example.com
。 - 浏览器缓存查询: 浏览器会首先检查自身缓存中是否有对应域名的IP地址。如果有缓存并且未过期,浏览器会直接使用这个IP地址进行连接。
- 操作系统缓存查询: 如果浏览器缓存中没有找到,浏览器会向操作系统查询是否缓存有该域名的IP地址。
- 本地DNS服务器查询: 如果操作系统也没有缓存,操作系统会向配置的本地DNS服务器(通常由ISP提供)发送请求。
- 递归查询: 如果本地DNS服务器没有对应的缓存记录,它会以递归方式向上级DNS服务器请求。这个递归查询过程通常会经过以下几步:
- 根DNS服务器查询:本地DNS服务器首先查询根DNS服务器,根服务器会返回负责顶级域(如
.com
)的顶级域名服务器(TLD DNS)的地址。 - 顶级域名服务器查询:本地DNS服务器接着向TLD DNS查询,这些服务器会返回负责该域名的权威DNS服务器的地址。
- 权威DNS服务器查询:本地DNS服务器最终向权威DNS服务器发送请求,权威DNS服务器返回最终的IP地址。
- 根DNS服务器查询:本地DNS服务器首先查询根DNS服务器,根服务器会返回负责顶级域(如
- 缓存结果: 本地DNS服务器获取到IP地址后会将结果缓存,并返回给操作系统。操作系统再将结果返回给浏览器。
- 建立连接: 浏览器获得IP地址后,通过该IP地址与目标服务器建立连接,进行数据传输。
这个DNS寻址过程通常在几毫秒内完成,确保用户能够快速访问网站。
23、从输入url到得到html的详细过程⭐
1. 输入URL并解析
- 用户在浏览器中输入URL,例如
https://www.example.com
。 - 浏览器解析URL,分解出协议(
https
)、域名(www.example.com
)、路径(如/index.html
)和查询参数。
2. DNS解析
- 浏览器检查本地缓存和操作系统缓存,尝试找到域名对应的IP地址。
- 如果没有命中缓存,浏览器会向配置的本地DNS服务器发起DNS查询。
- DNS服务器通过递归查询(从根DNS服务器到顶级域名服务器再到权威DNS服务器)获取域名对应的IP地址。
- 获取到IP地址后,DNS服务器将其返回给浏览器。
3. 建立TCP连接
- 浏览器使用获取到的IP地址,通过三次握手过程与服务器建立TCP连接。
- 1、客户端发起请求(SYN)连接服务器端。
- 2、服务器端接受请求,然后发送确认和请求(SYN+ACK)给客户端。
- 3、客户端接受请求,向服务器端发送确认连接(ACK),客户端和服务器端连接成功,完成三次握手。
4. TLS握手(如果使用HTTPS)
如果使用的是https
协议,还需要进行TLS握手来建立一个加密的通信通道。TLS握手步骤包括:
- 客户端Hello:客户端发送支持的加密算法和随机数。
- 服务器Hello:服务器选择加密算法,返回证书和随机数。
- 密钥生成:双方生成对称密钥并交换加密信息。
- 握手完成:加密通信通道建立,开始传输数据。
5. 发送HTTP请求
- 连接建立后,浏览器发送HTTP请求给服务器。请求通常包含以下部分:
- 请求行:包括请求方法(GET、POST等)、资源路径、使用的HTTP版本。
- 请求头:可能包括User-Agent、Accept-Language、Cookie等信息。
- 请求体:如果是POST请求,可能会有表单数据或其他类型的数据。
6. 服务器处理请求并响应
- 查找请求的资源(如HTML文件、动态生成页面等)。
- 生成HTTP响应,通常包括状态行(如
HTTP/1.1 200 OK
)、响应头(如Content-Type: text/html
)和响应体(即HTML内容)。
7. 浏览器接收响应
浏览器接收到HTTP响应后,开始解析HTML文档。
8.HTML解析与渲染
- 浏览器解析HTML内容并开始渲染页面,具体过程包括:
- 构建DOM树:将HTML标签解析成DOM(文档对象模型)树。
- 构建CSSOM树:解析CSS并构建CSSOM(CSS对象模型)树。
- 生成渲染树:结合DOM树和CSSOM树生成渲染树,用于实际显示内容。
- 布局与绘制:计算各个元素的布局位置,并将其绘制到屏幕上。
9.处理外部资源
在HTML解析过程中,如果遇到外部资源(如CSS、JavaScript、图像等),浏览器会发送额外的HTTP请求获取这些资源,并继续解析与渲染。
10. JavaScript执行
当遇到 JavaScript 后会阻塞 DOM 的解析
浏览器在解析HTML时会遇到JavaScript代码或外部脚本。这些脚本会被下载并执行,可以操作DOM、修改页面内容或发起额外的网络请求(如AJAX)。
11.页面呈现
当所有资源加载完成后,浏览器最终完成页面的渲染,用户可以看到完整的网页内容。
24、什么是token验证
在客户端频繁向服务端请求数据,服务端频繁的去数据库查询用户名和密码并进行对比,判断用户名和密码正确与否,并作出相应提示,在这样的背景下,Token便应运而生。
Token是服务端生成的一串字符串,以作客户端进行请求的一个令牌,当第一次登录后,服务器生成一个Token便将此Token返回给客户端,以后客户端只需带上这个Token前来请求数据即可,无需再次带上用户名和密码。
下面部分转载深入理解token
可以解决哪些问题呢?
- Token 完全由应用管理,所以它可以避开同源策略
- Token 可以避免 CSRF 攻击
- Token 可以是无状态的,可以在多个服务间共享
Token 是在服务端产生的。如果前端使用用户名/密码向服务端请求认证,服务端认证成功,那么在服务端会返回 Token 给前端。前端可以在每次请求的时候带上 Token 证明自己的合法地位。如果这个 Token 在服务端持久化(比如存入数据库),那它就是一个永久的身份令牌。
25、http预请求options
这是浏览器对复杂跨域请求的一种处理方式,在真正发送请求之前,会先进行一次预请求,就是我们刚刚说到的参数为OPTIONS的第一次请求,他的作用是用于试探服务器响应是否正确,即是否能接受真正的请求,如果在options请求之后获取到的响应是拒绝性质的,例如500等http状态,那么它就会停止第二次的真正请求的访问。
有三种方式会导致这种现象:
1、请求的方法不是GET/HEAD/POST
2、POST请求的Content-Type并非application/x-www-form-urlencoded, multipart/form-data, 或text/plain
3、请求设置了自定义的header字段
options 请求会携带 cookie 吗
OPTIONS 请求通常不会携带 Cookie。它是一个预检请求,用于检查实际请求是否可以安全地发送。浏览器在发送 OPTIONS 请求时,不会自动附带 Cookie 和 Authorization 等认证信息,除非明确设置了 credentials
选项。
如果需要在 OPTIONS 请求中携带 Cookie,可以在请求中设置 credentials: 'include'
,但通常不推荐这样做,因为 OPTIONS 请求的目的就是检查跨域请求的安全性,而不是进行身份验证。
26、HTTP 几个版本的区别⭐
HTTP/0.9 - 单行协议
- 只有 GET 请求行,无请求头和请求体
- 只能传输 HTML 文件,以 ASCII 字符流返回
- 无响应头
HTTP/1.0 - 多类型支持
- 支持多种文件类型传输,不限于 ASCII 编码
- 引入请求头和响应头( key-value 形式)
- 每个请求都需要建立新的 TCP 连接
HTTP/1.1 - 持久连接
- 引入持久连接( keep-alive ):一个 TCP 连接可传输多个 HTTP 请求
- 默认开启 keep-alive,通常限制 6-8 个并发连接
- 存在队头阻塞问题:前面的请求阻塞会影响后续请求
- 引入 Host 字段,支持虚拟主机
- 引入 Chunk transfer 机制处理动态内容长度
HTTP/2.0 - 多路复用
- 多路复用(Multiplexing):单连接上并行传输多个请求/响应,彻底解决队头阻塞。
- 一个域名只使用一个 TCP 长连接
- 头部压缩(HPACK):减少冗余数据。
- 可对请求设置优先级
- 引入 HTTPS(HTTP + TLS) 加密
HTTP/3.0 - QUIC 协议
它构建在QUIC协议上,QUIC(Quick UDP Internet Connections)是一个基于UDP的新传输层协议。
- 革命性变化:弃用 TCP,改用 QUIC 协议(基于 UDP 开发)。
- 使用UDP协议:与HTTP/1.x 和 HTTP/2 使用的TCP协议不同,HTTP/3 使用了UDP协议。这允许QUIC协议处理重传和流控制,从而减少了连接建立的延迟,并且可以更有效地绕过某些网络拥塞情况。
- 解决 TCP 队头阻塞:QUIC 在应用层实现多路复用,丢包仅影响单个流。
- 快速重连:由于QUIC是在应用层实现的,它可以快速地重新建立连接而不需要三次握手,这样可以在短时间内恢复连接。
- 加密连接建立:所有的QUIC连接都默认加密,这有助于防止中间人攻击,并且在客户端和服务器之间提供了更安全的数据交换。
- 降低延迟:QUIC的设计目标之一就是降低延迟,通过避免TCP的一些限制,如头部阻塞(head-of-line blocking),HTTP/3能够更快地传输数据。
- 可迁移性:QUIC支持无缝迁移,这意味着如果客户端改变了网络或IP地址,连接仍然可以继续,而不会像TCP那样断开。
总结对比表
特性 | HTTP/1.1 | HTTP/2 | HTTP/3 |
---|---|---|---|
传输协议 | TCP | TCP | QUIC (UDP) |
多路复用 | ❌ 队头阻塞严重 | ✅ 解决应用层队头阻塞 | ✅ 解决传输层队头阻塞 |
头部压缩 | ❌ | ✅ (HPACK) | ✅ (QPACK) |
服务器推送 | ❌ | ✅ | ✅ |
连接建立速度 | 慢(多次握手) | 需 TLS 握手 | ⚡ 0-RTT/1-RTT |
抗丢包能力 | 弱 | 弱(TCP 层阻塞) | 强(QUIC 流隔离) |
适用场景 | 兼容旧系统 | 主流高性能网站 | 高延迟/移动网络 |
27、http请求头中Referer的含义和作用
Referer是 HTTP 请求 header 的一部分,当浏览器(或者模拟浏览器行为)向 web 服务器发送请求的时候,头信息里有包含 Referer。
比如我在www.google.com
里有一个www.baidu.com
链接,那么点击这个www.baidu.com
,它的header
信息里就有:Referer=http://www.google.com
Referer的作用
1、防盗链
将这个http请求发给服务器后,如果服务器要求必须是某个地址或者某几个地址才能访问,而你发送的referer不符合他的要求,就会拦截或者跳转到他要求的地址,然后再通过这个地址进行访问。
2、防止恶意请求
比如静态请求是*.html
结尾的,动态请求是*.shtml
,那么由此可以这么用,所有的*.shtml
请求,必须Referer为我自己的网站。
28、什么是图片防盗链,如何实现?
图片防盗链是指服务器通过 HTTP 协议中的 Referer 字段来判断请求是否来自合法站点,从而防止其他网站直接引用本站图片资源。
实现方式:
服务器端实现
- 检查 HTTP Referer 字段
- 判断请求来源是否在白名单中
- 对非法请求返回 403 或替代图片
Nginx 配置示例:
location ~ .*\.(gif|jpg|jpeg|png|bmp)$ {
valid_referers none blocked server_names *.example.com;
if ($invalid_referer) {
return 403;
# 或者返回替代图片
# rewrite ^/ /path/to/default.jpg break;
}
}
其他防盗链方案:
- 给图片添加水印
- 使用 Token 验证
- 使用 CDN 提供的防盗链功能
- 对图片进行加密处理
注意事项:
- Referer 可以被伪造,不能作为唯一判断依据
- 移动端 APP 可能不发送 Referer
- 部分浏览器可能禁用 Referer
- 需要考虑用户体验和 SEO 影响
29、单核 CPU 如何实现并发
单核CPU 主要是通过时间片轮转和上下文切换来实现并发
时间片轮转
- CPU将时间划分为很小的时间片,通常是几十毫秒
- 每个进程、线程分配到一个时间片
- CPU轮流执行每个进程、线程的时间片
- 当一个时间片用完,CPU就会切换到下一个进程、线程
上下文切换
- 在切换进程、线程时,CPU需要保存当前进程的状态(上下文),包括
- 程序计数器的值
- 寄存器的值
- 内存映射信息
- 加载下一个要执行的进程、线程的上下文
在任意时刻,CPU只能执行一个任务,由于切换速度非常快,给用户的感觉就像是在同时运行多个程序,所以这种机制被称为“伪并发”,若线程过多也不好,频繁的上下文切换会带来一定的性能开销,所以过多的线程反而会带来性能下降的问题
30、CPU 调度算法有哪些?
- 先来先服务(First Come First Serve)
- 最简单的调度算法
- 进程按到达的顺序排队,先到达的先执行
- 缺点:可能导致长时间的等待,特别是当一个长进程在队列前面时
- 短作业优先(Shortest Job First)
- 优先执行预计运行时间最短的进程
- 可以是非抢占式或是抢占式(shortest Remaining Time First,SRTF)
- 可能导致“饥饿”现象,即长作业可能永远得不到执行
- 优先级调度(Priority Scheduling)
- 每个进程分配一个优先级,优先级高的进程先执行
- 也可能导致“饥饿”现象,通常使用老化(aging)技术来解决
- 轮转调度(Round Robin)
- 每个进程分配一个固定的时间片,时间片用完后,进程被放到队列的末尾
- 适用于时间共享系统
- 时间片的大小对系统性能有很大影响
- 多级队列调度(Multilevel Queue Scheduling)
- 将进程分成多个队列,每个队列有不同的优先级
- 不同队列可以使用不同的调度算法
- 多级反馈队列调度(Multilevel Feedback Queue)
- 允许进程在不同的队列之间移动
- 根据进程的行为动态调整其优先级
31、单核服务器连接数超载了怎么办
优化代码和查询
确保应用程序代码和数据库查询是高效的,以减少每个连接的资源消耗
使用负载均衡
将流量分配到多个服务器上,以分散负载
增加连接池
使用连接池来管理数据库连接,减少连接的创建和销毁开销
限制连接数
配置服务器以限制每个客户端的最大连接数,以防止单个客户端占用过多资源
使用缓存
利用缓存机制(如 Redis,Memcached)来减少对数据库的访问次数
32、请简述一个编译器的执行过程。前端有哪些常见的编译工具?
编译器的执行过程
词法分析
- 将源代码转换为一系列的标记(tokens),这些标记是编程语言的基本语法单位
语法分析
- 根据语言的语法规则,将标记序列转换为语法书(parse tree),也称为抽象语法树(AST)
语义分析
- 检查语法树是否符合语言的语义规则,例如类型检查,作用域检查等
中间代码生成
- 将语法树转换为中间代码,这种代码通常独立于机器
代码优化
- 对中间代码进行优化,以提高程序的执行效率
目标代码生成
- 将中间代码转换为目标机器代码
代码生成后优化 - 对生成的目标代码进行进一步优化
编译器的目的是将我们编写的源码转换为机器码(目标代码),以便计算机能够读懂执行
前端常见的编译工具
- Babel
- 用于将现代 JavaScript 代码转换为向后兼容的版本
- TypeScript Compiler
- 将 TypeScript 代码转换为 JavaScript
- Sass/SCSS
- 将 Sass/SCSS 代码转换为 CSS
- Webpack
- 用于打包 JavaScript 模块,并支持多种编译和转换插件
33、什么是编译型语言和解释型语言,他们有什么区别?
高级编程语言分为解释型语言和编译型语言
编译型语言
- 常见编译型语言:C,C++,Java,Go,Rust
- 执行方式:编译型语言的代码在运行期间由编译器一次性翻译成机器码,生成的机器码可以在目标机器上运行
- 优点:代码在运行前就已经被翻译成机器码,运行速度通常更快
- 缺点:需要编译步骤,开发和调试过程可能较慢
解释型语言
- 常见解释性语言:JavaScript,Python,Ruby,PHP
- 执行方式:解释型语言的代码在运行期间由解释器逐行翻译成机器码并执行。这意味着每次运行程序时,代码都需要被重新解释
- 优点:由于不需要编译成机器码,开发和调试过程通常更快,更灵活
- 缺点:运行速度通常比编译型语言慢,因为每次执行都需要进行翻译
编译型语言和解释型语言的区别
- 执行速度:编译型语言通常比解释型语言快,因为他们直接运行机器码
- 开发灵活性:解释型语言通常更灵活,适合快速开发和迭代
- 错误检测:编译型语言在编译阶就可以捕获更多的语法和类型错误,而解释型语言通常在运行时才发现错误
举个🌰:读一门外文著作,编译型语言就是给你将这本著作翻译成中文,然后你就可以直接看中文了。解释型语言就是给你一个翻译,一边看一边翻译,下次看仍需要翻译
JIT(Just-In-Time)编译
为了结合编译型和解释型语言的优点,JIT 随之诞生,可以理解为“即时编译”
- 执行方式:JIT 编译在程序运行时将部分代码编译成机器码,而不是逐行解释,这种编译方式在代码即将被执行时进行,因此得名“即时编译”
34、简述计算机网络的 OSI 模型
OSI(开放系统互联)模型是一个用于理解和实现网络协议的七层概念框架。每一层都有特定的功能,并与其直接上下的层进行通信。
- 物理层(Physical Layer):这是OSI模型的最低层,负责设备之间的物理连接,包括通过物理介质传输原始比特流。它涉及硬件组件,如电缆、交换机和网络接口卡。
- 数据链路层(Data Link Layer):负责节点到节点的数据传输以及错误检测和纠正,确保数据在物理链路上的可靠传输。它分为两个子层:媒体访问控制(MAC)层和逻辑链路控制(LLC)层。
- 网络层(Network Layer):负责数据的路由、转发和寻址,确定数据到达目的地的最佳物理路径。像IP(互联网协议)这样的协议在这一层运行。
- 传输层(Transport Layer):为应用程序提供端到端的通信服务,负责错误恢复、流量控制和确保完整的数据传输。像TCP(传输控制协议)和UDP(用户数据报协议)这样的协议在这一层运行。
- 会话层(Session Layer):管理应用程序之间的会话,建立、维护和终止应用程序之间的连接,负责会话的检查点和恢复。
- 表示层(Presentation Layer):负责数据的翻译、加密和压缩,确保数据以可用的格式呈现给应用层,充当网络和应用之间的翻译器。
- 应用层(Application Layer):这是OSI模型的最高层,直接为终端用户应用程序提供网络服务,负责电子邮件、文件传输和网页浏览等网络服务。像HTTP、FTP和SMTP这样的协议在这一层运行。
35、一个域名对应一个 ip 吗
一个域名不一定只对应一个 IP 地址,具体情况如下:
- 单个域名对应单个 IP 地址:这是最简单的情况,一个域名解析到一个固定的 IP 地址。
- 单个域名对应多个 IP 地址:这种情况通常用于负载均衡和高可用性。通过 DNS 轮询(Round Robin DNS),一个域名可以解析到多个 IP 地址,用户的请求会被分配到不同的服务器上。
- 多个域名对应单个 IP 地址:多个域名可以指向同一个 IP 地址,这在虚拟主机中很常见。通过服务器配置,服务器可以根据请求的域名来提供不同的内容。
- CDN(内容分发网络):CDN 服务会根据用户的地理位置将域名解析到不同的 IP 地址,以提高访问速度和可靠性。
因此,域名和 IP 地址之间的关系可以是多对多的,具体取决于网络架构和配置。
36、数组和链表有什么区别?从内存结构上来说
- 数组:
- 连续内存分配:数组在内存中是连续分配的,这意味着数组的所有元素在内存中是紧挨着的。
- 随机访问:由于数组的连续性,可以通过索引直接访问任意元素,访问速度快,时间复杂度为 O(1)。
- 固定大小:数组的大小在创建时就确定了,不能动态调整。
- 链表:
- 非连续内存分配:链表的每个元素(称为节点)在内存中可以是分散的,每个节点通过指针指向下一个节点。
- 顺序访问:访问链表中的元素需要从头节点开始,逐个遍历,访问速度较慢,时间复杂度为 O(n)。
- 动态大小:链表可以动态调整大小,方便插入和删除操作。
37、什么是 Restful API ?
RESTful API 是一种软件架构风格,用于设计网络应用程序的接口。主要特点:
资源导向
- 使用 URL 定位资源
- 每个资源都有唯一的 URL
- 资源可以有多种表现形式(如 JSON、XML)
HTTP 方法对应操作
- GET:获取资源
- POST:创建资源
- PUT:更新资源(完整更新)
- PATCH:更新资源(部分更新)
- DELETE:删除资源
无状态
- 服务器不保存客户端状态
- 每个请求包含所需的所有信息
- 有利于横向扩展
统一接口
- 使用标准的 HTTP 方法
- 使用标准的 HTTP 状态码
- 返回格式一致(通常是 JSON)
38、什么是 GraphQL ?
GraphQL 是一种用于 API 的查询语言和运行时,由 Facebook 开发。主要特点:
查询灵活性
- 客户端可以精确指定需要哪些数据
- 可以在一个请求中获取多个资源
- 避免了传统 REST API 的过度获取和获取不足问题
类型系统
- 强类型的 Schema 定义
- 自动生成文档
- 开发时有更好的类型提示
单个端点
- 只需要一个 API 端点
- 所有查询都发送到同一个地址
- 通过查询语句区分不同的操作
主要操作类型
- Query:获取数据
- Mutation:修改数据
- Subscription:实时数据订阅
优点
- 减少网络请求
- 避免版本化问题
- 强类型保障
- 更好的开发体验
缺点
- 学习成本较高
- 缓存较为复杂
- 服务端实现复杂度增加
39、什么是 JWT?描述它的工作过程
JWT (JSON Web Token) 是一种开放标准,用于在各方之间安全地传输信息。
组成部分(用 . 分隔的三部分):
- Header(头部):指定加密算法和令牌类型
- Payload(负载):包含声明(claims)的实际数据
- Signature(签名):对前两部分的签名,用于验证消息未被篡改
工作流程:
- 用户登录成功后,服务器创建 JWT
- 设置 Header 和 Payload
- 使用密钥生成签名
- 将三部分组合成 token
- 服务器将 token 返回给客户端
- 客户端存储在 localStorage 或 cookie 中
- 后续请求携带 token
- 通常放在 Authorization header
- 格式:
Bearer <token>
- 服务器验证 token
- 检查签名是否有效
- 验证是否过期
- 验证其他声明(claims)
特点:
- 无状态:服务器不需要存储会话信息
- 可扩展:负载部分可以包含自定义数据
- 跨域友好:可以在不同域名下使用
- 性能好:验证在服务端完成,不需要查询数据库
安全考虑:
- 不要在 payload 中存储敏感信息
- 设置合理的过期时间
- 使用 HTTPS 传输
- 妥善保管签名密钥
40、JWT 如何自动更新 token ?
JWT token 自动更新主要有以下几种方案:
双 token 机制
- access token:短期令牌,用于接口认证
- refresh token:长期令牌,用于刷新 access token
- 优点:安全性高,即使 access token 泄露影响有限
- 缺点:实现相对复杂,需要额外存储 refresh token
工作流程:
- 用户登录后获取 access token 和 refresh token
- 使用 access token 访问接口
- access token 过期时,使用 refresh token 获取新的 access token
- refresh token 过期时,需要重新登录
// 前端示例代码
async function request(url, options) {
try {
const res = await fetch(url, {
...options,
headers: {
Authorization: `Bearer ${getAccessToken()}`,
},
})
if (res.status === 401) {
// access token 过期,尝试刷新
const newToken = await refreshToken()
if (newToken) {
// 使用新 token 重试请求
return request(url, options)
} else {
// refresh token 也过期,跳转登录
redirectToLogin()
}
}
return res
} catch (error) {
console.error(error)
}
}
滑动过期机制
- 每次请求都刷新 token 过期时间
- 类似于会话超时机制
- 优点:实现简单,用户体验好
- 缺点:安全性相对较低
无感刷新机制
- 在 token 即将过期时自动刷新
- 可以通过定时器或请求拦截器实现
- 优点:用户无感知,体验好
- 缺点:需要处理并发请求的问题
最佳实践:
- 根据业务安全需求选择合适的方案
- access token 过期时间不宜过长(如 2 小时)
- refresh token 过期时间可以较长(如 7 天)
- 重要操作仍需要二次验证
- 考虑 token 注销机制
41、WebSocket 和 HTTP 协议有什么区别⭐
WebSocket 和 HTTP 的主要区别:
连接特性
- HTTP 是短连接:每次请求都需要建立新的 TCP 连接(除非使用 keep-alive)
- WebSocket 是持久化的长连接:只需要一次握手,后续可以持续通信
通信方式
- HTTP 是单向通信:客户端请求,服务器响应
- WebSocket 是双向通信:客户端和服务器都可以主动发送数据
数据格式
- HTTP 每次请求都要带完整的 HTTP 头
- WebSocket 第一次握手完成后,后续数据传输只需要很小的头部
应用场景
- HTTP 适合一次性的数据交互
- WebSocket 适合实时性要求高的场景,如:
- 实时聊天
- 游戏实时数据
- 实时协作文档
性能
- WebSocket 的性能和效率通常优于 HTTP 轮询
- WebSocket 可以更好地节省服务器资源和带宽
支持性
- HTTP 被所有浏览器支持
- WebSocket 需要浏览器支持(现代浏览器普遍已支持)
42、prefetch 和 dns-prefetch 分别是什么
prefetch 和 dns-prefetch 是两种不同的资源预加载技术:
prefetch
- 用于预加载将来可能需要的资源
- 浏览器空闲时才会下载
- 优先级较低,不影响当前页面加载
- 适用于下一页可能用到的资源
<!-- 预加载资源 -->
<link rel="prefetch" href="/next-page.js" />
<link rel="prefetch" href="/images/large.jpg" />
dns-prefetch
- 预先解析域名的 DNS 记录
- 减少 DNS 解析时间
- 适用于即将请求其他域名的资源
- 对跨域资源加载特别有效
<!-- DNS 预解析 -->
<link rel="dns-prefetch" href="//example.com" />
<link rel="dns-prefetch" href="//api.example.com" />
使用建议:
- 对确定即将访问的资源使用 prefetch
- 对跨域资源较多的站点使用 dns-prefetch
- 不要过度预加载,可能浪费带宽
- 移动端要谨慎使用,考虑流量消耗
相关技术:
- preload:当前页面必需资源的预加载
- preconnect:预先建立连接(DNS + TCP + TLS)
- prerender:预先渲染整个页面
43、前端会有哪些安全问题?该如何预防?⭐
先从应用架构的角度来看,前端可以分为多个核心模块,每个模块都有可能成为攻击目标。
1.用户界面与数据展示层
攻击风险 :跨站脚本攻击(XSS)、HTML注入、点击劫持
防御措施:
严格过滤和转义用户输入,防止恶意代码注入
使用安全模板渲染,避免直接操作 DOM(例如避免使用 innerHTML)
配置内容安全策略(CSP),限制脚本来源
- CSP本质上是建立白名单,规定了浏览器只能够执行特定来源的代码。
- 通常可以通过HTTP Header中的Content-Security-Policy来开启CSP:
- 只允许加载本站资源
Content-Security-Policy: default-src 'self'
- 只允许加载HTTPS协议图片
Content-Security-Policy: img-src https://*
- 允许加载任何来源框架
Content-Security-Policy: child-src 'none'
cestors 指令防止点击劫持
使用 HTTP 响应头
X-Frame-Options
来控制页面的嵌入方式。可以设置为:DENY
:不允许该页面在任何<iframe>
中嵌入。SAMEORIGIN
:只允许同一来源的页面嵌入。ALLOW-FROM uri
:允许特定来源的页面嵌入。
使用 CSP 的
frame-ancestors
指令来限制哪些来源可以嵌入你的页面Content-Security-Policy: frame-ancestors 'self';
仅允许来自自己网站的页面嵌入
使用 JavaScript 代码检测页面是否被嵌入
<iframe>
中。if (window.top !== window.self) { // 当前页面在 iframe 中被嵌入 window.top.location = window.self.location; // 重定向至当前页面 }
这段代码检查当前窗口是否是顶层窗口,如果不是,则将顶层窗口的URL设置为当前窗口的URL。
2.业务逻辑层
- 攻击风险 :业务逻辑漏洞导致未授权访问或功能滥用
- 防御措施:
- 实现完善的权限校验和身份验证机制
- 定期进行代码审查和安全测试,及时发现逻辑漏洞
3.数据交互层
- 攻击风险 :跨站请求伪造(CSRF)、中间人攻击、数据窃取
- 防御措施:
- 使用 HTTPS 加密数据传输,防止数据在传输过程中被窃取或篡改
- 在请求中加入 CSRF Token,并在服务器端验证请求合法性
- 配置严格的 CORS 策略,确保 API 调用来源可信
4.数据存储层
- 攻击风险 :本地存储数据泄露(LocalStorage、SessionStorage)、Cookie 劫持
- 防御措施:
- 避免在前端存储敏感信息,如必须存储应进行加密处理
- 对 Cookie 设置 HttpOnly、Secure 等属性,降低被脚本读取的风险
5.资源加载与依赖管理层
- 攻击风险 :第三方依赖库漏洞、供应链攻击、外部资源篡改
- 防御措施:
- 定期更新和审查第三方依赖,及时修补已知漏洞
- 使用子资源完整性(SRI)校验机制,确保加载的外部资源未被篡改
- 仅从可信源加载资源,杜绝未知或不受信任的代码注入
6.构建与部署流程
- 攻击风险 :构建工具或 CI/CD 流程被攻击,导致恶意代码注入
- 防御措施:
- 加强构建环境安全管理,定期更新和审查构建工具及依赖
- 采用代码签名、版本管理和自动化安全测试,确保发布版本的完整性和可追溯性
- 将安全检测纳入 CI/CD 流程,实现自动化的安全漏洞扫描
44、什么是 HTTPS 中间人攻击,如何预防
中间人攻击(MITM, Man-In-The-Middle) 是指攻击者拦截客户端与服务器之间的通信,获取敏感信息或篡改数据。
攻击原理
攻击者通过伪造证书或劫持网络流量,冒充服务器或客户端,使通信双方无法察觉中间人的存在。
预防措施
- 启用 HTTPS 和强证书验证
- 配置 TLS 并购买可信的 SSL 证书。
- 使用 HSTS(HTTP Strict Transport Security)强制 HTTPS 访问。
- 证书固定(Certificate Pinning) 确保客户端只接受特定 CA 签发的证书。
- 开启 CORS 配置 配置严格的跨域策略,减少不必要的网络暴露。
- 安全头部配置
- 设置
Content-Security-Policy
防止资源篡改。 - 设置
Strict-Transport-Security
强制使用 HTTPS。
- 设置
- 客户端验证 通过双向 TLS(Mutual TLS)验证客户端身份。