面试官您好,应用层是TCP/IP协议栈的最高层,它直接面向用户和应用程序,定义了我们能用网络来做什么。这一层的协议非常丰富,我通常会把它们按照核心功能进行分类来介绍。
这是我们日常上网接触最多的协议。
www.google.com
),解析为计算机能够理解的IP地址(如172.217.160.78
)。没有DNS,我们就只能靠记IP地址来上网了。这是一组协同工作的协议,共同完成了邮件的收发。
通过这些丰富多样的应用层协议,我们的应用程序才能在网络上实现各种复杂的功能。
面试官您好,HTTP报文是HTTP协议中,客户端和服务器之间进行通信所传输的数据块。它遵循一个非常严格的格式,我们可以把它想象成一封格式化的“信件”。
这封“信件”主要分为两种:客户端发出的请求报文(Request Message)和服务器返回的响应报文(Response Message)。它们的结构大同小异,都主要由四个部分组成。
下面我通过一个用户登录的例子,来具体说明这两类报文的组成:
假设我们正在一个登录页面,输入了用户名和密码,点击登录按钮。浏览器就会构造并发送一个类似下面这样的HTTP请求报文:
POST /login HTTP/1.1
Host: www.example.com
Content-Type: application/json
User-Agent: Mozilla/5.0 ...
Content-Length: 43
{
"username": "alice",
"password": "mypassword"
}
这封“请求信”的结构,完美地对应了四个部分:
a. 请求行 (Request Line) —— “信的标题”
POST /login HTTP/1.1
POST
,表明这次请求的意图是提交数据。/login
,指明了要请求的服务器资源路径。HTTP/1.1
。b. 请求头部 (Request Headers) —— “信封上的信息”
Host: www.example.com
Content-Type: application/json
Host
指明了目标服务器的域名,Content-Type
说明了请求体中的数据格式是JSON,User-Agent
则告诉服务器客户端的类型(浏览器信息)。c. 空行 (Blank Line) —— “信头与正文的分隔线”
d. 请求体 (Request Body) —— “信的正文内容”
{ "username": "alice", ... }
POST
请求,这里就是我们要提交的表单数据。对于GET
请求,请求体通常是空的。服务器在处理完上面的登录请求后,会返回一个类似下面这样的HTTP响应报文:
HTTP/1.1 200 OK
Content-Type: application/json
Content-Length: 35
Date: Mon, 28 Oct 2023 12:00:00 GMT
{
"status": "success",
"token": "xyz123abc"
}
这封“回信”的结构,也同样包含四个部分:
a. 状态行 (Status Line) —— “回信的标题”
HTTP/1.1 200 OK
HTTP/1.1
。200
,这是一个非常重要的数字,代表“请求成功”。其他常见的如404
(未找到)、500
(服务器内部错误)等。OK
,对状态码的一个简短的文本描述。b. 响应头部 (Response Headers) —— “回信信封上的信息”
Content-Type: application/json
Content-Type
告诉浏览器,我返回给你的也是JSON格式的数据,Content-Length
则指明了响应体的长度。c. 空行 (Blank Line)
d. 响应体 (Response Body) —— “回信的正文内容”
{ "status": "success", ... }
总结一下,无论是请求还是响应,HTTP报文都遵循着这样一个 “起始行 + 头部 + 空行 + 主体” 的清晰结构。正是这个标准化的格式,保证了在复杂的互联网环境中,客户端和服务器之间能够准确无误地进行通信。
面试官您好,HTTP状态码是服务器用来响应客户端请求结果的一种标准化方式。它们被清晰地划分为五个大的类别,每个类别都代表了一类特定的响应情况。
100 Continue
:客户端可以继续发送请求的剩余部分。在上传大文件时可能会用到。200 OK
:最常见的成功状态码。表示请求已成功,响应体中包含了请求的资源。201 Created
:表示请求已成功,并且在服务器上创建了一个新的资源。通常是POST
或PUT
请求成功后的响应。204 No Content
:表示请求已成功处理,但没有内容可以返回。通常用于DELETE
请求成功后,或者一个更新操作成功但无需返回新数据时。301 Moved Permanently
(永久重定向):请求的资源已被永久移动到新的URL。浏览器和搜索引擎会缓存这个新的URL。非常适合用于网站域名更换或URL结构调整。302 Found
(临时重定向):请求的资源暂时被移动到新的URL。浏览器和搜索引擎不会缓存这个重定向。适用于临时的活动页面、登录跳转等场景。304 Not Modified
:用于HTTP缓存。当客户端发起一个带条件的GET请求(比如If-Modified-Since
),服务器发现资源没有变化,就会返回304,告诉客户端可以直接使用本地缓存,从而节省了带宽。400 Bad Request
:最常见的客户端错误,表示请求报文的语法有误,服务器无法理解。401 Unauthorized
:表示请求需要身份认证。客户端需要提供有效的凭证(如Token)才能访问。403 Forbidden
:表示服务器已经理解了请求,但拒绝执行。与401不同,这通常意味着“即使你亮明了身份,你也没有权限访问这个资源”。404 Not Found
:最著名的状态码,表示服务器上找不到请求的资源。405 Method Not Allowed
:您提到的这个也很重要,表示请求的方法(如POST
)不被目标资源所支持(比如该URL只支持GET
)。500 Internal Server Error
:最常见的服务器错误。这是一个通用的、笼统的错误码,表示服务器遇到了一个意外情况,无法完成请求。通常是代码中的Bug导致的。502 Bad Gateway
:通常出现在使用了反向代理或网关的架构中。它表示作为网关的服务器,从上游服务器(比如真正的业务服务器)收到了一个无效的响应。503 Service Unavailable
:表示服务器当前暂时无法处理请求。这可能是因为服务器过载、正在进行停机维护等。通常这是一个临时状态,稍后会恢复。504 Gateway Timeout
:也与网关有关。它表示作为网关的服务器,在指定时间内没有收到来自上游服务器的响应,即上游服务超时了。通过理解这些状态码的精确含义,我们可以在开发和排查问题时,快速地定位到是客户端、网络还是服务器端出了问题。
面试官您好,HTTP协议定义了一组请求方法(Request Methods),也常被称为“HTTP动词”。它们用来指明客户端希望对服务器上的目标资源(由URL指定) 执行什么样的操作。
我通常会按照常用程度和语义特性来介绍它们:
这四种方法,完美地对应了我们对资源的“增删改查”操作,是构建RESTful API的基础。
1. GET:查 (Read)
2. POST:增 (Create)
3. PUT:改 (Update)
PUT /users/123
来更新用户信息,无论执行多少次,最终用户123的信息都是最后一次提交的那个。4. DELETE:删 (Delete)
5. HEAD
Content-Length
(文件大小)、Last-Modified
(最后修改时间)等,而无需下载整个文件,非常节省带宽。6. PATCH
PATCH
请求是“将age字段加1”,执行多次,结果就不同了。7. OPTIONS
总结一下,通过语义化地使用这些不同的HTTP方法,我们可以设计出结构清晰、符合RESTful风格的API,让客户端和服务器之间的通信意图变得一目了然。
面试官您好,GET和POST是HTTP协议中最常用、也最容易被混淆的两种请求方法。要理解它们的区别,我通常会从两个层面来看:一是RFC规范定义的“语义”层面,二是实际工程应用中的“惯例”层面。
这是它们最核心、最根本的区别。
1. 核心作用与语义 (What for?)
2. 参数传递方式 (How to send data?)
.../search?q=keyword
。
3. 安全性 (Safety) 与 幂等性 (Idempotence)
4. 缓存与收藏 (Caching & Bookmarking)
虽然RFC规范定义得非常清晰,但在实际开发中,开发者并不总是严格遵守。
“万能的POST”:
“不规范的GET”:
.../delete?id=123
)。这是一种非常糟糕的、违反HTTP语义的设计,因为它可能被网络爬虫、预加载等机制无意中触发,导致严重的数据误删问题。特性 | GET (规范定义) | POST (规范定义) |
---|---|---|
语义 | 获取/查询 | 提交/创建/处理 |
参数位置 | URL 查询字符串 | 请求体 (Body) |
安全性 | 安全 | 不安全 |
幂等性 | 幂等 | 不幂等 |
可缓存性 | 可缓存 | 不可缓存 |
在我的开发实践中,我会严格遵循RFC的语义规范:
这样做,不仅能让我们的API设计更清晰、更符合RESTful风格,也能更好地利用HTTP协议自身的缓存等特性,并避免很多潜在的安全问题。我只会在遇到像“URL长度超限”这类不得已的情况下,才考虑用POST来做查询。
面试官您好,HTTP的长连接(也叫持久连接,Persistent Connection 或 HTTP keep-alive),是HTTP协议中一种非常重要的连接管理机制。
要理解它的作用,我们首先要看一下它的“反面”——短连接。
为了解决短连接的性能问题,HTTP/1.1协议将长连接作为了默认的行为。
工作模式:
Keep-Alive
头部的timeout
参数控制)没有新的请求,或者客户端/服务器明确地要求关闭,这个连接才会被断开。比喻升级:现在,你和店员握一次手后,可以一直保持着握手的状态,连续地向他要水、要薯片、要面包……直到你买完所有东西,才最终松手说再见。
带来的巨大好处:
Connection
字段来控制。
Connection: keep-alive
(默认值):表示希望保持长连接。Connection: close
:表示处理完当前请求后,就关闭连接。Keep-Alive
头部还可以包含timeout
(连接超时时间)和max
(一个连接上最多能处理的请求数)等参数。总结一下,HTTP长连接是一种通过复用TCP连接,来减少连接建立和关闭开销的关键性能优化技术。它是HTTP/1.1的默认行为,并为后续HTTP/2更高效的多路复用奠定了基础。
面试官您好
“默认端口”的意义在于,当我们在浏览器中访问一个网址时,如果没有明确地指定端口号,浏览器就会根据协议的类型,自动地去连接这个默认端口。
http://www.example.com
时,浏览器实际访问的是 http://www.example.com:80
。https://www.example.com
时,浏览器实际访问的是 https://www.example.com:443
。正是因为有这个“默认”的约定,我们平时上网才不需要在网址后面手动输入:80
或:443
,大大简化了URL。当然,如果服务器的HTTP/HTTPS服务监听在非标准端口上(比如我们本地开发时常用的8080端口),那么在访问时就必须显式地指定端口号,例如http://localhost:8080
。
面试官您好,您提出的这个问题非常好,它触及了HTTP协议如何在一个TCP连接上,准确地界定一个HTTP报文边界的核心问题。
在HTTP/1.1中,为了在一个TCP连接上传输多个HTTP请求和响应(长连接),客户端和服务器必须有一种明确的方式来知道“一个报文到哪里结束,下一个报文从哪里开始”。这个“拆包”或“边界界定”,主要有以下两种机制:
Content-Length
的长度界定(最常见)这是最直观、最常用的一种方式。
工作原理:
Content-Length
字段,它的值表示报文主体(Body)的精确字节数。Content-Length
后,就会严格地按照这个长度,从TCP流中读取相应字节数的数据作为报文主体。读完这么多字节后,它就知道这个报文结束了,后面紧跟着的就是下一个报文的起始行。适用场景:
Content-Length
。Transfer-Encoding: chunked
的分块传输编码这种机制,是为了解决一个Content-Length
无法处理的痛点。
Content-Length
就无能为力了。比如,一个需要从数据库中流式查询并实时返回的大型JSON数组。Transfer-Encoding: chunked
,表示将采用分块的方式传输数据。chunk-size
: 一个十六进制的数字,表示后面chunk-data
的长度。chunk-data
: 实际的数据块。0\r\n\r\n
),来明确地标记整个报文主体的结束。read()
返回-1),就知道报文已经接收完毕。Connection: close
头部被明确指定时,才会使用这种方式。总结一下,在HTTP/1.1中,“拆包”或界定消息边界,主要通过两种方式:
Content-Length
。Transfer-Encoding: chunked
。这两种机制的配合,保证了HTTP/1.1能够在长连接上,准确、高效地传输多个报文。
面试官您好,HTTP的断点续传,也叫范围请求(Range Requests),是HTTP/1.1协议中一项非常重要的功能。
它的核心作用,是允许客户端只请求一个大资源(如视频、大文件)的一部分,而不是每次都必须从头开始下载整个文件。这对于实现文件下载的断点续传、在线视频的拖动播放等功能至关重要。
这个功能的实现,是一场客户端和服务器之间,基于特定HTTP头部的精密“对话”。
首先,服务器需要告诉客户端:“我支持范围请求”。
HEAD
请求),服务器会在响应头中,包含一个关键字段:
Accept-Ranges: bytes
现在,我们来模拟一个断点续传的场景:
Range: bytes=512000-
Range
头部的含义是:“请从第512000个字节(从0开始计数)开始,一直把文件剩下的部分都传给我。”Range
的格式非常灵活,比如bytes=0-499
(请求前500字节),bytes=-500
(请求最后500字节)。服务器在接收到这个带Range
头的请求后,如果它能处理这个范围,它就会返回一个特殊的响应:
状态码变为 206 Partial Content
200 OK
。206
这个状态码,明确地告诉客户端:“好的,我理解你的范围请求,现在发给你的,只是这个资源的一部分。”响应头包含关键的范围信息:
Content-Range: bytes 512000-1048575/1048576
512000
到1048575
这部分内容,而这个资源的总大小是1048576
字节(1MB)。”Content-Length: 524288
Content-Length
,不再是整个文件的总大小,而是本次响应体的大小(即512KB)。416 Requested Range Not Satisfiable
的状态码,并可能在Content-Range
头中指明正确的资源大小。所以,HTTP断点续传的实现,就是通过这样一套客户端与服务器之间的“问答”机制来完成的:
Accept-Ranges
表明能力。Range
头,提出具体的范围请求。206
状态码和Content-Range
头,来精确地响应这部分内容。正是这套基于HTTP头部的标准协议,才使得我们能够在大文件下载和流媒体播放等场景中,获得流畅、可恢复的用户体验。
面试官您好,HTTP协议之所以被认为是不安全的,其根源在于它的两大天生缺陷:“明文传输”和“无身份验证”。
这两个缺陷,直接导致了三大经典安全风险。
风险一:窃听风险 (Eavesdropping)
风险二:篡改风险 (Tampering)
风险三:冒充风险 (Impersonation / Spoofing)
为了解决这些问题,HTTPS应运而生。它并不是一个全新的协议,而是在HTTP和TCP之间,增加了一层SSL/TLS安全层。这个安全层,提供了“安全三件套”,完美地应对了上述三大风险。
解决方案一:信息加密 (Encryption) —— 对抗窃听
解决方案二:校验机制 (Integrity) —— 对抗篡改
解决方案三:身份证书 (Authentication) —— 对抗冒充
所以,HTTPS通过加密解决了窃听问题,通过校验解决了篡改问题,通过证书解决了冒充问题。它为我们的网络通信,提供了一个安全、可靠的通道。
当然,正如您幽默的总结,HTTPS虽然保护了我们的数据在传输过程中的安全,但它管不了我们自己的“剁手”行为,也管不了网站本身的内容(比如竞价排名广告)。这是技术安全与业务行为的边界。
面试官您好,HTTP和HTTPS虽然看起来只有一字之差,但它们之间存在着本质的区别。最核心的一点是:HTTPS可以被看作是HTTP的安全增强版,它通过在HTTP之下、TCP之上,增加了一层SSL/TLS安全协议,来解决HTTP自身存在的安全风险。
这种根本性的设计差异,导致了它们在以下几个方面有显著的不同:
特性 | HTTP | HTTPS |
---|---|---|
安全性 | 明文,不安全 | SSL/TLS加密,安全 |
连接过程 | TCP三次握手 | TCP三次握手 + SSL/TLS握手 |
默认端口 | 80 | 443 |
证书要求 | 不需要 | 需要CA证书 |
成本 | 低 | 较高(证书费用、CPU消耗) |
在今天的互联网环境下,安全性已经不再是一个可选项。Google等主流浏览器已经将未使用HTTPS的网站标记为“不安全”,并且搜索引擎也更青睐HTTPS网站。因此,全站启用HTTPS已经成为了现代Web开发的标准实践。
面试官您好,HTTPS的握手过程,其核心目标是让客户端和服务器在一个不安全的网络上,安全地协商出一个用于后续通信的对称加密密钥。
正如您所分析的,我们以传统的、基于RSA算法的密钥交换为例,这个握手过程,可以形象地分为 “两次对话”,总共涉及四次消息传递。
下面我来详细描述一下这四次握手的每一步都在做什么:
Client Random
)。这个随机数是后续生成会话密钥的关键参数之一。TLS_RSA_WITH_AES_128_GCM_SHA256
),你看看你会哪个,我们挑一个用吧。”Server Hello
:服务器从客户端的加密套件列表中,选择一个它也支持的加密套件,并确定一个TLS协议版本,然后告诉客户端:“好的,我们就用这个版本和这个加密套件来通信吧。” 同时,它也会生成一个服务器端的随机数(Server Random
)。Certificate
:这是最关键的一步。服务器会将自己的数字证书发送给客户端。这个证书,是由权威的CA机构签发的,里面包含了服务器的公钥,以及服务器的身份信息。Server Hello Done
:一个结束标记,告诉客户端:“我的话说完了,该你了。”(此时,客户端会先对服务器的证书进行验证,确保其合法可信。如果验证失败,握手就会中断。)
Client Key Exchange
:【RSA算法的核心】客户端会用从服务器证书中获取到的公钥,来加密这个刚刚生成的“预主密钥”,然后将其发送给服务器。
Client Random
、Server Random
、Pre-master Secret
。它们会用完全相同的算法,将这三个数混合在一起,各自独立地计算出最终用于通信的 “会话密钥”(一个对称密钥)。Change Cipher Spec
:客户端发送一个通知,告诉服务器:“我准备好了,从现在开始,我们后面的通信就用刚刚协商好的会话密钥来加密了。”Encrypted Handshake Message
:客户端会将之前所有握手消息的摘要,用这个新的会话密钥加密后,发送给服务器。这既是作为一个“握手完成”的信号,也是对服务器的一次验证,看它能否正确解密。Change Cipher Spec
:服务器也发送一个通知,告诉客户端:“我也准备好了,我们开始加密通信吧。”Encrypted Handshake Message
:服务器同样会将之前所有握手消息的摘要,用会话密钥加密后发送给客户端,让客户端也验证一下。当客户端成功解密并验证了这条消息后,SSL/TLS握手过程就正式完成了。之后,双方就可以使用这个高效的、对称的会话密钥,来加密和解密真正的HTTP应用数据了。
总结一下,这个过程,就是通过非对称加密(RSA)来安全地交换一个用于对称加密的密钥,从而兼顾了安全性和性能。
面试官您好,您提出的这个问题,直击了HTTPS协议设计的核心价值。HTTPS之所以能够有效防范中间人攻击(Man-in-the-Middle Attack, MITM),其秘诀在于它通过SSL/TLS层,建立了一套环环相扣、无法被攻破的信任链和加密机制。
一个典型的中间人攻击,其过程是这样的:
HTTPS通过两大核心机制,让中间人的“作案”无法得逞。
机制一:数字证书与身份认证 (Authentication) —— “验明正身,你是谁?”
机制二:密钥交换与数据加密 (Encryption) —— “即使截获,也看不懂”
所以,HTTPS防范中间人攻击,是一个双重保险的过程:
正是这套严谨的机制,确保了我们的在线支付、信息传递等敏感操作,能够在复杂的互联网环境中安全地进行。
面试官您好,HTTP/2的诞生,并不是对HTTP协议的推倒重建,而是一次以性能为核心目标的、革命性的升级。它所做的所有改进,几乎都是为了解决HTTP/1.1在性能上存在的几个核心瓶颈。
我可以从以下几个方面,来详细对比它们之间的区别:
HEADERS
帧和DATA
帧。这些帧都是二进制编码的。这是HTTP/2最核心、最重要的改进。
User-Agent
, Accept
等),并且每次请求都必须发送完整的头部,这在请求量大时,会浪费大量带宽。总结一下,HTTP/2通过二进制分帧奠定了基础,通过多路复用解决了核心的队头阻塞问题,再通过头部压缩和服务器推送这两个“助推器”,从多个维度,对HTTP/1.1进行了一次全方位的、以性能为导向的彻底革新。
面试官您好,一个HTTP通信所依赖的TCP连接,其“中断”或“关闭”,可以由多个层面的多种原因触发。我通常会把这些情况分为 “主动关闭”、“异常中断”和“超时中断” 三大类。
这是最常见、最正常的关闭方式,由通信的一方主动发起。
Connection: close
:当客户端或服务器在HTTP头部中明确发送了Connection: close
时,表示处理完当前这次通信后,就希望关闭连接。close()
:服务器或客户端的应用程序,因为业务逻辑需要(比如用户退出、服务关闭),主动调用了socket的close()
方法。这种情况通常是由于不可预期的错误或强制操作导致的。
a. RST报文复位连接
RST
(Reset)是TCP协议中一个表示 “连接重置” 的标志位。它是一种粗暴的、单方面的关闭方式,收到RST的一方,会立即关闭连接,而不会进行四次挥手。b. 重传超时
这种情况是为了防止大量空闲连接,无谓地占用服务器资源。
a. HTTP层面的Keep-Alive
超时
Keep-Alive
机制允许在一个TCP连接上处理多个请求。但这个连接不会永久保持。keepalive_timeout 60s
)。如果一个TCP连接,在指定的时间内,没有任何新的HTTP请求到达,服务器就会主动发起四次挥手,关闭这个连接以释放资源。b. TCP层面的Keepalive
机制
Keep-Alive
是两回事。总结一下,一个TCP连接的中断,既可能是由上层应用主动、优雅地发起的(四次挥手),也可能是因为网络异常或程序错误而被动、粗暴地中断的(RST、重传超时),还可能是因为长时间空闲而被策略性地清理的(超时中断)。理解这些不同的中断方式,对于我们排查网络问题和设计健壮的网络程序非常有帮助。
面试官您好,HTTP、Socket和TCP是网络编程中三个不同层面、但又紧密相关的概念。要理解它们的区别,最好的方式是把它们放在TCP/IP协议栈的框架里来看。
我们可以把一次网络通信,比作一次完整的“寄快递”过程:
1. 从“层次”上看:HTTP是上层,TCP是下层,Socket是桥梁
200 OK
状态码代表什么意思。connect
, bind
, read
, write
),让应用程序可以方便地使用网络服务。2. 从“关系”上看:HTTP“依赖”于TCP,而我们通过Socket来“使用”TCP
socket()
API,请求操作系统创建一个TCP Socket。connect
请求,触发TCP的三次握手,与服务器建立一个可靠的连接。write
方法,将HTTP请求报文写入TCP的发送缓冲区,由TCP协议负责将其分段、打包、发送出去。服务器端则通过Socket的read
方法来接收。总结一下:
它们是网络通信中,不同抽象层次上、各司其职又紧密协作的三个核心概念。
面试官您好,我了解DNS。它的全称是Domain Name System(域名系统)。
www.google.com
),翻译成计算机网络能够理解的IP地址(比如 172.217.160.78
)。要理解DNS的工作原理,首先要理解它的域名结构。域名的结构是分层的,并且是 “从右到左,级别越高”。
一个生动的比喻:您用的“中外地址”的比喻非常贴切。域名的层级,就像是西方人写地址的顺序,从最小的单位开始,逐步到最大的单位。
域名的树状层级结构:
.
,比如 www.google.com.
,这个点就代表根域。全球只有13组根DNS服务器。.com
, .org
, .net
(通用顶级域),以及 .cn
, .us
(国家顶级域)。google.com
, baidu.com
。www.google.com
中的 www
,或者 mail.google.com
中的 mail
。为什么要分层和分布式?
当我们在浏览器输入www.google.com
并回车时,一次典型的DNS解析过程是这样的(以递归+迭代查询为例):
第一站:本地DNS服务器 (Local DNS Server)
www.google.com
的IP地址。”第二站:根DNS服务器 (Root Server)
www.google.com
?”.com
顶级域的事,你去问负责.com
的服务器吧。” 然后,它会返回.com
顶级域DNS服务器的地址列表。第三站:顶级域DNS服务器 (TLD Server)
www.google.com
?”.com
服务器说:“我也不知道,但这是google.com
这个域自己的事,你应该去问它自己的权威服务器。” 然后,它会返回google.com
的权威DNS服务器(也叫NS服务器)的地址。第四站:权威DNS服务器 (Authoritative Name Server)
google.com
的权威DNS服务器发起迭代查询:“www.google.com
的IP地址到底是多少?”google.com
这个域下所有记录的最终答案。它会查询自己的记录,找到www
这条A记录对应的IP地址,然后将这个最终的IP地址返回给本地DNS服务器。最后一站:返回给客户端
总结一下,DNS通过一个全球性的、分层分布式的树状数据库系统,和一套递归与迭代相结合的查询机制,高效、可靠地完成了将域名“翻译”成IP地址这一互联网的基石性任务。
面试官您好,DNS服务使用的默认端口号是53。
值得一提的是,DNS在进行查询和响应时,会根据不同的场景,在UDP的53端口和TCP的53端口之间进行选择。
绝大多数情况使用 UDP/53
在特定情况下会使用 TCP/53
总结一下,DNS的默认端口是53。它主要使用UDP/53来进行快速的、日常的域名查询,同时保留了使用TCP/53来处理大数据量传输和高可靠性同步的能力。
面试官您好,关于DNS底层使用的传输协议,最准确的回答是:它主要使用UDP,但在特定场景下,必须使用TCP。 这是一个在“效率”和“可靠性”之间做出的经典权衡设计。
正如您所分析的,对于日常的、一次性的域名解析请求,UDP是最佳选择,其优势主要体现在:
a. 低延迟,响应快
b. 资源开销小,性能高
如何应对UDP的不可靠?
虽然UDP快,但它有两个致命的限制:不可靠和数据包大小有限(通常为512字节)。当遇到以下两种情况时,DNS就必须切换到更可靠的TCP协议上来:
a. 当DNS响应报文过大时
b. 进行“区域传送”(Zone Transfer)时
总结一下:
DNS的设计非常务实。它在日常的、大量的、小数据包的查询中,选择了UDP来追求极致的速度和效率;同时,它又保留了在大数据量传输和高可靠性要求的场景下,“升级”到TCP的能力。这是一个非常经典的、根据场景选择最合适协议的案例。
面试官您好,您提出的这个问题非常核心。最准确的回答是:HTTP协议本身,在设计上是完全无状态的(Stateless)。但是,为了满足现代Web应用的业务需求,我们通过一些额外的机制,在应用层面实现了状态的维持。
它是什么?
为什么当初要这样设计?
为了解决这个矛盾,Web开发者们发明了一套非常经典的机制,来在无状态的HTTP协议之上,构建一个“有状态”的会话。这个机制的核心,就是Cookie和Session的配合。
Set-Cookie
字段,将这个独一无二的Session ID,像一张“储物柜的钥匙”,发送给客户端的浏览器。Cookie: sessionId=...
)。总结一下:
这种设计,既保留了HTTP无状态带来的高可扩展性的优点,又通过Cookie和Session,满足了现代Web应用对会话保持的需求,是一个非常经典和优雅的工程解决方案。
面试官您好,您提出的这个问题非常棒,它直击了HTTP协议设计哲学的一个核心。
最精确的答案是:即使一个HTTP请求携带了Cookie,HTTP协议本身,从根本上来说,依然是无状态的。
要理解这个看似矛盾的结论,我们需要弄清楚“状态”到底是由谁来维护的。
我们可以把HTTP服务器想象成一个记忆力极差、“脸盲”且“健忘” 的图书管理员。
无状态的本质:这个管理员不会去记任何一个读者的脸,也不会记得任何一个读者之前借了什么书。每次有读者来找他,对他来说都是一个全新的、独立的事件。他处理完这次借阅后,立刻就会把这个读者忘得一干二净。这就是HTTP的无状态性。
Cookie扮演的角色:一张“借书卡”
从这个比喻中,我们可以清晰地看到:
总结一下:
所以,可以说,我们是在一个无状态的协议上,通过一个有状态的“信物”(Cookie),实现了一个看起来“有状态”的应用会话。协议的本质,并未改变。
面试官您好,Cookie、Session和Token,都是我们在Web开发中,为了解决HTTP无状态问题,而采用的用户身份认证和状态保持的技术。它们代表了三种不同时期、不同设计思想的解决方案。
我通常会按照它们的演进关系来介绍:
为了解决Cookie不安全的问题,Session机制应运而生。
为了解决Session机制在分布式和跨域场景下的弊端,基于Token的认证方式成为了现代Web开发(特别是前后端分离、微服务架构)的主流。
localStorage
)。Authorization
头,以Bearer
为前缀),手动地带上这个Token。特性 | Cookie | Session | Token |
---|---|---|---|
存储位置 | 客户端 | 服务端 | 客户端 |
状态 | 有状态(在客户端) | 有状态(在服务端) | 无状态(服务端不存) |
依赖关系 | 无 | 依赖Cookie传递ID | 无 |
可扩展性 | - | 差(需Session共享) | 好 |
适用场景 | 简单数据存储 | 传统的、有状态的Web应用 | 前后端分离、微服务、移动App |
在我的实践中,对于传统的、紧密耦合的Web应用,可能会使用Session。但对于所有现代的、前后端分离或微服务的项目,我都会优先选择基于JWT(JSON Web Token)的Token认证方案。
面试官您好,您提出的这个问题非常好,它触及了Web早期会话管理的一个核心依赖关系。
直接的答案是:在默认配置下,如果客户端禁用了Cookie,Session机制就会失效,无法正常使用。
要理解这一点,我们需要回顾一下标准的Session工作流程:
Set-Cookie
响应头,将Session ID写入一个特殊的Cookie中(通常叫JSESSIONID
)。JSESSIONID
的Cookie。服务器就是通过读取这个Cookie中的ID,来找到对应的Session数据。所以,一旦客户端禁用了Cookie,这个传递“身份证”(Session ID)的通道就被切断了。服务器无法将会话标识发给客户端,客户端也无法在后续请求中携带它,服务器自然就无法识别这是哪个用户的会话了。
虽然默认机制失效了,但为了兼容那些极少数禁用Cookie的“古老”浏览器或特殊客户端,Web开发者们也设计出了一些“备用方案”。主要有两种:
方案一:URL重写 (URL Rewriting)
href="/products/123"
变成了 href="/products/123;jsessionid=ABCDEF123456"
。方案二:隐藏表单字段 (Hidden Form Fields)
字段,其value
就是Session ID。
POST
) 的请求有效。对于普通的链接点击(GET
请求)或Ajax请求,就无能为力了。在今天的Web开发实践中:
localStorage
),并且需要客户端通过HTTP头部手动附加,但它从设计上就摆脱了对浏览器自动发送Cookie的依赖,更加灵活。总结一下,虽然理论上可以通过URL重写等“奇技淫巧”来在禁用Cookie的情况下维持Session,但这些方案都存在严重的安全和可用性问题,在现代开发中几乎不再使用。因此,我们可以认为,在正常的、安全的Web应用中,Session的有效工作,是强依赖于客户端开启Cookie支持的。
面试官您好,localStorage
和Cookie
都是浏览器提供的、用于在客户端本地存储数据的技术,但它们的设计目的、工作机制和特性截然不同,适用于完全不同的场景。
正如您所分析的,它们的主要区别体现在以下几个方面:
特性 | Cookie | localStorage |
---|---|---|
1. 存储位置与目的 | 客户端,但设计初衷是为了与服务器通信 | 纯粹的客户端存储 |
2. 与服务器的交互 | 自动发送:每次HTTP请求,都会自动附加在请求头中发送给服务器。 | 不发送:数据仅存在于本地,除非你用JS手动取出并放入请求。 |
3. 存储容量 | 非常小 (约4KB) | 较大 (通常5-10MB,因浏览器而异) |
4. 生命周期 | 可配置:可设置过期时间,过期后自动删除;若不设置,则为会话Cookie,浏览器关闭时删除。 | 永久性:数据会一直存在,除非被用户手动清除浏览器缓存,或被JS代码显式删除。它不受浏览器关闭的影响。 |
5. API易用性 | 较差:原生JS操作Cookie比较繁琐,需要自己封装或使用库。 | 非常好:提供了非常简洁的同步API,如 localStorage.setItem() , localStorage.getItem() , localStorage.removeItem() 。 |
6. 安全性 | 较低:易受CSRF攻击,且因为随请求发送,可能暴露给中间人。 | 较高:数据不离开客户端,不易被网络窃听,但仍需防范XSS攻击。 |
基于以上区别,它们的适用场景非常明确:
什么情况下使用 Cookie?
什么情况下使用 localStorage?
sessionStorage
的区别sessionStorage
,它的API和localStorage
几乎一模一样。sessionStorage
中存储的数据,是会话级别的。一旦用户关闭了浏览器标签页或浏览器,sessionStorage
中的数据就会被自动清除。它非常适合用来存储一些一次性会话的临时数据。总结一下,我会根据数据的用途、大小、生命周期要求来做选择:
面试官您好,当我们需要在客户端存储数据时,选择Cookie还是LocalStorage,我的决策过程主要基于对这份数据特性的分析。我会问自己几个关键问题:
问题一:这份数据,是否需要在每一次HTTP请求中,都自动地、无差别地发送给服务器?
是 -> 必须使用 Cookie
Cookie
头中带上这个身份标识,我们无需手动处理。这是Cookie最核心、最不可替代的用途。否 -> 优先考虑 LocalStorage
问题二:这份数据的敏感性如何?
敏感数据(如用户凭证)
HttpOnly
属性,来防止JavaScript脚本读取,从而抵御XSS攻击。同时,也需要配合后端的CSRF Token等机制来防范CSRF攻击。非敏感数据
问题三:这份数据的生命周期要求是怎样的?
需要精确的、自动的过期控制 -> 使用 Cookie
Expires
或Max-Age
属性,浏览器会自动地在指定时间后将其删除。需要“永久”存储,直到用户或代码主动清除 -> 使用 LocalStorage
localStorage
的数据不会因为浏览器关闭而消失,它会一直存在,除非被手动清除。这非常适合存储那些希望长期保留的用户设置。问题四:这份数据的大小如何?
所以,我的决策流程是这样的:
HttpOnly
, Secure
, SameSite
)。通过这个清晰的决策流程,我们就能为不同类型的数据,选择最合适的客户端存储方案。
面试官您好,JWT(JSON Web Token)是一种现代的、轻量级的身份认证和授权规范。它与传统的、基于Session-Cookie的认证方式,在设计哲学和实现机制上,有着根本性的区别。
JWT则完全不同。它是一个自包含(Self-Contained)的、带有数字签名的字符串。这个字符串本身,就包含了所有必要的认证信息。
一个JWT通常由三部分组成,用.
隔开:Header.Payload.Signature
这种“无状态、自包含”的设计,带来了几大革命性的优势:
1. 无状态性与强大的可扩展性
2. 更高的安全性
Authorization
头部中传输的,并且需要前端JS代码手动添加,它不依赖于浏览器的Cookie自动发送机制,因此天然地就能在很大程度上防范CSRF攻击。3. 天生的跨域支持与多平台适用性
当然,JWT也并非银弹,它也带来了一些新的挑战:
总结一下,JWT通过一种无状态、自包含、带签名的令牌机制,完美地解决了传统Session-Cookie模式在分布式扩展、安全性和跨域方面的核心痛点,是现代Web应用(特别是前后端分离和微服务架构)中,进行身份认证和授权的事实标准。
面试官您好,您提出的这个问题,直击了现代Web架构从单体走向分布式过程中,身份认证方式演进的核心驱动力。
集群部署,简单来说,就是为了应对高并发、保证高可用,我们将同一个应用程序,同时部署在多台服务器上,然后通过一个负载均衡器(Load Balancer),将用户的请求分发到这些不同的服务器实例上去处理。
在单机时代,传统的Session-Cookie方式工作得很好。但一旦进入集群部署,它就遇到了一个致命的难题——Session共享。
问题根源:Session是有状态的,它默认被存储在单个服务器的内存中。
一个生动的比喻:多位“健忘的”图书管理员
传统的解决方案:为了解决这个问题,我们需要让所有管理员都能访问到同一份档案。常见的方案有:
JWT(JSON Web Token)的出现,提供了一种完全不同的、无状态的解决思路。
核心思想:正如您所说,JWT是自包含(Self-Contained) 的。它不再需要服务器端存储任何Session信息。
比喻升级:
JWT的优势:
总结一下,JWT通过将认证信息和用户状态“自包含”在令牌本身,并用数字签名来保证其不可篡改,从而彻底摆脱了对服务端Session存储的依赖。这使得它能够完美地适应无状态、可水平扩展的集群和微服务架构,是现代分布式系统身份认证的事实标准。
面试官您好,这个问题,直击了JWT(JSON Web Token)设计哲学中的一个核心权衡点。
JWT最大的优点是“无状态”,但这也正是它最大的“缺点”来源:一旦签发,在它过期之前,服务器端默认是无法主动让它失效的。
所以,当JWT泄露时,我们就必须引入一些额外的机制,来“打破”这种纯粹的无状态,从而实现令牌的失效。
正如您所分析的,主要有以下几种方案:
方案一:缩短令牌有效期(预防为主)
方案二:引入刷新令牌(Refresh Token)机制(业界标准实践)
Access Token
(如15分钟),用于日常的API访问。Refresh Token
(如7天或更长),它只用于一个目的——获取新的Access Token
。Access Token
和Refresh Token
都保存起来。Access Token
过期后,客户端会带着这个 Refresh Token
,去访问一个专门的“刷新”接口。Refresh Token
的有效性,如果通过,就签发一个新的Access Token
给客户端,实现“无感续签”。Access Token
,泄露风险窗口小。Refresh Token
只在刷新时使用一次,并且服务器可以在服务端记录和管理每一个Refresh Token
的状态。Refresh Token
给失效掉即可。 这样,他就再也无法获取到新的Access Token
了。方案三:建立黑名单机制(主动失效)
jti
声明)以及它的过期时间,存入Redis的黑名单中。Access Token
有效期:是必须采取的基础安全措施。在实践中,我们通常会采用 “短Access Token + 长Refresh Token + 可选的黑名单” 这套组合拳,来构建一个既安全又灵活的JWT认证系统。
面试官您好,JWT通过一种无状态、自包含的方式,完美地解决了传统Session在分布式和跨域场景下的难题。
当服务器签发一个JWT给前端后,前端如何安全、有效地存储这个Token,就成了一个非常关键的问题。主要有三种存储位置,它们各有优劣。
localStorage
中 (最常见的方式)localStorage.setItem('jwt_token', token)
将Token存入。localStorage
中取出Token,并放入HTTP的Authorization
请求头中。通常这会封装在axios或fetch的请求拦截器里。localStorage
的存储空间较大(通常5-10MB),足以存放Token。setItem
, getItem
API简单易用。localStorage
可以被任何同源的JavaScript代码访问到。如果网站存在XSS(跨站脚本攻击)漏洞,攻击者注入的恶意脚本,就可以轻松地读取到localStorage
中的Token,然后冒用用户身份。Cookie
中Set-Cookie
将Token写入Cookie。浏览器会自动保存,并在后续请求中自动携带。HttpOnly
属性,那么这个Cookie就无法被JavaScript访问。这极大地提升了安全性,有效防止了因XSS攻击导致的Token泄露。SameSite
属性为Strict
或Lax
。sessionStorage
中localStorage
完全一样,但生命周期不同。sessionStorage
是会话级别的。一旦用户关闭了浏览器标签页或浏览器,其中存储的数据就会被自动清除。localStorage
类似,同样存在XSS风险,但因为关闭标签页就失效,其泄露的风险窗口更小。存储方式 | 优点 | 缺点 | 安全性 |
---|---|---|---|
localStorage |
容量大、API简单、防CSRF | 易受XSS攻击 | XSS: ❌, CSRF: ✅ |
Cookie |
使用方便、HttpOnly 可防XSS |
易受CSRF攻击、容量小、有跨域问题 | XSS: ✅, CSRF: ❌ |
sessionStorage |
同localStorage, 但生命周期短 | 同localStorage | XSS: ❌, CSRF: ✅ |
我的选型策略与最佳实践:
这是一个典型的 “安全权衡” 问题。XSS和CSRF是两种最主要的Web攻击方式,我们需要在这两者之间找到平衡。
如果安全是第一要务:我会选择将Token存储在HttpOnly
的Cookie中。
SameSite=Strict
Cookie策略和基于表单的CSRF Token校验,来构建一个纵深防御体系。如果是构建纯粹的前后端分离SPA应用,且跨域场景复杂:
localStorage
。总而言之,没有绝对安全的方案,只有更安全的组合策略。我个人更倾向于 HttpOnly
Cookie + CSRF防护的方案,因为它能更有效地保护Token本身不被窃取。
面试官您好,您提出的这个问题非常好,它触及了后端服务间通信技术选型的一个核心。
直接的答案是:HTTP和RPC并不是“替代”关系,而是两种定位不同、各有专长的通信方式。 它们在不同的场景下,分别解决了不同的问题。
正如您所分析的,RPC(Remote Procedure Call,远程过程调用)本身,并不是一个具体的协议,而是一种调用方式或编程模型。
Dubbo
, gRPC
, Thrift
,这些都是实现了RPC思想的具体框架和协议。它们可以选择不同的传输层协议(通常是TCP),以及不同的序列化方式(如Protobuf, Avro)。HTTP协议,特别是HTTP/1.1,虽然非常通用,但在一些特定场景下,其性能和开发效率并不理想。RPC的出现,正是为了解决这些痛点。
对比维度 | HTTP (特别是/1.1) | RPC (如 gRPC, Dubbo) |
---|---|---|
1. 定位与应用场景 | “对外”通信,人机交互。主要用于浏览器与服务器之间(B/S),或者作为开放API,需要很好的可读性和通用性。 | “对内”通信,机器间交互。主要用于分布式、微服务架构下,服务与服务之间的高效调用。 |
2. 传输协议与效率 | 通常基于TCP。HTTP/1.1是文本协议,头部信息冗余,性能较低。 | 通常也基于TCP。但其上层协议通常是自定义的、高度优化的二进制协议。传输效率远高于HTTP/1.1。 |
3. 序列化方式 | 通常是JSON或XML等文本格式。可读性好,但性能差,序列化/反序列化开销大,体积也大。 | 通常使用Protobuf, Avro, Kryo等高性能的二进制序列化框架。性能极高,体积小,但可读性差。 |
4. 开发效率与服务治理 | HTTP接口通常需要手动定义URL、参数、返回值,并编写文档。服务治理能力(如服务发现、负载均衡、熔断)需要借助额外组件(如Spring Cloud)实现。 | RPC框架通常与IDL(接口定义语言)配合,可以自动生成客户端和服务端的代码桩(stub),开发体验非常流畅。并且,像Dubbo, gRPC这样的框架,原生就集成了丰富的服务治理能力。 |
总结一下,在我的技术选型中:
HTTP和RPC将会在各自最擅长的领域,长期地共存和发展下去。
面试官您好,HTTP长连接和WebSocket虽然都是基于TCP协议的、并且都能保持长期的连接,但它们在通信模式、协议开销和应用场景上,有着根本性的区别。
我们可以把它们比作两种不同的 “对讲机”:
这是两者最本质的区别。
HTTP/1.1长连接:
WebSocket:
HTTP长连接:
WebSocket:
Upgrade: websocket
和Connection: Upgrade
等字段,明确告诉服务器:“我不仅仅是一个HTTP请求,我想把这个连接升级成WebSocket连接。”HTTP长连接:
WebSocket:
特性 | HTTP长连接 | WebSocket |
---|---|---|
通信模式 | 半双工 (请求-响应) | 全双工 (双向实时) |
服务器主动推送 | 否 | 是 |
连接建立 | TCP握手 | TCP握手 + HTTP升级握手 |
协议开销 | 每次请求都有HTTP头,较大 | 只有握手时有,后续帧头极小 |
基于这些区别,它们的适用场景非常明确:
HTTP长连接:
WebSocket:
面试官您好,Nginx作为业界最主流的高性能反向代理和负载均衡器,它提供了非常丰富和灵活的负载均衡算法(或称为策略),以适应不同的业务场景。
我通常会把这些算法分为两大类:Nginx内置的核心算法和需要额外配置或第三方模块支持的扩展算法。
这些是Nginx开箱即用的、最基础也是最常用的算法。
1. 轮询 (Round Robin) —— 默认策略
upstream
中配置的服务器列表顺序,将接收到的请求依次地、循环地分配给后端的每一台服务器。2. 加权轮询 (Weighted Round Robin)
weight
)。权重越高的服务器,被分配到请求的概率就越大。upstream backend {
server backend1.example.com weight=3;
server backend2.example.com weight=1;
}
3. IP哈希 (IP Hash)
4. 最少连接 (Least Connections)
5. URL哈希 (URL Hash) / 一致性哈希
ngx_http_upstream_hash_module
)实现的。它根据请求的URL进行哈希,然后将同一URL的请求,固定地分配给某一台后端服务器。6. 最短响应时间 (Least Time)
总结一下,在我的实践中:
面试官您好,Nginx工作在OSI七层网络模型中的最高层——第七层,即应用层。
也正因为如此,我们通常称Nginx为 “七层负载均衡器” 或 “应用层负载均衡器”。
这个定位,是由Nginx的工作能力决定的。它之所以能工作在应用层,是因为它能够完全理解和解析应用层协议的内容,特别是我们最常用的HTTP协议。
一个生动的比喻:
Nginx能做什么?
/api/
的请求转发给A服务,将/static/
的请求转发给B服务。User-Agent
头,将移动端和PC端的请求分发到不同的后端。这些操作,都必须在理解了HTTP协议内容的基础上才能完成,因此,Nginx是名副其实的应用层设备。
通过与四层负载均衡的对比,我们可以更清晰地看到Nginx的定位:
特性 | 四层负载均衡 (如LVS, F5) | 七层负载均衡 (如Nginx, HAProxy) |
---|---|---|
工作层次 | 传输层 (L4) | 应用层 (L7) |
理解内容 | 只解析IP地址和端口号 | 能完整解析HTTP/HTTPS等应用层协议 |
转发方式 | 基于IP+Port进行转发,性能极高 | 基于URL、HTTP头、Cookie等应用层信息进行转发 |
功能 | 主要是流量分发 | 流量分发、内容路由、反向代理、静态资源服务、安全过滤等 |
性能 | 更高(因为逻辑简单,不涉及应用层解析) | 相对较低(但对于绝大多数场景已足够快) |
灵活性 | 较低 | 极高 |
所以,Nginx凭借其在应用层的强大处理能力,不仅仅是一个负载均衡器,更是一个功能丰富的Web服务器、反向代理服务器、以及API网关。它通过理解HTTP协议,为我们提供了极其灵活和强大的流量控制与内容路由能力,是现代Web架构中不可或缺的核心组件。
参考小林 coding