Web 开发中必须要了解的 HTTP 相关知识

发布于 2022-07-12 15:19:53 字数 19443 浏览 1032 评论 0

本文主要记录与HTTP相关的具体概念和知识,关于 HTTP 协议的诞生和历史发展,不多做介绍,自己但是既然是写HTTP,顺带说两句,上下文也能衔接的上。

CERN(欧洲核子研究组织)的蒂姆 • 伯纳斯 - 李(Tim BernersLee)博士提出了一种能让远隔两地的研究者们共享知识的设想,于是 HTTP 慢慢的诞生了。

另外,HTTP 协议是无状态可以,于是为了保存用户的状态,cookie 诞生了。

HTTP 协议是建立在 TCP 连接之上的,当浏览器输入 URL 进行访问,浏览器冲 URL 中解析出主机名和端口,浏览器建立一条与 Web 服务器的连接,然后才进行 http 请求。

TCP连接的建立与终止

建立TCP连接(三次握手)

在客户端与服务端进行 http 通信之前,需要建立 TCP 连接,这时需要三次握手

  1. 请求新的 TCP 连接,客户端发送一个小的 TCP 分组,这个分组设置一个特殊的SYN标记,表明是一个客户端请求。
  2. 如果服务器接受这个连接,就会对一些连接参数进行计算,并向客户端回送一个 TCP 分组,发送 SY 和 ACK 标记,表明连接请求已经被接受
  3. 最后,客户端向服务器回送一条确认消息,通知服务器连接已经建立。

HTTP-三次握手

断开 TCP 连接(四次断开)

建立一个连接需要三次握手,而终止一个连接要经过4次握手。这由TCP的半关闭(half-close)造成的。既然一个TCP连接是全双工(即数据在两个方向上能同时传递),因此每个方向必须单独地进行关闭。这原则就是当一方完成它的数据发送任务后就能发送一个FIN来终止这个方向连接。当一端收到一个FIN,它必须通知应用层另一端几经终止了那个方向的数据传送。发送FIN通常是应用层进行关闭的结果。

  1. 客户端发送 FIN 标记到服务器,表明客户端发起关闭连接
  2. 服务器接收客户端的 FIN 标记并,向客户端发送 FIN 的 ACK 确认标记
  3. 服务器发送FIN到客户端,服务器关闭连接
  4. 服务器端发送一个 FIN 的 ACK 确认标记,确认连接关闭

HTTP-四次分手

建立持久连接的请求和响应交互:

使用 wireshark 进行数据抓包:

这里向大家推荐一款抓包软件 Wireshark,可以用来分析 TCP 连接的建立和断开过程,以及抓取 HTTP 请求和相应的信息等,下面是我进行一次客户端和服务端通信的抓包数据截图:

HTTP 报文

HTTP 协议报文是应用程序之间发送的数据块,也就是客户端和服务端用于交互的信息。客户端的报文叫做请求报文,服务器端的报文叫做响应报文。

HTTP 报文组成

HTTP报文由起始行、首部和实体的主体(也称报文主体或主体)组成。起始行和首部以一个回车符和换行符作为结束,主体部分可以是二进制数据,也可以为空。

HTTP报文组成

1. 起始行

请求报文起始行:

请求报文起始行说明了要做什么,由请求方法 、请求 URI 和协议版本构成。

GET /index.html HTTP/1.1

响应报文起始行:

响应报文的起始行,由协议版本、状态码和原因短语构成。

HTTP/1.1 200 OK   // OK就是原因短语

HTTP-请求-响应报文

2. 首部

首部字段分类

1. 通用首部

客户端和服务端都可以使用的首部

通用首部字段表:

通用首部字段

2. 请求首部

请求报文特有的首部,为服务器提供了一些额外的信息,补充了请求的附加内容、客户端信息、响应内容相关的优先级等信息。

请求首部字段
请求首部字段

3. 响应首部

响应报文特有的字段

响应首部字段表:

响应首部字段

4. 实体首部

用于针对请求报文和响应报文主体部分使用的首部

实体首部字段

5. 扩展首部

