我只是个初学者, 图片文字有些来自网上, 写的不对的地方, 还望大佬指出!
浏览器输入 URL 后 HTTP 请求返回过程
网络协议分层
OSI 七层协议
五层协议
五层协议只是 OSI 和 TCP/IP 的综合, 实际应用还是 TCP/IP 的四层结构.
TCP/IP 协议
TCP(Transmission Control Protocol)传输控制协议
TCP/IP 协议将应用层, 表示层, 会话层合并为应用层, 物理层和数据链路层合并为网络接口层
三种模型结构
各层的作用
物理层:
主要定义物理设备标准, 如网线的接口类型, 光纤的接口类型, 各种传输介质的传输速率等. 它的主要作用是传输比特流(就是由 1,0 转化为电流强弱来进行传输, 到达目的地后在转化为 1,0, 也就是我们常说的数模转换与模数转换). 这一层的数据叫做比特.
数据链路层:
定义了如何让格式化数据以进行传输, 以及如何让控制对物理介质的访问. 这一层通常还提供错误检测和纠正, 以确保数据的可靠传输.
网络层:
在位于不同地理位置的网络中的两个主机系统之间提供连接和路径选择. Internet 的发展使得从世界各站点访问信息的用户数大大增加, 而网络层正是管理这种连接的层.
传输层:
定义了一些传输数据的协议和端口号(WWW 端口 80 等), 如:
TCP(transmission control protocol - 传输控制协议, 传输效率低, 可靠性强, 用于传输可靠性要求高, 数据量大的数据)
UDP(user datagram protocol - 用户数据报协议, 与 TCP 特性恰恰相反, 用于传输可靠性要求不高, 数据量小的数据, 如 QQ 聊天数据就是通过这种方式传输的). 主要是将从下层接收的数据进行分段和传输, 到达目的地址后再进行重组. 常常把这一层数据叫做段.
会话层:
通过运输层 (端口号: 传输端口与接收端口) 建立数据传输的通路. 主要在你的系统之间发起会话或者接受会话请求(设备之间需要互相认识可以是 IP 也可以是 MAC 或者是主机名)
表示层:
可确保一个系统的应用层所发送的信息可以被另一个系统的应用层读取. 例如, PC 程序与另一台计算机进行通信, 其中一台计算机使用扩展二一十进制交换码 (EBCDIC), 而另一台则使用美国信息交换标准码(ASCII) 来表示相同的字符. 如有必要, 表示层会通过使用一种通格式来实现多种数据格式之间的转换.
应用层:
是最靠近用户的 OSI 层. 这一层为用户的应用程序 (例如电子邮件, 文件传输和终端仿真) 提供网络服务.
HTTP 发展历史
HTTP/0.9
只有一个命令 GET
响应类型: 仅 超文本
没有 header 等描述数据的信息
服务器发送完毕, 就关闭 TCP 连接
HTTP/1.0
增加了很多命令(post HESD )
增加 status code 和 header
多字符集支持, 多部分发送, 权限, 缓存等
响应: 不再只限于超文本 (Content-Type 头部提供了传输 html 之外文件的能力 - 如脚本, 样式或媒体文件)
HTTP/1.1
持久连接. TCP 三次握手会在任何连接被建立之前发生一次. 最终, 当发送了所有数据之后, 服务器发送一个消息, 表示不会再有更多数据向客户端发送了; 则客户端才会关闭连接(断开 TCP)
支持的方法: `GET` , `HEAD` , `POST` , `PUT` ,` DELETE` , `TRACE` , `OPTIONS`
进行了重大的性能优化和特性增强, 分块传输, 压缩 / 解压, 内容缓存磋商, 虚拟主机(有单个 IP 地址的主机具有多个域名), 更快的响应, 以及通过增加缓存节省了更多的带宽
HTTP2
所有数据以二进制传输. HTTP1.x 是基于文本的, 无法保证健壮性, HTTP2.0 绝对使用新的二进制格式, 方便且健壮
同一个连接里面发送多个请求不再需要按照顺序来
头信息压缩以及 ** 推送 ** 等提高效率的功能
三次握手
客服端和服务端在进行 http 请求和返回的工程中, 需要创建一个 TCP connection(由客户端发起),http 不存在连接这个概念, 它只有请求和响应. 请求和响应都是数据包, 它们之间的传输通道就是 TCP connection.
位码即 tcp 标志位, 有 6 种标示: SYN(synchronous 建立联机) ACK(acknowledgement 确认) PSH(push 传送) FIN(finish 结束) RST(reset 重置) URG(urgent 紧急)Sequence number(顺序号码) Acknowledge number(确认号码)
第一次握手: 主机 A 发送位码为 syn=1, 随机产生 seq number=1234567 的数据包到服务器, 主机 B 由 SYN=1 知道, A 要求建立联机;
第二次握手: 主机 B 收到请求后要确认联机信息, 向 A 发送 ack number=(主机 A 的 seq+1),syn=1,ack=1, 随机产生 seq=7654321 的包;
第三次握手: 主机 A 收到后检查 ack number 是否正确, 即第一次发送的 seq number+1, 以及位码 ack 是否为 1, 若正确, 主机 A 会再发送 ack number=(主机 B 的 seq+1),ack=1, 主机 B 收到后确认 seq 值与 ack=1 则连接建立成功
URI,URL,URN
URI: Uniform Resource Identifier / 统一资源标识符
URL: Uniform Resource Locator / 统一资源定位器
URN: Uniform Resource Name / 永久统一资源定位符
web 上的各种资源 (html, 图片, 视频, 音频等) 都由一个 URI 标识定位. URI 相当于它们的详细 "家庭住址".
URI 包含了 URL 和 URN.
URL 是 URI 的一种, 不仅标识了 Web 资源, 还指定了操作或者获取方式, 同时指出了主要访问机制和网络位置.
URN 是 URI 的一种, 用特定命名空间的名字标识资源. 使用 URN 可以在不知道其网络位置及访问方式的情况下讨论资源.
网上的一个例子:
- // 这是一个 URI
- http://bitpoetry.io/posts/hello.html#intro
- // 资源访问方式
- http://
- // 资源存储位置
- bitpoetry.io/posts/hello.html
- #intro // 资源
- // URL
- http://bitpoetry.io/posts/hello.html
- // URN
- bitpoetry.io/posts/hello.html#intro
HTTP 报文
请求报文:
响应报文:
HTTP 各种特性
curl
curl 命令是一个利用 URL 规则在命令行下工作的文件传输工具. 它支持文件的上传和下载, 所以是综合传输工具, 但按传统, 习惯称 curl 为下载工具. 作为一款强力工具, curl 支持包括 HTTP,HTTPS,ftp 等众多协议, 还支持 POST,cookies, 认证, 从指定偏移处下载部分文件, 用户代理字符串, 限速, 文件大小, 进度条等特征. 做网页处理流程和数据检索自动化, curl 可以祝一臂之力.
更详细的 CURL, 点这里. http://man.linuxde.net/curl
curl 访问 baidu.com:
返回的内容中, html 部分只有一个 meta 标签,
<meta http-equiv="refresh" content="0;url=http://www.baidu.com/">
, 这是因为我们访问的是 baidu.com, 在浏览器中, 浏览器会自动解析这个 meta 标签并重定向到
http://www.baidu.com/
, 然而命令行中并没有解析的功能.
curl 访问 www.baidu.com:
curl 常用命令
-v 显示详细的请求信息
-X 指定请求方式
- curl -X GET www.xxxx.com/xx/xx?xx=123
- curl -X POST www.xxxx.com/xx/xx?xx=123
-o / -O 保存下载的文件
- // 将文件下载到本地并命名为 mygettext.html
- curl -o mygettext.html http://www.gnu.org/software/gettext/manual/gettext.html
- // 将文件保存到本地并命名为 gettext.html
- curl -O http://www.gnu.org/software/gettext/manual/gettext.html
CORS 跨域请求的限制与解决
- // server1.js
- const http = require('http')
- const fs = require('fs')
- http.createServer(function (request, response) {
- console.log('request come', request.url)
- const html = fs.readFileSync('test.html', 'utf8')
- response.writeHead(200, {
- 'Content-Type': 'text/html'
- })
- response.end(html)
- }).listen(8888)
- console.log('server listening on 8888')
- // server2.js
- const http = require('http')
- http.createServer(function (request, response) {
- console.log('request come', request.url)
- response.end('123')
- }).listen(8887)
- console.log('server listening on 8887')
- // test.html
- <!DOCTYPE html>
- <html lang="en">
- <head>
- <meta charset="UTF-8">
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
- <meta http-equiv="X-UA-Compatible" content="ie=edge">
- <title>Document</title>
- </head>
- <body>
- </body>
- <script>
- fetch('http://127.0.0.1:8887');
- </script>
- </html>
处理方法:
1. 服务器端处理
- // server2.js 服务器端设置允许跨域
- response.writeHead(200, {
- 'Access-Control-Allow-Origin': '*' //* 表示任何域名下都可以访问这个服务, 也可以指定域名
- })
- 2.jsonp
- // test.html
- <script src="http://127.0.0.1:8887"></script>
就算存在跨域, 请求还是会发送, 响应也会返回, 只是浏览器端发现了存在跨域问题就将返回内容屏蔽了, 并报错提示.
CORS 预请求
- // test.html
- <script>
- fetch('http://127.0.0.1:8887',{
- method: 'post',
- headers: {
- 'X-Test-Cors': '123'
- }
- });
- </script>
我们设置的请求头中 X-Test-Cors 在跨域请求的时候, 不被允许.
虽然不允许跨域, 但是请求仍然会发送, 并返回成功.
默认允许的请求方法:
GET
HEAD
POST
其他的方法 (PUT,DELETE) 都需要预请求验证的.
默认允许的 Content-Type:
- text/plain
- multipart/form-data
- application/x-www-form-urlencoded
怎样设置允许我们设置的请求头:
- // server2.js
- response.writeHead(200, {
- 'Access-Control-Allow-Origin': '*',
- 'Access-Control-Allow-Headers': 'X-Test-Cors' // 加上这个设置
- })
首先发送一个预请求, 预请求就是告诉浏览器接下来要发送的 post 请求是被允许的.
设置允许的请求方法:
- // server2.js
- response.writeHead(200, {
- 'Access-Control-Allow-Origin': '*',
- 'Access-Control-Allow-Headers': 'X-Test-Cors',
- 'Access-Control-Allow-Methods': 'POST, PUT, DELETE'
- })
设置一个安全时间:
- // server2.js
- response.writeHead(200, {
- 'Access-Control-Allow-Origin': '*',
- 'Access-Control-Allow-Headers': 'X-Test-Cors',
- 'Access-Control-Allow-Methods': 'POST, PUT, DELETE',
- 'Access-Control-Max-Age': '1000'
- })
- Access-Control-Max-Age
的单位是秒, 意思就是在多少秒以内, 我们设置的这些允许的请求头, 请求方法, 是不需要发送预请求验证的, 直接就可以通过, 并发送.
缓存 Cache-Control
常用值:
Cache-Control | 说明 |
---|---|
public | 所有内容都将被缓存 (客户端和代理服务器都可缓存) |
private | 内容只缓存到私有缓存中 (仅客户端可以缓存,代理服务器不可缓存) |
no-cache | 必须先与服务器确认返回的响应是否被更改,然后才能使用该响应来满足后续对同一个网址的请求。因此,如果存在合适的验证令牌 (ETag),no-cache 会发起往返通信来验证缓存的响应,如果资源未被更改,可以避免下载。 |
no-store | 所有内容都不会被缓存到缓存或 Internet 临时文件中 |
must-revalidation/proxy-revalidation | 如果缓存的内容失效,请求必须发送到服务器 / 代理以进行重新验证 |
max-age=xxx (xxx is numeric) | 缓存的内容将在 xxx 秒后失效, 这个选项只在 HTTP 1.1 可用, 并如果和 Last-Modified 一起使用时, 优先级较高 |
- // server.js
- const http = require('http')
- const fs = require('fs')
- http.createServer(function (request, response) {
- console.log('request come', request.url)
- if (request.url === '/') {
- const html = fs.readFileSync('test.html', 'utf8')
- response.writeHead(200, {
- 'Content-Type': 'text/html'
- })
- response.end(html)
- }
- if (request.url === '/script.js') {
- response.writeHead(200, {
- 'Content-Type': 'text/javascript',
- 'Cache-Control': 'max-age=20,public' // 缓存 20s 多个值用逗号分开
- })
- response.end('console.log("script loaded")')
- }
- }).listen(8888)
- console.log('server listening on 8888')
- // test.html
- <!DOCTYPE html>
- <html lang="en">
- <head>
- <meta charset="UTF-8">
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
- <meta http-equiv="X-UA-Compatible" content="ie=edge">
- <title>Document</title>
- </head>
- <body>
- </body>
- <script src="/script.js"></script>
- </html>
刷新会发现 script.js 是从缓存中获取的, 请求时间也是 0.
我们希望浏览器缓存我们的图片, 文件, js 代码, 但是服务器端代码更新了, 浏览器端还是在缓存中获取的旧的文件. 这就诞生了, webpack 打包中出现的文件名后加上 hash 值, 当文件改变时 hash 值也改变, 这样浏览器就会发送新的请求到服务器端.
缓存验证
验证头:
Last-Modified
上次修改时间
配合 If-Modified-Since 或者 If-Unmodified-Since 使用
对比上次修改时间以验证资源是否需要更新
Etag
数据签名(内容修改, 签名就会改变)
配合 If-Match 或者 If-Non-Match 使用
对比资源的签名判断是否使用缓存
- Redirect
- const http = require('http')
- http.createServer(function (request, response) {
- console.log('request come', request.url)
- if (request.url === '/') {
- response.writeHead(302, { // or 301
- 'Location': '/new'
- })
- response.end()
- }
- if (request.url === '/new') {
- response.writeHead(200, {
- 'Content-Type': 'text/html',
- })
- response.end('<div>this is content</div>')
- }
- }).listen(8888)
- console.log('server listening on 8888')
302 临时跳转, 301 永久跳转, 301 从缓存种获取跳转, 使用 301 之后, 主动权就掌握在用户手里, 如果用户不清理缓存, 那就算服务器端改变了也没用.
Content Security Policy (网页安全政策)
阮一峰: Content Security Policy 入门教程 http://www.ruanyifeng.com/blog/2016/09/csp.html
HTTPS
HTTPS 和 HTTP 的区别主要为以下四点:
一, https 协议需要到 ca 申请证书, 一般免费证书很少, 需要交费.
二, http 是超文本传输协议, 信息是明文传输, https 则是具有安全性的 ssl 加密传输协议.
三, http 和 https 使用的是完全不同的连接方式, 用的端口也不一样, 前者是 80, 后者是 443.
四, http 的连接很简单, 是无状态的; HTTPS 协议是由 SSL+HTTP 协议构建的可进行加密传输, 身份认证的网络协议, 比 http 协议安全.
来源: http://www.qdfuns.com/article/45942/de95a04ba24a172276213587abc7e176.html