本文已同步到 github, web 开发中,必须要了解的 HTTP 相关知识, 欢迎收藏,欢迎 start。
本文主要记录与 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) 最后,客户端向服务器回送一条确认消息,通知服务器连接已经建立。
断开 TCP 连接(四次断开)
建立一个连接需要三次握手,而终止一个连接要经过 4 次握手。这由 TCP 的半关闭(half-close)造成的。既然一个 TCP 连接是全双工(即数据在两个方向上能同时传递),因此每个方向必须单独地进行关闭。这原则就是当一方完成它的数据发送任务后就能发送一个 FIN 来终止这个方向连接。当一端收到一个 FIN,它必须通知应用层另一端几经终止了那个方向的数据传送。发送 FIN 通常是应用层进行关闭的结果。
(1) 客户端发送 FIN 标记到服务器,表明客户端发起关闭连接
(2) 服务器接收客户端的 FIN 标记并,向客户端发送 FIN 的 ACK 确认标记
(3) 服务器发送 FIN 到客户端,服务器关闭连接
(4) 服务器端发送一个 FIN 的 ACK 确认标记,确认连接关闭
建立持久连接的请求和响应交互:
使用 wireshark 进行数据抓包:
这里向大家推荐一款抓包软件 Wireshark,可以用来分析 TCP 连接的建立和断开过程,以及抓取 HTTP 请求和相应的信息等,下面是我进行一次客户端和服务端通信的抓包数据截图:
HTTP 报文
HTTP 协议报文是应用程序之间发送的数据块,也就是客户端和服务端用于交互的信息。客户端的报文叫做请求报文,服务器端的报文叫做响应报文。
HTTP 报文组成
HTTP 报文由起始行、首部和实体的主体 (也称报文主体或主体) 组成。起始行和首部以一个回车符和换行符作为结束,主体部分可以是二进制数据,也可以为空。
1. 起始行
请求报文起始行:
请求报文起始行说明了要做什么,由请求方法、请求 URI 和协议版本构成。
GET /index.html HTTP/1.1
响应报文起始行:
响应报文的起始行,由协议版本、状态码和原因短语构成。
HTTP/1.1 200 OK // OK 就是原因短语
2. 首部
首部字段分类
1. 通用首部
客户端和服务端都可以使用的首部
通用首部字段表:
2. 请求首部
请求报文特有的首部,为服务器提供了一些额外的信息,补充了请求的附加内容、客户端信息、响应内容相关的优先级等信息。
请求首部字段
3. 响应首部
响应报文特有的字段
响应首部字段表:
4. 实体首部
用于针对请求报文和响应报文主体部分使用的首部
5. 扩展首部
扩展首部是非标准的首部,由应用程序开发者创建,但还未添加到已批准的 HTTP 标准中去。
http 状态码
状态码的职责是当客户端向服务器端发送请求时,描述返回的请求结果。借助状态码,用户可以知道服务器端是正常处理了请求,还是出现了错误。
状态码分类:
状态码区间
类别
100~199
信息性状态码
200~299
成功状态码
300~399
重定向状态码
400~499
客户端错误状态码
500~599
服务器错误状态码
常用状态码列表:
状态码
原因短语
含义
200
OK
表示从客户端发来的请求在服务器端被正常处理了
204
No Content
该状态码代表服务器接收的请求已成功处理,但在返回的响应报文中不含实体的主体部分。另外,也不允许返回任何实体的主体。
301
Moved Permanently
永久重定向,该状态码表示请求的资源已被分配了新的 URI,以后应使用资源现在所指的 URI
302
Found
临时性重定向,该状态码表示请求的资源已被分配了新的 URI,希望用户(本次)能使用新的 URI 访问
303
See Other
303 状态码和 302 Found 状态码有着相同的功能,但 303 状态码明确表示客户端应当采用 GET 方法获取资源,这点与 302 状态码有区别
304
Not Modified
缓存
307
Temporary Redirect
临时重定向,和 302 一样
400
Bad Request
该状态码表示请求报文中存在语法错误。当错误发生时,需修改请求的内容后再次发送请求。另外,浏览器会像 200 OK 一样对待该状态码
401
Unauthorized
该状态码表示发送的请求需要有通过 HTTP 认证(BASIC 认证、DIGEST 认证)的认证信息
403
Forbidden
该状态码表明对请求资源的访问被服务器拒绝了
404
Not Found
该状态码表明服务器上无法找到请求的资源
500
Internal Server Error
该状态码表明服务器端在执行请求时发生了错误。也有可能是 Web 应用存在的 bug 或某些临时的故障
502
Bad Gateway
网关错误
503
Service Unavailable
该状态码表明服务器暂时处于超负载或正在进行停机维护,现在无法处理请求。如果事先得知解除以上状况需要的时间,最好写入 RetryAfter 首部字段再返回给客户端
HTTP 中不同场景下,首部字段的作用
1. CORS 跨域资源共享
跨域资源共享(CORS) 是一种机制,它使用额外的 HTTP 头来告诉浏览器 让运行在一个 origin (domain) 上的 Web 应用被准许访问来自不同源服务器上的指定的资源。当一个资源从与该资源本身所在的服务器不同的域、协议或端口请求一个资源时,资源会发起一个跨域 HTTP 请求。–MDN
下面使用 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 权威指南》,可放心参考。笔者功力实在有限,如有问题,请大家多多指出,相互学习和进步,也希望通过我的学习与实践过程,整理出的笔记能对大家有所帮助,谢谢。
本文参考链接:
TCP/IP 详解 卷 1:协议图解 HTTPHTTP 权威指南跨域资源共享 CORS 详解 – 阮一峰