扩展首部是非标准的首部,由应用程序开发者创建,但还未添加到已批准的HTTP标准中去。

HTTP 状态码

状态码的职责是当客户端向服务器端发送请求时,描述返回的请求结果。借助状态码,用户可以知道服务器端是正常处理了请求,还是出现了错误。

状态码分类:

状态码区间类别
100~199信息性状态码
200~299成功状态码
300~399重定向状态码
400~499客户端错误状态码
500~599服务器错误状态码

常用状态码列表:

状态码原因短语含义
200OK表示从客户端发来的请求在服务器端被正常处理了
204No Content该状态码代表服务器接收的请求已成功处理,但在返回的响应报文中不含实体的主体部分。另外,也不允许返回任何实体的主体。
301Moved Permanently永久重定向,该状态码表示请求的资源已被分配了新的 URI,以后应使用资源现在所指的 URI
302Found临时性重定向,该状态码表示请求的资源已被分配了新的 URI,希望用户(本次)能使用新的 URI 访问
303See Other303 状态码和 302 Found 状态码有着相同的功能,但 303 状态码明确表示客户端应当采用 GET 方法获取资源,这点与 302 状态码有区别
304Not Modified缓存
307Temporary Redirect临时重定向,和302一样
400Bad Request该状态码表示请求报文中存在语法错误。当错误发生时,需修改请求的内容后再次发送请求。另外,浏览器会像 200 OK 一样对待该状态码
401Unauthorized该状态码表示发送的请求需要有通过 HTTP 认证(BASIC 认证、DIGEST 认证)的认证信息
403Forbidden该状态码表明对请求资源的访问被服务器拒绝了
404Not Found该状态码表明服务器上无法找到请求的资源
500Internal Server Error该状态码表明服务器端在执行请求时发生了错误。也有可能是 Web应用存在的 bug 或某些临时的故障
502Bad Gateway网关错误
503Service Unavailable该状态码表明服务器暂时处于超负载或正在进行停机维护,现在无法处理请求。如果事先得知解除以上状况需要的时间,最好写入RetryAfter 首部字段再返回给客户端

HTTP中不同场景下,首部字段的作用

1. CORS 跨域资源共享

跨域资源共享(CORS)是一种机制,它使用额外的 HTTP 头来告诉浏览器 让运行在一个 origin (domain) 上的Web应用被准许访问来自不同源服务器上的指定的资源。当一个资源从与该资源本身所在的服务器不同的域、协议或端口请求一个资源时,资源会发起一个跨域 HTTP 请求。

下面使用 nodejs 来搭建一个简单的服务器,来介绍一个跨域问题的解决方法

// index.html

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8" />
  <title>CORS</title>
</head>
<body>
  Hello World
<script>
  fetch('http://127.0.0.1:8081')
</script>
</body>
</html>
// server.js

const http = require('http')

http.createServer(function(req, res) {
  res.writeHead('200', {   
  'Access-Control-Allow-Origin': 'http://localhost:8082'
  })
}).listen(8081)

在源地址为 http://localhost:8082 下,请求http://localhost:8081,是跨域请求,浏览器会自动在 request Header 中发送 Origin 首部字段,并把值设置为来自哪个源,本例为 http://localhost:8081。

服务器需要在响应头中设置 Access-Control-Allow-Origin,来告知浏览器可以处理返回的数据。如果响应头中不设置 Access-Control-Allow-Origin 则会报错,但是返回状态码为200,跨域实际上是浏览器本身的一个安全机制。

// server2.js
// 启动8082端口服务,在浏览器中访问http://127.0.0.1:8082,会返回index.html内容

const http = require('http')
const fs = require('fs')
http.createServer(function(req, res) {
  var page = fs.readFileSync('index.html', 'utf-8')
  res.writeHead(200, {
  'Content-Type': 'text/html'
  })
  res.end(page)
}).listen(8082)

关于CORS跨域请求的分类:

1.简单请求:

需要同时满足以下的条件就是简单请求

1、请求方法:

GET、POST、HEAD

2、请求头不能为以下其他字段之外

