learn.lianglianglee.com/专栏/透视HTTP协议/04 HTTP世界全览(下):与HTTP相关的各种协议.md.html
2022-05-11 19:04:14 +08:00

711 lines
28 KiB
HTML
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!DOCTYPE html>
<!-- saved from url=(0046)https://kaiiiz.github.io/hexo-theme-book-demo/ -->
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1.0, user-scalable=no">
<link rel="icon" href="/static/favicon.png">
<title>04 HTTP世界全览与HTTP相关的各种协议.md.html</title>
<!-- Spectre.css framework -->
<link rel="stylesheet" href="/static/index.css">
<!-- theme css & js -->
<meta name="generator" content="Hexo 4.2.0">
</head>
<body>
<div class="book-container">
<div class="book-sidebar">
<div class="book-brand">
<a href="/">
<img src="/static/favicon.png">
<span>技术文章摘抄</span>
</a>
</div>
<div class="book-menu uncollapsible">
<ul class="uncollapsible">
<li><a href="/" class="current-tab">首页</a></li>
</ul>
<ul class="uncollapsible">
<li><a href="../">上一级</a></li>
</ul>
<ul class="uncollapsible">
<li>
<a href="/专栏/透视HTTP协议/00 开篇词To Be a HTTP Hero.md.html">00 开篇词To Be a HTTP Hero.md.html</a>
</li>
<li>
<a href="/专栏/透视HTTP协议/01 时势与英雄HTTP的前世今生.md.html">01 时势与英雄HTTP的前世今生.md.html</a>
</li>
<li>
<a href="/专栏/透视HTTP协议/02 HTTP是什么HTTP又不是什么.md.html">02 HTTP是什么HTTP又不是什么.md.html</a>
</li>
<li>
<a href="/专栏/透视HTTP协议/03 HTTP世界全览与HTTP相关的各种概念.md.html">03 HTTP世界全览与HTTP相关的各种概念.md.html</a>
</li>
<li>
<a class="current-tab" href="/专栏/透视HTTP协议/04 HTTP世界全览与HTTP相关的各种协议.md.html">04 HTTP世界全览与HTTP相关的各种协议.md.html</a>
</li>
<li>
<a href="/专栏/透视HTTP协议/05 常说的“四层”和“七层”到底是什么?“五层”“六层”哪去了?.md.html">05 常说的“四层”和“七层”到底是什么?“五层”“六层”哪去了?.md.html</a>
</li>
<li>
<a href="/专栏/透视HTTP协议/06 域名里有哪些门道?.md.html">06 域名里有哪些门道?.md.html</a>
</li>
<li>
<a href="/专栏/透视HTTP协议/07 自己动手搭建HTTP实验环境.md.html">07 自己动手搭建HTTP实验环境.md.html</a>
</li>
<li>
<a href="/专栏/透视HTTP协议/08 键入网址再按下回车,后面究竟发生了什么?.md.html">08 键入网址再按下回车,后面究竟发生了什么?.md.html</a>
</li>
<li>
<a href="/专栏/透视HTTP协议/09 HTTP报文是什么样子的.md.html">09 HTTP报文是什么样子的.md.html</a>
</li>
<li>
<a href="/专栏/透视HTTP协议/10 应该如何理解请求方法?.md.html">10 应该如何理解请求方法?.md.html</a>
</li>
<li>
<a href="/专栏/透视HTTP协议/11 你能写出正确的网址吗?.md.html">11 你能写出正确的网址吗?.md.html</a>
</li>
<li>
<a href="/专栏/透视HTTP协议/12 响应状态码该怎么用?.md.html">12 响应状态码该怎么用?.md.html</a>
</li>
<li>
<a href="/专栏/透视HTTP协议/13 HTTP有哪些特点.md.html">13 HTTP有哪些特点.md.html</a>
</li>
<li>
<a href="/专栏/透视HTTP协议/14 HTTP有哪些优点又有哪些缺点.md.html">14 HTTP有哪些优点又有哪些缺点.md.html</a>
</li>
<li>
<a href="/专栏/透视HTTP协议/15 海纳百川HTTP的实体数据.md.html">15 海纳百川HTTP的实体数据.md.html</a>
</li>
<li>
<a href="/专栏/透视HTTP协议/16 把大象装进冰箱HTTP传输大文件的方法.md.html">16 把大象装进冰箱HTTP传输大文件的方法.md.html</a>
</li>
<li>
<a href="/专栏/透视HTTP协议/17 排队也要讲效率HTTP的连接管理.md.html">17 排队也要讲效率HTTP的连接管理.md.html</a>
</li>
<li>
<a href="/专栏/透视HTTP协议/18 四通八达HTTP的重定向和跳转.md.html">18 四通八达HTTP的重定向和跳转.md.html</a>
</li>
<li>
<a href="/专栏/透视HTTP协议/19 让我知道你是谁HTTP的Cookie机制.md.html">19 让我知道你是谁HTTP的Cookie机制.md.html</a>
</li>
<li>
<a href="/专栏/透视HTTP协议/20 生鲜速递HTTP的缓存控制.md.html">20 生鲜速递HTTP的缓存控制.md.html</a>
</li>
<li>
<a href="/专栏/透视HTTP协议/21 良心中间商HTTP的代理服务.md.html">21 良心中间商HTTP的代理服务.md.html</a>
</li>
<li>
<a href="/专栏/透视HTTP协议/22 冷链周转HTTP的缓存代理.md.html">22 冷链周转HTTP的缓存代理.md.html</a>
</li>
<li>
<a href="/专栏/透视HTTP协议/23 HTTPS是什么SSLTLS又是什么.md.html">23 HTTPS是什么SSLTLS又是什么.md.html</a>
</li>
<li>
<a href="/专栏/透视HTTP协议/24 固若金汤的根本(上):对称加密与非对称加密.md.html">24 固若金汤的根本(上):对称加密与非对称加密.md.html</a>
</li>
<li>
<a href="/专栏/透视HTTP协议/25 固若金汤的根本(下):数字签名与证书.md.html">25 固若金汤的根本(下):数字签名与证书.md.html</a>
</li>
<li>
<a href="/专栏/透视HTTP协议/26 信任始于握手TLS1.2连接过程解析.md.html">26 信任始于握手TLS1.2连接过程解析.md.html</a>
</li>
<li>
<a href="/专栏/透视HTTP协议/27 更好更快的握手TLS1.3特性解析.md.html">27 更好更快的握手TLS1.3特性解析.md.html</a>
</li>
<li>
<a href="/专栏/透视HTTP协议/28 连接太慢该怎么办HTTPS的优化.md.html">28 连接太慢该怎么办HTTPS的优化.md.html</a>
</li>
<li>
<a href="/专栏/透视HTTP协议/29 我应该迁移到HTTPS吗.md.html">29 我应该迁移到HTTPS吗.md.html</a>
</li>
<li>
<a href="/专栏/透视HTTP协议/30 时代之风HTTP2特性概览.md.html">30 时代之风HTTP2特性概览.md.html</a>
</li>
<li>
<a href="/专栏/透视HTTP协议/31 时代之风HTTP2内核剖析.md.html">31 时代之风HTTP2内核剖析.md.html</a>
</li>
<li>
<a href="/专栏/透视HTTP协议/32 未来之路HTTP3展望.md.html">32 未来之路HTTP3展望.md.html</a>
</li>
<li>
<a href="/专栏/透视HTTP协议/33 我应该迁移到HTTP2吗.md.html">33 我应该迁移到HTTP2吗.md.html</a>
</li>
<li>
<a href="/专栏/透视HTTP协议/34 Nginx高性能的Web服务器.md.html">34 Nginx高性能的Web服务器.md.html</a>
</li>
<li>
<a href="/专栏/透视HTTP协议/35 OpenResty更灵活的Web服务器.md.html">35 OpenResty更灵活的Web服务器.md.html</a>
</li>
<li>
<a href="/专栏/透视HTTP协议/36 WAF保护我们的网络服务.md.html">36 WAF保护我们的网络服务.md.html</a>
</li>
<li>
<a href="/专栏/透视HTTP协议/37 CDN加速我们的网络服务.md.html">37 CDN加速我们的网络服务.md.html</a>
</li>
<li>
<a href="/专栏/透视HTTP协议/38 WebSocket沙盒里的TCP.md.html">38 WebSocket沙盒里的TCP.md.html</a>
</li>
<li>
<a href="/专栏/透视HTTP协议/39 HTTP性能优化面面观.md.html">39 HTTP性能优化面面观.md.html</a>
</li>
<li>
<a href="/专栏/透视HTTP协议/40 HTTP性能优化面面观.md.html">40 HTTP性能优化面面观.md.html</a>
</li>
<li>
<a href="/专栏/透视HTTP协议/结束语 做兴趣使然的Hero.md.html">结束语 做兴趣使然的Hero.md.html</a>
</li>
</ul>
</div>
</div>
<div class="sidebar-toggle" onclick="sidebar_toggle()" onmouseover="add_inner()" onmouseleave="remove_inner()">
<div class="sidebar-toggle-inner"></div>
</div>
<script>
function add_inner() {
let inner = document.querySelector('.sidebar-toggle-inner')
inner.classList.add('show')
}
function remove_inner() {
let inner = document.querySelector('.sidebar-toggle-inner')
inner.classList.remove('show')
}
function sidebar_toggle() {
let sidebar_toggle = document.querySelector('.sidebar-toggle')
let sidebar = document.querySelector('.book-sidebar')
let content = document.querySelector('.off-canvas-content')
if (sidebar_toggle.classList.contains('extend')) { // show
sidebar_toggle.classList.remove('extend')
sidebar.classList.remove('hide')
content.classList.remove('extend')
} else { // hide
sidebar_toggle.classList.add('extend')
sidebar.classList.add('hide')
content.classList.add('extend')
}
}
function open_sidebar() {
let sidebar = document.querySelector('.book-sidebar')
let overlay = document.querySelector('.off-canvas-overlay')
sidebar.classList.add('show')
overlay.classList.add('show')
}
function hide_canvas() {
let sidebar = document.querySelector('.book-sidebar')
let overlay = document.querySelector('.off-canvas-overlay')
sidebar.classList.remove('show')
overlay.classList.remove('show')
}
</script>
<div class="off-canvas-content">
<div class="columns">
<div class="column col-12 col-lg-12">
<div class="book-navbar">
<!-- For Responsive Layout -->
<header class="navbar">
<section class="navbar-section">
<a onclick="open_sidebar()">
<i class="icon icon-menu"></i>
</a>
</section>
</header>
</div>
<div class="book-content" style="max-width: 960px; margin: 0 auto;
overflow-x: auto;
overflow-y: hidden;">
<div class="book-post">
<p id="tip" align="center"></p>
<div><h1>04 HTTP世界全览与HTTP相关的各种协议</h1>
<p>在上一讲中,我介绍了与 HTTP 相关的浏览器、服务器、CDN、网络爬虫等应用技术。</p>
<p>今天要讲的则是比较偏向于理论的各种 HTTP 相关协议,重点是 TCP/IP、DNS、URI、HTTPS 等,希望能够帮你理清楚它们与 HTTP 的关系。</p>
<p>同样的,我还是画了一张详细的思维导图,你可以点击后仔细查看。</p>
<p><img src="assets/261bac84bfb1f957c3e9fd45021a5d73.png" alt="img" /></p>
<h2>TCP/IP</h2>
<p>TCP/IP 协议是目前网络世界“事实上”的标准通信协议,即使你没有用过也一定听说过,因为它太著名了。</p>
<p>TCP/IP 协议实际上是一系列网络通信协议的统称,其中最核心的两个协议是<strong>TCP</strong><strong>IP</strong>,其他的还有 UDP、ICMP、ARP 等等,共同构成了一个复杂但有层次的协议栈。</p>
<p>这个协议栈有四层最上层是“应用层”最下层是“链接层”TCP 和 IP 则在中间:<strong>TCP 属于“传输层”IP 属于“网际层”</strong>。协议的层级关系模型非常重要,我会在下一讲中再专门讲解,这里先暂时放一放。</p>
<p><strong>IP 协议</strong>是“<strong>I</strong>nternet <strong>P</strong>rotocol”的缩写主要目的是解决寻址和路由问题以及如何在两点间传送数据包。IP 协议使用“<strong>IP 地址</strong>”的概念来定位互联网上的每一台计算机。可以对比一下现实中的电话系统,你拿着的手机相当于互联网上的计算机,而要打电话就必须接入电话网,由通信公司给你分配一个号码,这个号码就相当于 IP 地址。</p>
<p>现在我们使用的 IP 协议大多数是 v4 版,地址是四个用“.”分隔的数字例如“192.168.0.1”,总共有 2^32大约 42 亿个可以分配的地址。看上去好像很多,但互联网的快速发展让地址的分配管理很快就“捉襟见肘”。所以,就又出现了 v6 版,使用 8 组“:”分隔的数字作为地址,容量扩大了很多,有 2^128 个,在未来的几十年里应该是足够用了。</p>
<p><strong>TCP 协议</strong>是“<strong>T</strong>ransmission <strong>C</strong>ontrol <strong>P</strong>rotocol”的缩写意思是“传输控制协议”它位于 IP 协议之上,基于 IP 协议提供可靠的、字节流形式的通信,是 HTTP 协议得以实现的基础。</p>
<p>“可靠”是指保证数据不丢失,“字节流”是指保证数据完整,所以在 TCP 协议的两端可以如同操作文件一样访问传输的数据,就像是读写在一个密闭的管道里“流动”的字节。</p>
<p>在[第 2 讲]时我曾经说过HTTP 是一个&quot;传输协议&quot;,但它不关心寻址、路由、数据完整性等传输细节,而要求这些工作都由下层来处理。因为互联网上最流行的是 TCP/IP 协议,而它刚好满足 HTTP 的要求,所以互联网上的 HTTP 协议就运行在了 TCP/IP 上HTTP 也就可以更准确地称为“<strong>HTTP over TCP/IP</strong>”。</p>
<h2>DNS</h2>
<p>在 TCP/IP 协议中使用 IP 地址来标识计算机,数字形式的地址对于计算机来说是方便了,但对于人类来说却既难以记忆又难以输入。</p>
<p>于是“<strong>域名系统</strong>”(<strong>D</strong>omain <strong>N</strong>ame <strong>S</strong>ystem出现了用有意义的名字来作为 IP 地址的等价替代。设想一下你是愿意记“95.211.80.227”这样枯燥的数字还是“nginx.org”这样的词组呢</p>
<p>在 DNS 中“域名”Domain Name又称为“主机名”Host为了更好地标记不同国家或组织的主机让名字更好记所以被设计成了一个有层次的结构。</p>
<p>域名用“.”分隔成多个单词级别从左到右逐级升高最右边的被称为“顶级域名”。对于顶级域名可能你随口就能说出几个例如表示商业公司的“com”、表示教育机构的“edu”表示国家的“cn”“uk”等买火车票时的域名还记得吗是“www.12306.cn”。</p>
<p><img src="assets/36b6a41da6e9abc2fc28ee9a305f48b3.jpg" alt="unpreview" /></p>
<p>但想要使用 TCP/IP 协议来通信仍然要使用 IP 地址,所以需要把域名做一个转换,“映射”到它的真实 IP这就是所谓的“<strong>域名解析</strong>”。</p>
<p>继续用刚才的打电话做个比喻,你想要打电话给小明,但不知道电话号码,就得在手机里的号码簿里一项一项地找,直到找到小明那一条记录,然后才能查到号码。这里的“小明”就相当于域名,而“电话号码”就相当于 IP 地址,这个查找的过程就是域名解析。</p>
<p>域名解析的实际操作要比刚才的例子复杂很多,因为互联网上的电脑实在是太多了。目前全世界有 13 组根 DNS 服务器,下面再有许多的顶级 DNS、权威 DNS 和更小的本地 DNS逐层递归地实现域名查询。</p>
<p>HTTP 协议中并没有明确要求必须使用 DNS但实际上为了方便访问互联网上的 Web 服务器,通常都会使用 DNS 来定位或标记主机名,间接地把 DNS 与 HTTP 绑在了一起。</p>
<h2>URI/URL</h2>
<p>有了 TCP/IP 和 DNS是不是我们就可以任意访问网络上的资源了呢</p>
<p>还不行DNS 和 IP 地址只是标记了互联网上的主机,但主机上有那么多文本、图片、页面,到底要找哪一个呢?就像小明管理了一大堆文档,你怎么告诉他是哪个呢?</p>
<p>所以就出现了 URI<strong>U</strong>niform <strong>R</strong>esource <strong>I</strong>dentifier中文名称是 <strong>统一资源标识符</strong>,使用它就能够唯一地标记互联网上资源。</p>
<p>URI 另一个更常用的表现形式是 URL<strong>U</strong>niform <strong>R</strong>esource <strong>L</strong>ocator <strong>统一资源定位符</strong>,也就是我们俗称的“网址”,它实际上是 URI 的一个子集,不过因为这两者几乎是相同的,差异不大,所以通常不会做严格的区分。</p>
<p>我就拿 Nginx 网站来举例,看一下 URI 是什么样子的。</p>
<pre><code>http://nginx.org/en/download.html
</code></pre>
<p>你可以看到URI 主要有三个基本的部分构成:</p>
<ol>
<li>协议名即访问该资源应当使用的协议在这里是“http”</li>
<li>主机名:即互联网上主机的标记,可以是域名或 IP 地址在这里是“nginx.org”</li>
<li>路径:即资源在主机上的位置,使用“/”分隔多级目录,在这里是“/en/download.html”。</li>
</ol>
<p>还是用打电话来做比喻,你通过电话簿找到了小明,让他把昨天做好的宣传文案快递过来。那么这个过程中你就完成了一次 URI 资源访问,“小明”就是“主机名”,“昨天做好的宣传文案”就是“路径”,而“快递”,就是你要访问这个资源的“协议名”。</p>
<h2>HTTPS</h2>
<p>在 TCP/IP、DNS 和 URI 的“加持”之下HTTP 协议终于可以自由地穿梭在互联网世界里,顺利地访问任意的网页了,真的是“好生快活”。</p>
<p>但且慢,互联网上不仅有“美女”,还有很多的“野兽”。</p>
<p>假设你打电话找小明要一份广告创意,很不幸,电话被商业间谍给窃听了,他立刻动用种种手段偷窃了你的快递,就在你还在等包裹的时候,他抢先发布了这份广告,给你的公司造成了无形或有形的损失。</p>
<p>有没有什么办法能够防止这种情况的发生呢?确实有。你可以使用“加密”的方法,比如这样打电话:</p>
<blockquote>
<p>你:“喂,小明啊,接下来我们改用火星文通话吧。”
小明:“好啊好啊,就用火星文吧。”
你:“巴拉巴拉巴拉巴拉……”
小明:“巴拉巴拉巴拉巴拉……”</p>
</blockquote>
<p>如果你和小明说的火星文只有你们两个才懂,那么即使窃听到了这段谈话,他也不会知道你们到底在说什么,也就无从破坏你们的通话过程。</p>
<p>HTTPS 就相当于这个比喻中的“火星文”,它的全称是“<strong>HTTP over SSL/TLS</strong>”,也就是运行在 SSL/TLS 协议上的 HTTP。</p>
<p>注意它的名字,这里是 SSL/TLS而不是 TCP/IP它是一个负责加密通信的安全协议建立在 TCP/IP 之上,所以也是个可靠的传输协议,可以被用作 HTTP 的下层。</p>
<p>因为 HTTPS 相当于“HTTP+SSL/TLS+TCP/IP”其中的“HTTP”和“TCP/IP”我们都已经明白了只要再了解一下 SSL/TLSHTTPS 也就能够轻松掌握。</p>
<p>SSL 的全称是“<strong>S</strong>ecure <strong>S</strong>ocket <strong>L</strong>ayer”由网景公司发明当发展到 3.0 时被标准化,改名为 TLS即“<strong>T</strong>ransport <strong>L</strong>ayer <strong>S</strong>ecurity”但由于历史的原因还是有很多人称之为 SSL/TLS或者直接简称为 SSL。</p>
<p>SSL 使用了许多密码学最先进的研究成果,综合了对称加密、非对称加密、摘要算法、数字签名、数字证书等技术,能够在不安全的环境中为通信的双方创建出一个秘密的、安全的传输通道,为 HTTP 套上一副坚固的盔甲。</p>
<p>你可以在今后上网时留心看一下浏览器地址栏,如果有一个小锁头标志,那就表明网站启用了安全的 HTTPS 协议,而 URI 里的协议名也从“http”变成了“https”。</p>
<h2>代理</h2>
<p>代理Proxy是 HTTP 协议中请求方和应答方中间的一个环节,作为“中转站”,既可以转发客户端的请求,也可以转发服务器的应答。</p>
<p>代理有很多的种类,常见的有:</p>
<ol>
<li>匿名代理:完全“隐匿”了被代理的机器,外界看到的只是代理服务器;</li>
<li>透明代理:顾名思义,它在传输过程中是“透明开放”的,外界既知道代理,也知道客户端;</li>
<li>正向代理:靠近客户端,代表客户端向服务器发送请求;</li>
<li>反向代理:靠近服务器端,代表服务器响应客户端的请求;</li>
</ol>
<p>上一讲提到的 CDN实际上就是一种代理它代替源站服务器响应客户端的请求通常扮演着透明代理和反向代理的角色。</p>
<p>由于代理在传输过程中插入了一个“中间层”,所以可以在这个环节做很多有意思的事情,比如:</p>
<ol>
<li>负载均衡:把访问请求均匀分散到多台机器,实现访问集群化;</li>
<li>内容缓存:暂存上下行的数据,减轻后端的压力;</li>
<li>安全防护:隐匿 IP, 使用 WAF 等工具抵御网络攻击,保护被代理的机器;</li>
<li>数据处理:提供压缩、加密等额外的功能。</li>
</ol>
<p>关于 HTTP 的代理还有一个特殊的“代理协议”proxy protocol它由知名的代理软件 HAProxy 制订,但并不是 RFC 标准,我也会在之后的课程里专门讲解。</p>
<h2>小结</h2>
<p>这次我介绍了与 HTTP 相关的各种协议,在这里简单小结一下今天的内容。</p>
<ol>
<li>TCP/IP 是网络世界最常用的协议HTTP 通常运行在 TCP/IP 提供的可靠传输基础上;</li>
<li>DNS 域名是 IP 地址的等价替代,需要用域名解析实现到 IP 地址的映射;</li>
<li>URI 是用来标记互联网上资源的一个名字,由“协议名 + 主机名 + 路径”构成,俗称 URL</li>
<li>HTTPS 相当于“HTTP+SSL/TLS+TCP/IP”为 HTTP 套了一个安全的外壳;</li>
<li>代理是 HTTP 传输过程中的“中转站”,可以实现缓存加速、负载均衡等功能。</li>
</ol>
<p>经过这两讲的学习,相信你应该对 HTTP 有了一个比较全面的了解,虽然还不是很深入,但已经为后续的学习扫清了障碍。</p>
<h2>课下作业</h2>
<ol>
<li>DNS 与 URI 有什么关系?</li>
<li>在讲<strong>代理</strong>时我特意没有举例说明,你能够用引入一个“小强”的角色,通过打电话来比喻一下吗?</li>
</ol>
<p>欢迎你通过留言分享答案,与我和其他同学一起讨论。如果你觉得有所收获,欢迎你把文章分享给你的朋友。</p>
<p><img src="assets/4eab55dc3600071330e088b40cae4856.png" alt="unpreview" /></p>
</div>
</div>
<div>
<div style="float: left">
<a href="/专栏/透视HTTP协议/03 HTTP世界全览与HTTP相关的各种概念.md.html">上一页</a>
</div>
<div style="float: right">
<a href="/专栏/透视HTTP协议/05 常说的“四层”和“七层”到底是什么?“五层”“六层”哪去了?.md.html">下一页</a>
</div>
</div>
</div>
</div>
</div>
</div>
<a class="off-canvas-overlay" onclick="hide_canvas()"></a>
</div>
<script defer src="https://static.cloudflareinsights.com/beacon.min.js/v652eace1692a40cfa3763df669d7439c1639079717194" integrity="sha512-Gi7xpJR8tSkrpF7aordPZQlW2DLtzUlZcumS8dMQjwDHEnw9I7ZLyiOj/6tZStRBGtGgN6ceN6cMH8z7etPGlw==" data-cf-beacon='{"rayId":"70997ce46d203cfa","version":"2021.12.0","r":1,"token":"1f5d475227ce4f0089a7cff1ab17c0f5","si":100}' crossorigin="anonymous"></script>
</body>
<!-- Global site tag (gtag.js) - Google Analytics -->
<script async src="https://www.googletagmanager.com/gtag/js?id=G-NPSEEVD756"></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag() {
dataLayer.push(arguments);
}
gtag('js', new Date());
gtag('config', 'G-NPSEEVD756');
var path = window.location.pathname
var cookie = getCookie("lastPath");
console.log(path)
if (path.replace("/", "") === "") {
if (cookie.replace("/", "") !== "") {
console.log(cookie)
document.getElementById("tip").innerHTML = "<a href='" + cookie + "'>跳转到上次进度</a>"
}
} else {
setCookie("lastPath", path)
}
function setCookie(cname, cvalue) {
var d = new Date();
d.setTime(d.getTime() + (180 * 24 * 60 * 60 * 1000));
var expires = "expires=" + d.toGMTString();
document.cookie = cname + "=" + cvalue + "; " + expires + ";path = /";
}
function getCookie(cname) {
var name = cname + "=";
var ca = document.cookie.split(';');
for (var i = 0; i < ca.length; i++) {
var c = ca[i].trim();
if (c.indexOf(name) === 0) return c.substring(name.length, c.length);
}
return "";
}
</script>
</html>