1. http
1.1 什么是http:
http,即超文本传输协议,是TCP/IP协议集中的一个应用层协议,是客户端与服务端进行交互时必须遵循的规则。它用于Web浏览器与Web服务器之间交换数据的过程以及数据本身的格式,底层则是靠TCP进行可靠地信息传输。
附:除了http,我们经常能够看见https。https不是http的复数,而是在http的基础上进行了加密工作,具体在后文进行介绍。
1.2 http如何工作:
在浏览器的上方,有一个地址栏。如果我们在其中输入一个URL,浏览器就会给对应的服务器发送一个http请求,当服务器收到这个请求之后,就会进行处理,然后返回一个http响应。那么,具体的过程是怎样的呢?
- 建立连接:客户端(如Web浏览器)与服务器建立连接。这个连接是通过TCP协议完成的,期间经过了三次握手。HTTP协议是短连接的,每次请求都需要建立一个新的连接。(http/1.1后又引入了持久连接Keep-Alive,通过复用TCP连接来发送多个http请求和接受多个响应)
- 发送请求:客户端向服务器发送请求。请求的格式包括请求行、请求头和请求体。请求行包含请求方法(如GET、POST)、请求的URL和HTTP版本。请求头包含一些附加信息,如主机、用户代理等。请求体则包含实际的数据。
- 服务器响应:服务器接收到请求后,进行处理并返回响应。响应的格式包括状态行、响应头和响应体。状态行包含HTTP版本、状态码和状态描述。响应头包含一些附加信息,如服务器类型、内容类型等。响应体则包含实际的数据,如HTML文档。
- 关闭连接:服务器发送完响应后,关闭连接。HTTP是无状态的协议,这意味着每次请求和响应都是独立的,服务器不会保留连接的状态信息。
具体如下:
1 | GET /index.html HTTP/1.1 |
GET /index.html HTTP/1.1
:请求方法+资源路径+HTTP 版本Host: www.example.com
:目标服务器地址User-Agent: Mozilla/5.0 ...
:客户端类型(浏览器、操作系统等)Accept: text/html,...
:客户端可接受的数据类型Accept-Encoding: gzip,...
:支持的压缩格式Connection: keep-alive
:连接方式,是否保持连接
1 | HTTP/1.1 200 OK |
HTTP/1.1 200 OK
:HTTP 版本 + 状态码 + 状态信息Date: Wed, 19 Mar 2025 12:00:00 GMT
:响应时间Server: Apache/2.4.41 (Ubuntu)
:服务器信息Content-Length: 1024
:响应体大小(字节数)Connection: keep-alive
:连接方式,支持长连接响应体
:<html>...</html>
是服务器返回的 HTML 页面
**短连接:HTTP/1.0 及默认情况,请求完成后关闭连接:
1 | Connection: close |
长连接:HTTP/1.1 及以上默认启用长连接:
1 | Connection: keep-alive |
省流就是:一发一收,一问一答。
当然,在http/2中,还可以实现其他的模式:
当然都是后话了,等我下次再讲(又可以水一期分享了😋)
1.3 http的方法:
1. GET
作用:请求指定的资源。GET请求通常用于获取数据,而不会对资源产生副作用。
特点:
- 请求的参数附加在URL中(查询字符串)。
- 可以被缓存。
- 不应修改服务器上的资源。
- 幂等性
示例:
1
2GET /index.html HTTP/1.1
Host: www.example.com常见用途:
- 获取网页内容。
- 查询数据库记录。
2. POST
作用:向服务器提交数据,通常用于创建新资源或触发服务器上的处理操作。
特点:
- 请求的参数包含在请求体中。
- 不会被缓存。
- 可能会修改服务器上的资源。
- 不具有幂等性。
示例:
1
2
3
4
5POST /submit-form HTTP/1.1
Host: www.example.com
Content-Type: application/x-www-form-urlencoded
username=🫚&password=123456常见用途:
- 提交表单数据。
- 上传文件。
- 创建新资源(如用户注册)。
3. PUT
作用:更新指定资源。如果资源不存在,则创建新资源。
特点:
- 请求的参数包含在请求体中。
- 幂等性。
示例:
1
2
3
4
5PUT /users/123 HTTP/1.1
Host: www.example.com
Content-Type: application/json
{"name": "🦌🐟⚽", "password": 159357}常见用途:
- 更新用户信息。
- 替换现有资源。
4. DELETE
作用:删除指定的资源。
特点:
- 幂等性。
- 请求通常没有请求体。
示例:
1
2DELETE /users/123 HTTP/1.1
Host: www.example.com常见用途:
- 删除用户。
- 删除文件。
5. HEAD
作用:请求资源的元信息,而不返回实际内容。
特点:
- 响应中没有响应体。
- 可以用于检查资源是否存在或获取资源的元信息(如大小、类型)。
示例:
1
2HEAD /index.html HTTP/1.1
Host: www.example.com常见用途:
- 检查资源是否更新(通过
Last-Modified
或ETag
)。 - 获取资源的大小和类型。
- 检查资源是否更新(通过
6. OPTIONS
作用:获取服务器支持的HTTP方法或其他选项。
特点:
- 响应中包含
Allow
头部,列出支持的HTTP方法。 - 幂等性
- 响应中包含
示例:
1
2OPTIONS /index.html HTTP/1.1
Host: www.example.com常见用途:
- 检查服务器支持的请求方法。
- 跨域请求时,用于预检请求(Preflight Request)。
7. TRACE
作用:用于诊断或测试,服务器会将收到的请求原样返回,以便客户端检查请求是否被篡改。
特点:
- 响应中包含客户端发送的请求信息。
- 通常用于调试。
- 幂等性
示例:
1
2TRACE /index.html HTTP/1.1
Host: www.example.com常见用途:
- 检查请求在传输过程中是否被修改。
8. CONNECT
作用:用于建立与服务器的隧道连接,通常用于HTTPS代理。
特点:
- 请求中包含目标服务器的地址和端口。
- 用于客户端通过代理服务器与目标服务器建立连接。
示例:
1
2CONNECT www.example2.com:443 HTTP/1.1
Host: www.example1.com常见用途:
- 通过代理服务器建立HTTPS连接。
附图如下:
幂等性:在忽略错误或过期问题的情况下,多次相同请求的副作用与一次请求的副作用相同。总结就是多次执行后效果相同,不存在副作用。而post每次请求都回创建新的资源,因此是不幂等的。
1.4 http的状态码
1xx(信息性状态码):表示请求已被接收,继续处理。
2xx(成功):
- 200 OK:请求成功。
- 201 Created:资源已创建。
- 204 No Content:成功但无响应体。
- **206 Partial Content :**范围请求成功
3xx(重定向):
- 301 Moved Permanently:永久重定向(资源被分配了新的URI)。
- 302 Found:临时重定向(资源被分配了新的URI,但是未来还有可能改变)。
- 304 Not Modified:允许访问资源,但未满足条件。
4xx(客户端错误):
- 400 Bad Request:请求语法错误。
- 401 Unauthorized:未认证。
- 403 Forbidden:无权限。
- 404 Not Found:资源不存在。
5xx(服务器错误):
- 500 Internal Server Error:服务器内部错误。
- 502 Bad Gateway:网关错误。
- 503 Service Unavailable:服务不可用(如武器超负载或正在维护)。
1.5 http的不足:
1.无状态性
问题:
HTTP是无状态协议,服务器不会记住客户端的状态。每次请求都是独立的,服务器不会保留之前的请求信息。
影响:
- 增加了开发和维护的复杂性。
- 需要额外的机制(如Cookie、Session,补充在附录)来管理状态。
改进:
- 使用Cookie或Session来管理状态。
- 使用Token进行无状态认证。
2.安全问题
问题:
- 通信采用明文,内容可能被窃听
- 不验证通信双方的身份,可能遭遇伪装
- 无法验证明文的完整性,有可能遭到篡改
改进:
诞生了https,使用https(http over SSL/TLS)加密传输数据。
(https见附录)
3. 头部冗余
问题:
HTTP请求和响应的头部信息是文本格式的,且每次请求都会发送完整的头部信息,导致冗余。
改进:
http/2引入了头部压缩(HPACK),减少了头部信息的冗余。
4. 不支持服务器推送
问题:
HTTP/1.x不支持服务器主动推送数据,客户端必须通过轮询或长轮询来获取更新,导致实时性差,增加了客户端的负担。
改进:
HTTP/2支持服务器推送(Server Push),服务器可以主动向客户端推送资源。
附:
轮询:客户端每隔一段时间向服务器发送http请求,服务器在收到请求后不论是否有数据更新,都直接 进行响应。
长轮询:客户端向服务器发起请求,如果服务器没有数据更新,就把请求挂住,直到服务端的数据发生 了更新,或者超时便会返回。
5. 性能问题
问题:
短连接:HTTP/1.0默认是短连接的,每次请求都需要重新建立和关闭连接,增加了延迟和资源消耗。
队头阻塞:HTTP/1.1虽然支持持久连接,但仍然存在队头阻塞问题(一个请求的延迟会影响后续请求)。
改进:
使用HTTP/2的多路复用技术,允许在同一个连接上同时发送多个请求和接收多个响应。
使用HTTP/3的QUIC协议,进一步优化性能。
除了上面这么多,http还有很多不足,比如缺乏优先级,不支持二进制传输,存在跨域限制等问题。
为了解决http的部分不足,便引入了新的协议,其名为WebSocket。
2. WebSocket
2.1 WebSocket
WebSocket 是一种在单个TCP连接上进行全双工通信的协议,允许客户端和服务器之间进行实时、双向的数据传输。它是HTML5规范的一部分,旨在解决HTTP协议在实时通信中的局限性。
附:数据只能单向传送为单工;
数据能双向传送但不能同时双向传送称为半双工;
数据能够同时双向传送则称为全双工。
2.2 WebSocket的优点
WebSocket的优势包括:
- 实时性: 由于WebSocket的持久化连接,它可以实现实时的数据传输,避免了Web应用程序需要不断地发送请求以获取最新数据的情况。
- 双向通信: WebSocket协议支持双向通信,这意味着服务器可以主动向客户端发送数据,而不需要客户端发送请求。
- 减少网络负载: 由于WebSocket的持久化连接,它可以减少HTTP请求的数量,从而减少了网络负载。
2.3WebSocket的工作过程
WebSocket 生命周期描述了 WebSocket 连接从创建到关闭的过程。一个 WebSocket 连接包含以下四个主要阶段:
连接建立阶段:
在连接建立阶段,客户端会基于http协议向客户端发送一个特殊的http(get)请求,之所以特殊,是因为其请求头包含了一些特殊的字段:
1 | GET /chat HTTP/1.1 |
Upgrade:websocket
表示客户端希望协议升级为WebSocketConnection:Upgrade
进一步确认客户端请求协议升级的意图Sec-WebSocket-Key
一个由客户端生成的Base64编码的随机密钥,用于安全认证Sec-WebSocket-Version
用于指定客户端支持的WebSocket版本Origin
:指定请求的来源,服务器用于检查请求是否来自合法的域。
服务器接受到请求后,会对头部字段进行验证。如果验证通过,就会返回一个带有101 Switching Protocols的http响应,表示同意协议升级。
1 | HTTP/1.1 101 Switching Protocols |
101 Switching Protocols
:表示服务器同意将协议升级。Sec-WebSocket-Accept
:服务器通过Sec-WebSocket-Key
和一个特定的算法计算出一个值并返回,这个值是用来验证客户端发起的握手是否合法。
连接开放阶段:
连接建立后,客户端和服务端便可以通过WebSocket进行双向的数据传输。数据以帧(就是数据链路层的那个)的形式进行传输。帧有许多种类型,比如文本帧(传输文本数据),二进制帧(传输二进制数据,比如图片,音频等),ping帧(用于心跳检测),关闭帧(用于关闭连接)。
连接关闭阶段:
如果此时客户端或者服务端需要关闭一个WebSocket连接,便会发送一个关闭帧。关闭帧包含一个状态码和可选的关闭原因,用于对方关闭连接的原因。另一方收到关闭帧后,会响应一个关闭帧,并关闭连接。这样的关闭方式可以保证连接正常终止,避免数据丢失或连接异常。
连接关闭完成阶段:
在这个阶段,WebSocket 连接已经完全关闭。客户端和服务器之间的任何交互都将无效。
如图:
引用自伟大的🫚圣:
2-8 WebSocket的底层逻辑 - 飞书云文档
2.4 WebSocket的API
WebSocket API 是用于在 Web 应用程序中创建和管理 WebSocket 连接的接口集合。WebSocket API 由浏览器原生支持,无需使用额外的 JavaScript 库或框架,可以直接在 JavaScript 中使用。
下面是一些常用的 WebSocket API:
WebSocket 构造函数: WebSocket 构造函数用于创建 WebSocket 对象。它接受一个 URL 作为参数,表示要连接的 WebSocket 服务器的地址。例如:
1 | let ws = new WebSocket('ws://example.com/ws'); |
WebSocket.send() 方法: WebSocket.send()
方法用于向服务器发送数据。它接受一个参数,表示要发送的数据。数据可以是字符串、Blob 对象或 ArrayBuffer 对象(常用于处理二进制)。例如:
1 | ws.send('Hello, server!'); |
WebSocket.onopen 事件: WebSocket.onopen
事件在 WebSocket 连接成功建立时触发。例如:
1 | ws.onopen = function() { |
WebSocket.onmessage 事件: WebSocket.onmessage
事件在接收到服务器发送的消息时触发。它的 event 对象包含一个 data 属性,表示接收到的数据。例如:
1 | ws.onmessage = function(event) { |
WebSocket.onerror 事件: WebSocket.onerror
事件在 WebSocket 连接出现错误时触发。例如:
1 | ws.onerror = function(event) { |
WebSocket.onclose 事件: WebSocket.onclose
事件在 WebSocket 连接被关闭时触发。例如:
1 | ws.onclose = function() { |
以上是一些常用的 WebSocket API。
2.5 WebSocket的不足
- 需要浏览器和服务器都支持: WebSocket是一种相对新的技术,需要浏览器和服务器都支持。一些旧的浏览器和服务器可能不支持WebSocket。
- 需要额外的开销: WebSocket需要在服务器上维护长时间的连接,这需要额外的开销,包括内存和CPU。
- 安全问题: 由于WebSocket允许服务器主动向客户端发送数据,可能会存在安全问题。服务器必须保证只向合法的客户端发送数据。
2.6 WebSokcet的应用场景
- 游戏应用程序
- 聊天应用程序
- 实时股票、外汇、加密货币行情
- 在线协作编辑
附录:
Cookie,Session和Token:
关于Cookie:
Cookie 是存储在客户端浏览器中的小型文本数据,用于在 HTTP 请求之间保持用户状态。由于 HTTP 协议是无状态的,每次请求都是独立的,服务器无法记住客户端的状态,而 Cookie 可以帮助服务器跟踪用户。每个 Cookie 不能超过 4KB,一个站点最多可存储 300 个 Cookie,单个域名最多 20 个 Cookie。
附:JS能够通过document.cookie来访问cookie,从而进行身份冒充访问服务器,但是我们可以通过在Set-Cookie响应头设置HttpOnly来禁止documnet.cookie访问cookie。
当用户访问网站时,服务器会在http的响应头中通过Set-Cookie设置cookie,客户端收到后,会将其存储到本地,之后,每次用户请求该网站,浏览器都会自动在请求头中携带cookie,服务器也就可以通过cookie来判断身份。
关于Session:
Session 是存储在服务器端的用户会话数据,用于记录用户的状态。由于 Cookie 存储在客户端,容易被篡改,而 Session 存储在服务器端,相对更安全。
当用户访问服务器时,服务器创建一个 Session,并生成一个 session_id,然后,服务器返回session_id,并让客户端存储(通常存储在 Cookie),客户端在后续请求时携带 session_id,服务器便会通过session_id来验证身份。
关于Token:
Token是一种身份验证机制,基于无状态(Stateless)认证,不同于 Session,它不需要服务器存储用户信息,而是通过签名机制来验证数据的完整性,JWT(JSON Web Token) 是一种常见的 token 格式。
用户登录时,服务器会生成token返回给客户端,客户端可以来存储这个token(比如localStorage.setItem()),后续每次请求时,客户端可以在Authorization中携带token来让服务器验证,如果有效,解析用户信息,否则拒绝请求
附:
单一token存在一些问题,比如无法安全地延长登陆状态,要么强制用户重新登录,要么一直有效。如果是前者,对于用户体验不太好;如果是后者,一旦token泄露,攻击者可以长期使用。
因此,现在大多采用长短token机制。
长短token分为短期token和长token:
短token有效期较短,通常用于身份验证和授权操作等敏感操作。由于其生命周期较短,即使被盗用,利用时间也有限,从而降低了安全风险。
长token有效期较长,通常用于用户在一段时间内保持登录状态,避免频繁登录。即使其生命周期较长,但由于不直接进行敏感操作,安全风险较低。
用户登录时,服务端会生成一个长token和短token给用户,用户请求资源时,短token用于验证身份。如果短token过期,用户可以通过长token来请求一个新的短token,而无需重新登录。
区别:
Cookie-Session适用于传统的Web应用,前端代码和后端代码在同一个服务器,浏览器直接从后端加载页面,浏览器会自动携带Cookie或Session_id,因为前端和后端是同一个域名,因此通常不支持跨域。
Token适用于前后端分离的项目,前端和后端分开,各自运行在独立的服务器,前端通过Ajax/Fetch请求后端API,此时存在跨域,浏览器不会自动携带Cookie或Session_id,因此适用Token认证。
https:
什么是https:
https,即超文本安全传输协议,它在HTTP的基础上增加了加密和认证机制,以保护数据的机密性、完整性和真实性。
相较于http,https通过TLS修正了http存在的安全问题:
1. 加密传输
- HTTP:数据以明文形式传输,容易被窃听或篡改。
- HTTPS:通过SSL/TLS协议对数据进行加密,确保传输过程中的安全性。
加密机制:
共享密钥加密:
加密和解密共用同一个密钥的方式叫做共享密钥加密,也叫做对称密钥加密。在加密时,用户使用一把密钥进行加密,之后将密钥和加密后的数据发送给另一方,用于解密。但问题又来了,任何人只要有密钥就能解密,如果我的通信被监听,密钥不就落入他人之手了吗?所以传输时还得设法安全保管密钥。
公开密钥加密:
两名用户各有两把钥匙,一把用于加密,是公开的;一把用于解密,是私密的。在加密时,我可以使用对方公开的钥匙加密,然后发送给对方,对方使用自己私密的密钥进行解密,从而保证通信的安全性。但是,公开密钥加密在传输时由于非对称计算较为复杂,导致效率不如前者,不适合传输大量数据。
混合密钥加密:
顾名思义,融合了前两种方法。具体过程是使用公开钥匙加密安全地交换在共享密钥中要使用的密钥,然后使用共享密钥加密的方式进行通信。
2. 数据完整性
- HTTP:数据在传输过程中可能被篡改,无法保证完整性。
- HTTPS:通过消息认证码(MAC)或数字签名确保数据的完整性,防止数据被篡改。
完整性保护:
消息认证码(MAC)
MAC用于验证数据完整性和真实性,确保数据在传输过程中没有被篡改,而且来自可信的发送方。其原理用到了上面的密钥。
发送方和接受方共享一个密钥。生成时,发送方使用密钥和哈希算法对数据计算MAC,然后把数据和MAC一起发给接收方,接收方收到数据后,使用相同的密钥和哈希算法计算MAC,如果相同,则数据完整且来自可信的发送方;如果不同,则数据可能被篡改或来自不可信的发送方。
数字签名
数字签名和MAC大致相同,区别在于数字签名使用的是非对称的公开密钥加密。
3. 身份认证
- HTTP:无法验证服务器的身份,可能存在中间人攻击。
- HTTPS:通过数字证书验证服务器的身份,确保客户端与真实的服务器通信。
数字证书:
数字证书是身份认证的核心,它包含了服务器的身份信息和公钥,并由可信的证书颁发机构(CA)签名。
证书颁发机构(CA)
证书颁发机构(CA)是受信任的第三方机构,负责验证服务器的身份并颁发数字证书。
数字证书内容
数字证书通常包含以下信息:
- 服务器的公钥:用于加密通信。
- 服务器的域名:证书绑定的域名(如
www.example.com
)。 - 证书的有效期:证书的生效日期和过期日期。
- 颁发机构的信息:CA 的名称和标识。
- 颁发机构的数字签名:CA 对证书内容的签名,用于验证证书的真实性。
证书验证流程
首先,服务器在TLS/SSL 握手过程中将自己的数字证书发送给客户端。
然后,客户端使用 CA 的公钥解密证书中的签名,得到证书的哈希值。
客户端对证书内容计算哈希值,并与解密得到的哈希值进行比较。
1. 如果一致,则证书真实可信。
2. 如果不一致,则证书可能被篡改或伪造。
再然后,客户端检查证书中的域名是否与访问的域名一致。
1. 如果一致,则域名验证通过。
2. 如果不一致,则可能是域名不匹配或证书被滥用。
之后,客户端检查证书是否在有效期内。
1. 如果证书在有效期内,则有效期验证通过。
2. 如果证书已过期,则客户端会拒绝连接。
最后,客户端检查证书是否被吊销(如通过 CRL 或 OCSP)。
1. 如果证书未被吊销,则吊销状态验证通过。
2. 如果证书已被吊销,则客户端会拒绝连接。
所有验证全部通过,通过,通过,通过,通过之后,客户端就成功验证了服务器的身份,可以确认服务器是可信的,并与之建立安全的 HTTPS 连接。
附:
自签名证书是由服务器自己生成并签名的数字证书,而不是由受信任的第三方证书颁发机构签发的。换句话说,服务器既是证书的申请者,也是证书的签发者,就像你向老师交作业自己给自己签字一样。
GET和POST:
GET与POST的一些区别:
- GET习惯上把客户端的数据通过query string来传输,而POST习惯上通过body来传输。
- GET习惯上从服务器获取数据,POST习惯上是客户端给服务器上传数据
- GET请求具有幂等性,POST请求不具有幂等性。
- GET请求可以被缓存,而POST请求不能被缓存。
GET和POST的职能:
从理论上讲,GET 和 POST 请求虽然有各自的语义和推荐用途,但在一定条件下,它们的功能是可以交换的,即一个请求方法可以用于另一个请求方法的传统功能。
理论上,可以通过 GET 请求来提交数据(比如在 URL 中传递参数),这相当于在某些情况下 模拟 POST 请求,甚至可以直接带上body数据。
1 | fetch('http://example.com/api/test', { |
理论上,你也可以使用 POST 请求来获取数据,例如,使用 POST 请求提交一个查询条件(将条件放在请求体中),服务器根据这些条件返回结果。
1 | fetch('http://example.com/api/test?param1=value1¶m2=value2', { |
然而,这些做法通常不符合 HTTP 协议的最佳实践,并且可能带来一些问题和副作用。比如在 GET 请求中包含 body,很多实现会忽略 body 数据,因为body的语义未定义。
关于GET请求的URL长度:
反正都杂谈了,顺便说点小垃圾只是吧。
网上有一种说法是说GET请求的长度存在上限,但实际上RFC 2616或者RFC 2396,都没有具体设定URL的长度上限,之所以实际开发中有上限,是因为浏览器为了确保性能、安全和兼容性对其做出上限;又或者服务器为了防止过长的请求导致资源消耗过大或引发安全问题而限制长度。
关于POST比GET更加安全:
其实我开始也觉得POST比GET更加安全,因为GET把数据放在query string上,从URL中就能看到用户名和密码了;而POST把数据放在body中,登录时看不到,很安全😋。
实际上,通过F12开发者工具或者Fiddler(我还没搞懂,搞懂再水一期😋)一抓包就原形毕露了。
Socket.IO:
什么是Socket.IO:
Socket.IO 是一个用于 实时、双向、基于事件通信 的 JavaScript 库,适用于浏览器和 Node.js。它在 WebSockets 之上进行了一层封装,并提供了自动降级机制(如 HTTP 长轮询),确保在不同网络环境下都能实现稳定的实时通信。
Socket.IO的优点:
兼容性更好
WebSocket 仅适用于 支持 WebSocket 的浏览器,而 Socket.IO 可以在所有环境中运行,即使浏览器不支持 WebSocket,它也会自动降级到 XHR 轮询 或 JSONP 轮询,保证通信稳定性。
附:
XHR 轮询:传统的AJAX轮询,每隔一段时间(如 1 秒)向服务器发送请求,检查是否有新消息,但是频繁请求可 能会增加服务器负担。
JSONP 轮询:利用