Accept
Accept-Language
Content-Language
Content-Type的值必须为application/x-www-form-urlencoded、multipart/form-data、text/plain之一

2.非简单请求:

非简单请求是当请求信息不满足简单请求的条件,浏览器就发送方法为OPTIONS的预请求,包含自己请求的方法及需要使用的请求头字段,在得到服务器响应允许之后,浏览器会按照想要使用的请求方法及头信息再发一次请求。

现在修改以下上面的例子:

// index.html

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8" />
  <title>CORS</title>
</head>
<body>
  Hello World
<script>
  fetch('http://127.0.0.1:8081', {
  method: 'PUT',
  headers: {
    X-Coustom-Head: 'abc'
  }
  })
</script>
</body>
</html>
// server.js

const http = require('http')

http.createServer(function(req, res) {
  res.writeHead('200', {   
  'Access-Control-Allow-Origin': 'http://localhost:8082'
  })
}).listen(8081)

如果服务端不进行相应的设置告诉浏览器允许跨域访问则会报错

但是预请求返回状态码为 200

// server2.js
// 启动8082端口服务,在浏览器中访问http://127.0.0.1:8082,会返回index.html内容

const http = require('http')
const fs = require('fs')
http.createServer(function(req, res) {
  var page = fs.readFileSync('index.html', 'utf-8')
  res.writeHead(200, {
  'Content-Type': 'text/html'
  })
  res.end(page)
}).listen(8082)

现在我们修改以下 server.js

// server.js

const http = require('http')

http.createServer(function(req, res) {
  res.writeHead('200', {   
  'Access-Control-Allow-Origin': 'http://localhost:8082',
  'Access-Control-Allow-Headers': 'X-Coustom-Head',
  'Access-Control-Allow-Methods': 'PUT'
  })
}).listen(8081)

重新启动 node 服务,访问 http://locaohost:8082,可以看到在发送预请求后,浏览器会继续发送 PUT 请求

关于CORS的其他设置这里就不多做介绍了,这里主要是用一个例子来说明以下http不同字段在跨域场景下的作用。

2. 缓存(Cache-Control 的作用)

本例依旧用node服务来讲解一下Cache-Control的作用,新建三个文件

// index.html
<!DOCTYPE html>
<html>

<head>
  <meta charset="utf-8" />
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <title>Cache-Control</title>
  <meta name="viewport" content="width=device-width, initial-scale=1">
</head>

<body>
  <script src="/script.js"></script>
</body>

</html>
// script.js
console.log('script.js')
// server.js
const http = require('http')
const fs = require('fs')


http.createServer(function(req, res) {
  if (req.url === '/') {
  let page = fs.readFileSync('index2.html', 'utf-8')
  res.writeHead(200, {
    'Content-Type': 'text/html'
  })
  res.end(page)
  }

  if (req.url === '/script.js') {
  let page = fs.readFileSync('script.js', 'utf-8')
  res.writeHead(200, {
    'Content-Type': 'text/javascript',
    'Cache-Control': 'max-age=10'
  })
  res.end(page)
  }
}).listen(8082)

在第一次请求 script.js 资源时,向服务器发送请求

由于服务器返回响应时,设置 Cache-Control: 'max-age=10' 时,修改 script.js 后,在 10 秒内继续请求 script.js 资源,则从缓存中读取,而打印信息依旧是 script.js

// script.js
console.log('script-modify.js')

更多关于缓存的知识在这里也不多介绍了,贴两张cache-control字段在请求和响应时可以设置的值和其表示含义:

1. Cache-Control 缓存请求指令:

2. Cache-Control 缓存响应指令:

3. cookie

指某些网站为了辨别用户身份、进行 session 跟踪而储存在用户本地终端上的数据(通常经过加密),当下次再访问时浏览器会将该网站的cookie发回给服务器端。

cookie如果不设置过期时间,随浏览器关闭而失效,如果有需要可以设置过期时间,继续上代码例子🌰,新建两个文件如下

// index.html

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8" />
  <title>Cookie</title>
