一,HTTP请求过程
浏览器地址栏输入URL地址,回车之后开始跳转,首先检查应用缓存,如果缓存中有请求信息则直接从缓存中获取,如果没有则开始发送请求。首先需要做的是域名解析,通过DNS查找获取域名对应IP地址,请求需要通过IP地址找到对应服务器,然后通过三次握手创建TCP链接,http协议通过创建的TCP链接通道向服务器发送请求,服务器收到请求后对请求进行响应,发送响应信息给客户端。
二,网络模型介绍
(1)TCP链接三次握手
三,HTTP协议发展历史
(1)第一个版本
四,URI、URL、 URN
(1)URI:
(2)URL
(3)URN
五,HTTP报文格式
六,使用node.js实现一个最简单的HTTP服务
首先电脑需要安装node服务
(1)新建server.js文件,并写以下node代码
const http=require('http')
http.createServer(function (req,res) {
console.log('request come',req.url)
res.end('123')
}).listen(8888)
console.log('server listen 8888')
(2)终端启动,在server.js所在文件夹下打开终端,并输入 ‘node server.js’
(3)浏览器访问 http://localhost:8888
七,HTTP客户端
能发送HTTP请求的终端都属于HTTP客户端,最常见的是浏览器,
在终端使用curl也可以发送HTTP请求
curl -v +url 可以展示http请求的详细信息
windows自带的终端不支持CURL指令,可以下载git bush 终端代替cmd终端,苹果系统终端自带CURL指令
七,CORS跨域请求的限制与解析
(1)CORS请求实现方式
使用node新建server2.js服务文件,端口为8889,加载一个名为test.html的 HTML文件并返回浏览器客户端运行
const http=require('http')
const fs=require('fs')
http.createServer(function (req,res) {
console.log('request come',req.url)
const html=fs.readFileSync('test.html','utf8')
res.writeHead(200,{
'Content-Type':'text/html'
})
res.end(html)
}).listen(8889)
console.log('server listen 8889')
test.html文件内容如下,在script标签中,向http://127.0.0.1:8888发送一个ajax请求,
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
</body>
<script>
var xhr=new XMLHttpRequest();
xhr.open('GET','http://127.0.0.1:8888')
xhr.send()
</script>
</html>
http://127.0.0.1:8888服务端代码即为server.js代码
const http=require('http')
http.createServer(function (req,res) {
console.log('request come',req.url)
res.writeHead(200,{
'Content-Type':'text/html'
})
res.end('123')
}).listen(8888)
console.log('server listen 8888')
启动server.js和server2.js 在浏览器中访问server2,我们发现报错了,因为在server2中我们返回了一个html页面,html页面中我们访问了‘http://127.0.0.1:8888’,这里存在跨域请求的问题,
Access to XMLHttpRequest at 'http://127.0.0.1:8888/' from origin 'http://localhost:8889' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
提示‘http://127.0.0.1:8888’服务并没有设置Access-Control-Allow-Origin, 不能通过跨域,所以接下来我们设置一下server.js的Access-Control-Allow-Origin
const http=require('http')
http.createServer(function (req,res) {
console.log('request come',req.url)
res.writeHead(200,{
'Access-Control-Allow-Origin':'*',
'Content-Type':'text/html'
})
res.end('123')
}).listen(8888)
console.log('server listen 8888')
‘*’表示所有访问都被允许通过,我们重启server.js,然后浏览器访问http://localhost:8889(server2.js)
结果报错消失了,Access-Control-Allow-Origin可以设置为允许通过的域名或者IP地址,这样就只有被设置的值可以通过访问了,如果有多个域被允许,我们可以通过request.url获取访问的域名,然后判断该域名是否被允许,被允许的情况下动态写入Access-Control-Allow-Origin就可以了
在未设置Access-Control-Allow-Origin时,虽然页面报错,但是请求是成功的,这是因为服务端是没有跨域的概念的,服务端接收到请求就会发出响应,但浏览器有同源策略的限制,如果Access-Control-Allow-Origin 没有设置该域可以跨域访问,浏览器检测该值时会自动忽略服务端返回的数据而不展现出来。
jsonp的跨域原理
jsonp利用script、img标签的src属性被允许跨域的的特点实现,我们在代码上去掉ajax请求部分,在script的src属性中加入我们要请求的地址,浏览器访问localhost:8889,可以访问通过并没有报错
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
</body>
<script src="http://127.0.0.1:8888">
// var xhr=new XMLHttpRequest();
// xhr.open('GET','http://127.0.0.1:8888')
// xhr.send()
</script>
</html>
(2)cors请求限制和预请求
1,cors请求默认允许方法
GET HEAD POST
其他的方法需要预请求验证
2,cors请求默认允许的Content-Type
3,cors请求允许通过的请求头
其他的数据类型或者请求头需要预请求验证后才能通过
服务端不做预请求设置,我们更改test.html请求中的请求头设置,加入自定义请求头'X-Test-Cors':123
fetch('http://127.0.0.1:8888',{
method:'POST',
headers:{
'X-Test-Cors':123
}
})
访问localhost:8889,浏览器报错如下
我们为了避免出现这个错误,在server.js服务端设置允许访问请求头如下
重启服务,再次浏览器访问localhost:8889
发现报错消失了,查看network发现发送了两次127.0.0.1:8888的请求,第一次请求的Method为OPTIONS,这次的请求即为预请求,客户端首先询问服务端请求头或者方法是否可用,服务端返回允许使用信息后浏览器客户端发送的正式请求的返回信息才能不被浏览器忽略正常解析
'Access-Control-Max-Age':'1000',
允许跨域请求的最长时间,1000s内不需要再发送预请求给服务器,可以直接发送请求而被允许访问
八,缓存头Cache-Control的含义和使用
代码演示
服务端设置最长有效期,客户端第一次访问正常加载,之后在有效期内访问会从缓存中读取
第一次访问(勾选Disable cache会防止从缓存中读取数据)
第二次访问,在有效期内服务端发生改变客户端是不会获取的,因为url没有发生变化客户端数据会从缓存中获取,所以会有给url添加时间戳或者hash值的方式防止页面缓存
在http请求过程中缓存的使用过程
缓存预请求验证,Last-Modified和Etag为预请求标识符,缓存预请求只有在cache-control设置为no-cache时需要,cache-control设置为no-cache表示浏览器缓存数据,但浏览器忽略缓存从服务器重新请求数据。
预请求的使用过程,Cache-Control设置为no-cache时,浏览器会向客户端会忽略缓存,设置Last-Modified和Etag时浏览器客户端会发送预请求询问服务器是否从缓存中读取数据,服务端返回304状态码即是从缓存中读取数据。
浏览器第一次发送HTTP请求,正常返回数据并返回200状态码
第二次请求服务器,服务器判断数据未发生变化返回304状态码告诉浏览器从缓存中读取数据
九,cookie和session
使用cookie保存session,session对应用户信息,使用cookie保存用户id,能对应到用户既是session,session和cookie并不是一一对应的,也不一定使用cookie保存session。
十,HTTP长连接
长连接:HTTP 发送请求时,要先创建一个 TCP 连接,并在 TCP 连接上把 HTTP 请求的内容发送并且接收完返回,这是一次请求完成,浏览器与服务器进行协商是否关闭 TCP 链接,若不关闭 TCP 连接会有一定的消耗,好处是如果还有请求可以直接在这个 TCP 连接上发送,不需要经过创建时三次握手的消耗。从HTTP1.1开始http协议默认保持长连接,http请求完成后tcp连接不会立即关闭,同源下再次发送http请求还会继续使用,不过http1.1的长连接只能串行发送http请求,到http2.0开始在同一个tcp连接上可以并行发送http请求,多次请求时大大缩短了响应时间。
短连接:若关闭 TCP 连接,下次请求需要重新创建,这时会有网络延迟的开销,好处是每次请求完关闭 TCP 连接,减少客户端和服务端连接的并发数。
WebSocket 与 HTTP
WebSocket 协议在2008年诞生,2011年成为国际标准。现在所有浏览器都已经支持了。WebSocket 的最大特点就是,服务器可以主动向客户端推送信息,客户端也可以主动向服务器发送信息,是真正的双向平等对话。
HTTP 有 1.1 和 1.0 之说,也就是所谓的 keep-alive ,把多个 HTTP 请求合并为一个,但是 Websocket 其实是一个新协议,跟 HTTP 协议基本没有关系,只是为了兼容现有浏览器,所以在握手阶段使用了 HTTP 。
下面一张图说明了 HTTP 与 WebSocket 的主要区别:
WebSocket 的其他特点:
- 建立在 TCP 协议之上,服务器端的实现比较容易。
- 与 HTTP 协议有着良好的兼容性。默认端口也是80和443,并且握手阶段采用 HTTP 协议,因此握手时不容易屏蔽,能通过各种 HTTP 代理服务器。
- 数据格式比较轻量,性能开销小,通信高效。
- 可以发送文本,也可以发送二进制数据。
- 没有同源限制,客户端可以与任意服务器通信。
- 协议标识符是ws(如果加密,则为wss),服务器网址就是 URL。
WebSocket实现了真正的全双工,实现的是持久化的连接,连接建立后客户端可以向服务端推送消息,服务端也可以主动向客户端推送消息
十一,数据协商
(1)客户端请求
Accept可能的数据类型
(2)服务端响应
十二,重定向(Redirect)
请求服务器时,服务器资源位置发生改变,服务器可以设置新的资源位置,是请求重定向到新的位置
302是临时跳转,每次访问老的路径都需要服务器端做跳转,301是永久跳转,访问一次后,再次访问老的路径会直接访问新路径
浏览器在访问得到301状态码后会把自动路径改为新路径,之后只会访问新的路径,即便服务端发生修改也不会改变,除非浏览器端清除缓存,所以要慎用301状态码
十三,内容安全策略(Content-Security-Policy)简称CSP
Content-Security-Policy头可以控制页面资源访问方式,default-src说明资源只能通过src访问服务器获取,在script 标签中写的代码则不会被运行而且报错,‘self’说明只能访问自己服务器获取资源,https://cdn.bootcss.com/是可以开放访问的域,不设置访问的话会报错,