</head>
<body>
  Cookie
<script>
  console.log(document.cookie)
</script>
</body>
</html>
// server.js

const http = require('http')
const fs = require('fs')

http.createServer(function(req, res) {
  if (req.url === '/') {
  let page = fs.readFileSync('index.html', 'utf-8')
  res.writeHead(200, {
    'Content-Type': 'text/html',
    'Set-Cookie': ['a=1;max-age:5', 'b=2;HTTPOnly']
  })
  res.end(page)
  }
}).listen(8082)

启动 node 服务,访问 localhost:8082,可以看到成功设置了 cookie

并在响应头信息中设置了 Set-Cookie 字段

另外关注以下打印信息,发现只有 a=1,因为给 b=2 设置了 HttpOnly 属性,不允许 JavaScript 通过脚本来获取到 cookie 信息。

由于当再次请求时,cookie 会在请求头中发送到服务器,由于 cookie a=1 设置了 5 秒后过期,在 5 秒后刷新页面,请求头中的 cookie 只有 a=1

在5秒内发送二次请求,cookie a=1 没有失效,在请求头中 cookie a=1;b=2 都会发送到服务器。

另外对于cookie的其他设置如expires、domain等在这里也不多做介绍了

4. 重定向

当服务端返回 301、302、307 等状态码都代表资源已经被重定向到其他位置,301 表示永久改变 URI,302 和 307 表示临时重定向到某个 URI

本例举一个服务器返回 302 状态码的例子,直接上代码:

// server.js

const http = require('http');
const fs = require('fs')

http.createServer((req, res) => {
  if (req.url === '/') {
  res.writeHead(302, {
    'Location': '/redirect'
  })
  res.end()
  }

  if (req.url === '/redirect') {
  res.end('redirect')
  }
}).listen(8082);

访问 localhost:8082,服务器返回 302 状态码时,在相应头中设置 Location 首部字段,浏览器会继续发送请求到重定向的地址

HTTP 与 HTTPS 的区别

首先说一下什么是 HTTPS

HTTPS(全称:Hyper Text Transfer Protocol over Secure Socket Layer 或 Hypertext Transfer Protocol Secure,超文本传输安全协议),是以安全为目标的 HTTP 通道,简单讲是 HTTP 的安全版。即HTTP下加入SSL层,HTTPS的安全基础是SSL,因此加密的详细内容就需要SSL。

HTTPS = HTTP+ 加密 + 认证 + 完整性保护

最主要是在应用层和传输层中间加了一个SSL(安全套阶层),通常,HTTP 直接和 TCP 通信。当使用 SSL 时,则演变成先和 SSL 通信,再由 SSL 和 TCP 通信。

HTTP 与 HTTPS 的区别:

  1. HTTP是明文传输,HTTPS 是经过 SSL 加密后进行传输,只有客户端和服务端根据公钥和私钥进行加密和解密能看到,中间任何传输环节无法获取传输信息,所以 HTTPS 比 HTTP 安全
  2. HTTPS需要到数字证书认证机构进行购买
  3. HTTP服务器默认端口是 80,HTTPS服务器默认端口是 443

本文主要介绍 HTTP,关于 HTTPS 主要就介绍这么多吧。

HTTP2

本想说点 HTTP2 的知识,奈何自己是小白,放个百度百科的链接:HTTP2

等后续随着不断的学习,再回来更新本文。

另外放一个 HTTP1.1 与 HTTP2 请求与相应对比的 demo 的链接 HTTP/2 is the future of the Web, and it is here!

最后,本文主要介绍了一些HTTP在web开发中的基础知识,关于概念和图解流程的截图基本上都是来自《TCP/IP详解 卷1:协议》《图解HTTP》《HTTP权威指南》,可放心参考。

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据

关于作者

0 文章
0 评论
25 人气
更多

推荐作者

醉城メ夜风

文章 0 评论 0

远昼

文章 0 评论 0

平生欢

文章 0 评论 0

微凉

文章 0 评论 0

Honwey

文章 0 评论 0

qq_ikhFfg

文章 0 评论 0

    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文