网络原理 - 11(HTTP/HTTPS - 2 - 请求)

目录

HTTP 请求(Request)

认识 URL

URL 基本格式

关于 URL encode

认识方法(method)

1. GET 方法

2. POST 方法

认识请求“报头”(header)

Host

Content-Length   Content-Type

User-Agent(简称 UA)

Referer

认识请求“正文”(body)

完!


HTTP 请求(Request)

认识 URL

URL 基本格式

URL 不仅仅是在 HTTP 中使用,回忆想想,我们之前在 JDBC 也使用过 URL:jdbc:mysql://127.0.0.1:3306/java110?characterEncoding=utf8&useSSL=false

平时我们俗称的“网址”,其实说的就是 URL(Uniform Resource Locator 统一资源定位符),描述一个互联网上的资源位置,即互联网上的每个文件都有一个唯一的 URL,它包含的信息指出文件的位置以及浏览器应该怎么处理它。(URL 的详细规则由因特网表述 RFC1738 进行约定~~)

我们上面的:https://www.baidu.com/ 

这是一个最简单的 URL,https 是协议的名称,www.baidu.com 是域名

我们如果抓一个百度里面的搜索内容请求如下:

此时的 URL 就不是上面那么简单了:在协议的名称,域名的后面,还跟了一长串字符~~ 称为查询字符串(query string),查询字符串中表达的意思,作为外人是无从得知的,这里面的内容就是写这个代码的程序员定义的~~

我们可以看到,在查询字符串中,也是键值对的格式。但此处的键值对,是使用 & 分割键值对,使用 = 来分割键和值~~

一个完整的 URL 结构如下:

http:协议方案名,常见的有 http 和 https,也有其他的类型(我们前面访问 mysql 的时候使用 jdbc:mysql)(可以省略,省略后默认为 http://)

user:pass:登录信息,之前是直接会把相关认证信息直接在 URL 中展示的,后来发现实在是不太安全~~ 现在都是通过单独的登陆页面来完成身份验证~~

www.example.jp:80:服务器地址,服务器端口号,这里的域名,也可以是 IP 地址,后面带有端口号,可以表示我们要访问服务器那个端口,如果 URL 中不带端口号,浏览器就会自动给一个默认的端口(这里的服务器端口,不是客户端中系统随机分配的那个),此处用什么端口作为默认值,取决于我们的协议是什么:http -- 80    https -- 443

dir/index.html:带有层次的文件路径

uid=1:查询字符串,是客户端给服务器传递信息的重要途径,这里的组织方式是按照键值对来组织的,这里面的键值对内容,是程序员自定义的~~

ch1:片段标识符,主要用于页面跳转,常用于通过不同的片段表示跳转到文档的不同章节

结合上述的 IP 地址,端口号,带层次的文件路径,查询字符串,就可以描述出一个网络资源了:

http://中北大学文韬餐厅:20/手抓饼/番茄手抓饼/?辣椒=少放&香菜=多放

关于 URL encode

在 query string 中都是程序员自定义的键值对,但在 URL 中,本身有些特殊符号具有特特定的含义: /   :   ?  @  .....

如果 URL 中的 query string 中也包含同样的符号,怎么办呢??? ==》 如果直接写进 query string 中,就可能会使服务器 / 浏览器解析失败!!

一个靠谱的办法,我们前面已经在代码中使用过了:对上述符号进行“转义”。包括中文字符或者是其他由 UTF-8 或者 GBK 这样的编码方式构成的字符,虽然在 URL 中没有特殊含义,但浏览器可能会把编码中的某个字节当作 URL 中的特殊符号,也需要进行转义~~

转义的规则如下:将需要转码的字符的转为 16 进制表示,前面加上 % 即可

比如我们搜索 C++:

可以看到,query string 中显示的是 C%2B%2B

我们可以通过 ASCII 码表发现,+ 的十六进度是 2B,则对应了 C++ ==》 C%2B%2B

我们也可以搜索一个女神

在上面虽然显示的 query string 直接是女神,是浏览器自动进行了解析,我们可以复制粘贴出来到文本文档就显示出来了:

我们“女神”进行 utf-8 编码:

结果相符~~

我们这里的 urlencode 编码是非常非常重要的~~ 在实际开发中,当我们要构造一个 URL 的时候,尤其是 URL 的 query string 中要包含中文的时候,务必要进行编码!!

认识方法(method)

请求的首行中,包括了 方法 URL 版本号

方法就描述了这次请求,想干啥(目的)

1. GET 方法

GET 是最常用的 HTTP 方法,常用于获取服务器上的某个资源(读操作)。在浏览器中直接输入 URL,此时浏览器就会发送出一个 GET 请求。另外,HTML 中的 link,img,script 等标签,也会触发 GET 请求。

使用 Fiddler 观察 GET 请求:

在浏览器访问百度主页,观察抓包结果:

我们可以看到,最上面蓝色的,是通过浏览器地址发送的 GET 请求,下面的

下面的一个写 baidu 域名相关的请求,有些是通过 HTML 中的 link/script/img 标签产生的。有些是通过 ajax 方式产生的。

我们选中第一条,观察请求的详细结果:

GET 请求的特点:

首行的第一部分为 GET

URL 的 query string 可以为空,也可以不为空

header 部分有若干个键值对结构

body 部分为空

2. POST 方法

POST 方法也是一种常见的方法,多用于提交用户输入的数据给服务器(例如登录页面)

我们可以登录 gitee 来进行一下抓包~

对第一个请求,点击查看详情

POST 请求的特点:

首行的第一部分为 POST

URL 的 query string 一般为空(可以不为空)

header 部分有若干个键值对结构

body 部分一般不为空,body 内的数据格式通过 header 中的 Content - Type 指定。body 的长度由 header 中的 Content - Length 指定~~

面试题:POST 和 GET 的区别

先盖棺定论:GET 和 POST 没有本质区别。使用 GET 的场景,也可以替换成 POST,使用 POST 的场景,也能替换成 GET(这里主要是取决于程序员代码是怎么写的,尤其是服务器和客户端都是同一个程序员实现的情况~~)(部分服务器 / 部分浏览器,在某些情况下 GET 和 POST 不能完美替换,但是大部分情况下,相互替换问题一般不大~~)

但是 GET 和 POST 还是在使用习惯上有一些区别的~~

1. GET 习惯于把数据放到 URL 的 query string 中。POST 习惯于把数据放到 body 中。

(GET 也可以把数据放到 body,POST 也可以把数据放到 query string -- 有些可能不支持,绝大部分都支持)

2. 语义上的区别。在标准文档中,GET 的语义是用来获取数据的,POST 的语义是给服务器传输数据的。(实际使用,当然还是程序员自己说了算~~)

3. 关于幂等性,在标准文档中,建议 GET 请求实现幂等的,POST 无要求。

(幂等:是一种数学术语,在计算机中也很常见~~ 即每次输入的内容一定,输出的结果也一定,就称为是幂等的,如果每次输入的内容一定,输出的结果不一定,就不是幂等的)

(GET 在实际开发中,也不一定非要实现幂等,标准是这么建议的,但我们可以不听标准的建议~~)

网上的一些不太准确的相关说法:

1. POST 比 GET 更加安全~~

论据:登录的时候,如果使用 GET,用户密码就会显示在 URL 中,此时会被别人看到,就不安全...

如今用户密码都会有一个单独的登录页面了,不会直接在 URL 中,即使是 POST ,即使没有显示子啊 URL 中,也是可以被黑客进行抓包获取的...真正保证安全性的关键在于加密!!

2. GET 传输的数据量小(存在上限),POST 传输的数据量更大

这句话描述的是以前的情况,以前是以前,现在是现在~~

实际上,HTTP 标准文档上明确说了,对于 GET,URL 的长度是不进行限制的,只不过是之前老版本的 IE 浏览器在实现的时候,URL 的长度有限制(现在已经没有了~~)

3. GET 只能携带文本数据,POST 可以携带二进制数据

这个说法,并不是完全错误,只是有一些局限性~~

URL 通过 query string 来携带数据

query string 是只能包含文本的,是对二进制数据进行 urlencode 了,自然就成为文本了。到了服务器再进行 urldecode,就能把数据还原成二进制。

POST 请求中,bodu 中虽然可以携带二进制数据,但也不是经常携带的,很多时候是对二进制数据进行 urlencode / base64 等等方式进行转码的~~

其他方法使用较少~~

认识请求“报头”(header)

header 的整体格式也是“键值对”结构

每个键值对占一行,键和值之间使用分号进行分割~

Host

表示服务器主机的地址和端口

(URL 中已经有 Host 了,这里的 Host 和 URL 中的 IP 地址 端口什么的,绝大部分情况下都是一样的~~)

Content-Length   Content-Type

Content-Length 表示 body 中的数据长度。

Content-Type 表示请求的 body 中的数据格式。

(和 body 密切相关,如果当前数据包没有 body,也就不会有这两个字段)

如果没有这两个字段,body 的请求/响应,就会直接使用空行作为分隔符了,如果有 body,空行就不是结束标志了,从空行开始来读取 body,body 要读多长就取决于 Content-Length,读完之后,这个包就结束了

Content - Type 中的常见选项:

请求中:

1) application/json:body 中的数据为 json 格式,形如:

{"username":"123456789","password":"xxx","code":"aaa","uuuid":"d110a05ccde65da13"}

2) application/x-www-form-urlencode:称为 form 表单,通过 HTML 中的 form 标签构造出来的一种格式,这种格式的特点是,把 query string 放到 body 中了

3) multipart/form-data:当需要在表单中上传文件,或者包含二进制数据的时候使用。在 HTML 的 form 标签中需要设置 enctype=“multipart/form=data“。

例如:

使用 boundary(边界标识符,如 ---------WebKitFormBoundaryrGKCBY7qhFd3TrwA)来分割不同表单数据部分。每个部分包含自身的元数据(如 Content-Disposition 指定数据类型和名称,Content-Type 指定具体的数据类型)。

响应中:

1) text/plain 纯文本

2) text/html   html 页面

3) text/css  css 页面

4) application/javascript js

5) application/json

6) image/png

7) image/jpg

.....................

补充:

我们后面自己写服务器程序返回网页,如果发现网页乱码,就可以检查一下是否是这里的编码方式没有设置或者是设置的不对~

User-Agent(简称 UA)

表示浏览器/操作系统的属性,描述了用户使用什么样的设备上网

Windows NT 10.0 Win64; x64 ==> 操作系统信息

Chrome/121.0.0.0 Safari/537.36 Edg/135.0.0.0 ==》 浏览器信息~~

上古时期,UA 是非常关键的内容,由于计算机发展迅速,不同用户使用的上网设备,差异很大,UA 就可以表明该用户上网的设备具体是什么信息,如何用户使用的是比较老的设备,返回的页面就不含新特性,确保这个页面可以正确的访问出来。如果用户使用的是新设备,返回的页面就包含新的特性,确保这个页面体验感足够好~~

随着时间推移,浏览器现在都差不多了~~ 但 UA 仍然很有用,很多网站在 PC 端和手机端,由于其设备的大小区别,返回的页面布局也会有所差异,这就是通过 UA 来进行控制的~~

Referer

表示这个页面是从那个页面跳转过来的。在浏览器中,直接输入 URL 或者点击收藏夹打开的网页,此时是没有 Refere 的

举例:

我有个朋友~~~当年做的是广告页面~~~搜索广告中,都是按照点击计费的,用户每次点击广告,负责的企业都能赚钱,所以必须要记录某个广告在某个时间段内被点击多少次。

比如说在百度中搜索“肾虚”,当我们用户点击一下,百度就能到账 money~~

这个点击次数,百度和广告主都要进行统计~~

百度中,用户点击就会把请求发送到百度的计费服务台,通过记录日志就知道了~~

广告主中,就在他的服务器上记录日志,但一个广告,一般不会仅仅就找一家平台,他会找 360,搜狗等等~他怎么知道那些请求是百度的广告引流过来的呢??? ==》借助 referer 即可~~

问题来了 referer 是否会被篡改呢??? ==》 很有可能!!

因此就有了 HTTPS 的出现~~ 这个 S 其实是 SSL(网络中用于加密的协议~~)

加密就能把 header 和 body 都进行加密,网络上传输的就是密文了~~

其他人要想修改,就得先进行破解,就算能破解,也无法篡改,一旦进行修改操作,用户的浏览器就能感知到了~~~

Cookie 本质上是一个浏览器这边本地持久化存储数据的机制。

浏览器作为电脑上的一个程序,是可以直接读写本地磁盘文件的,系统提供了一个 API 操作文件。注意的是,浏览器上运行的网页,理论上也是可以通过浏览器提供的 API 来读写本地的磁盘文件,但是浏览器禁止了这种做法(浏览器并没有给网页提供这样的 API) (不安全~~ 万一黑客通过网页,直接把我们 C 盘中的学习资料删了怎么办??? /哭 /哭 ~~)

但话又说回来,有些网站,是需要把一些信息保存到浏览器这边的,比如当前登录的用户身份信息~~ 浏览器退而求其次,给网页提供了 API,但这个 API 只能是有限度的存储数据,而不能随意的访问文件系统~~

HTTP 请求中的 Cookie 字段,就是把本地存储的 Cookie 信息发送到服务器这边。

HTTP 响应中会有一个 Set-Cookie 字段,就算服务器告诉浏览器你要在本地保存那些信息。

上述的请求和响应字段,都是以键值对的形式进行的(程序员可以自定义~~)

Cookie 中存储了一个字符串,这个数据可能是客户端(网页)自行通过 JavaScript 写入的,也可能是来自于服务器(服务器在 HTTP 响应的 header 中通过 Set-Cookie 字段给浏览器返回数据)

一般可以通过这个字段来实现“身份标识”的功能。(每个不同的域名下都可以有不同的 Cookie,不同网站之间的 Cookie 并不冲突~)

举个例子:

Cookie 的作用,非常类似于超市买东西时候的购物卡流程~~

1. 注册会员(初始设置 Cookie)

当我们第一次去超市,注册称为会员之后,超市会给我们办一张会员卡,这就类比是服务器给客户端设置 Cookie。会员卡上记录了我们的信息,比如姓名,会员卡号,积分等等,这些信息就如同 Cookie 里面存储的用户标识等基础数据

2. 首次购物(第一次请求携带 Cookie)

当我们再收银台结账的时候,收银员会扫描我们的会员卡。这就类似于客户端向服务器发起请求的时候带上了 Cookie。收银员通过会员卡,就可以知道我们是会员,会根据会员的政策给我们积分或者相关的优惠,就像服务器根据 Cookie 里面的信息,来识别出用户,并作出响应的处理,比如提供个性化的页面展示或服务~~

3. 再次购物(后续请求携带 Cookie)

之后我们再去超市购物,结账的时候每次都出示会员卡。收银员每次都能够通过会员卡来了解到我们之前的购物记录,积分情况等,然后根据这些信息为我们提供服务~~比如提醒我们,积分快能兑换礼品了,或者根据我们的购物偏好,来推荐一些商品。这就如同客户端后续每次向服务器发送请求时,Cookie 持续发挥作用,让服务器可以基于之前的交互历史,持续为用户提供连贯,个性化的服务~~

流程图如下:

关于 Cookie 的几个重要结论:

1. Cookie 从哪里来???

服务器返回给浏览器的。通常都是首次访问/登录成功之后。

2. Cookie 到哪里去???

Cookie 会存储在浏览器本地主机的硬盘上。后续每次访问服务器都会带上 Cookie。不同的客户端,保存的 Cookie 是不同的,即使是同一个主机,使用不同的浏览器,Cookie 大概率也不同。

3. Cookie 中存储什么???

Cookie 以键值对的形式对数据进行存储。这里的内容都是程序员自定义的,和 query string 一样,外人无从理解~ 不同的网站的 Cookie 是不同的~

4. Cookie 在浏览器这边如何组织???

在硬盘本地保存,是按照不同的域名为维度,分别进行存储。

5. Cookie 的用途是什么???

用来在客户端保存数据,其中最主要的就是保存用户的身份标识,使得服务器可以通过标识来区分用户了,一般其他的业务不会存在 Cookie 中~~

我们可以通过抓包来观察一下页面登录的过程(以 gitee 为例):

1) 清除之前的 Cookie

2) 登录操作:

登录请求:

登录响应:

可以看到,响应中包含了 Set-Cookie 属性

其中我们重点看一下第二个,里面包含了一个 gitee-session-n 这样的属性,属性值是一串很长的加密之后的信息。这个信息就是用户当前登录的身份标识,也称为“令牌(token)”。

3) 访问其他页面

当我们登录成功之后,此时访问码云中的其他页面(比如个人主页),请求中就会带着刚刚获取到的 Cookie 信息。

我们的请求中的 Cookie 字段也包含了一个 gitee-session-n 属性,里面的值和刚才服务器返回的值相同,后续只要访问 gitee 这个网站,就会一直带着这个令牌,直到令牌过期/下次重新登录~~

比如我们此时再访问一个“我的星选集”,此时请求中的 Cookie 还是和之前一样的~~

理解登录过程:

这个过程再结合我们前面去超市买东西例子:

首次进入超市后,就是打开登录页面。当我们第一次结账提供手机号信息办会员卡的时候,就是输入用户名密码,点击登录,此时就得到了一张“会员卡”,这张会员卡就相当于是我们的“令牌”。

此后再去超市进行购物,积分兑换奖品等等操作,就不需要再提供手机号了,只需要凭会员卡就可以识别出我们的身份了。

当我们要搬家的时候,不想要会员卡了,就可以注销这个卡,此时我们的身份就和会员卡的关联就销毁了(类似于网站的退出登录操作)

当我们之后再搬回来,又想在超市买东西,就可以再办一张会员卡,此时就得到了一个新的令牌~~

认识请求“正文”(body)

正文中常见的就是我们上面 Content - Type 中的一些格式内容~~

完!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/903533.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

实现MySQL高可用性:从原理到实践

目录 一、概述 1.什么是MySQL高可用 2.方案组成 3.优势 二、资源清单 三、案例实施 1.修改主机名 2.安装MySQL数据库(Master1、Master2) 3.配置mysql双主复制 4.安装haproxy(keepalived1、keepalived2) 5.安装keepaliv…

CSS学习笔记8——表格

一、表格 1-1、创建表格 在Word文档中,如果要创建表格,只需插入表格,然后设定相应的行数和列数即可。然而在HTML网页中,所有的元素都是通过标签定义的,要想创建表格,就需要使用与表格相关的标签。使用标签…

爬虫学习笔记(一)

目的 通过编写程序爬取互联网上的优质资源 爬虫必须要使用python吗 非也~ 编程语言知识工具,抓取到数据才是目的,而大多数爬虫采用python语言编写的原因是python的语法比较简单,python写爬虫比较简单!好用!而且pyt…

大厂面试:MySQL篇

前言 本章内容来自B站黑马程序员java大厂面试题和小林coding 博主学习笔记,如果有不对的地方,海涵。 如果这篇文章对你有帮助,可以点点关注,点点赞,谢谢你! 1.MySQL优化 1.1 定位慢查询 定位 一个SQL…

C++_数据结构_详解红黑树

✨✨ 欢迎大家来到小伞的大讲堂✨✨ 🎈🎈养成好习惯,先赞后看哦~🎈🎈 所属专栏:C学习 小伞的主页:xiaosan_blog 制作不易!点个赞吧!!谢谢喵!&…

DNA复制过程3D动画教学工具

DNA复制过程3D动画教学工具 访问工具页面: DNA复制动画演示 工具介绍 我开发了一个交互式的DNA复制过程3D动画演示工具,用于分子生物学教学。这个工具直观展示了: DNA双螺旋结构的解旋过程碱基互补配对原理半保留复制机制完整的复制周期动画 主要特点…

使用阿里云 CDN 保护网站真实 IP:完整配置指南

使用阿里云 CDN 保护网站真实 IP:完整配置指南 一、宝塔面板准备工作1. 确认网站部署状态2. 宝塔中检查网站配置 二、配置阿里云 CDN1. 添加域名到 CDN2. 配置 DNS 解析3. 配置成功确认 三、宝塔面板安全加固(隐藏 IP 的关键步骤)1. 禁止通过…

PHP经验笔记

isset — 检测变量是否设置,并且不是NULL; 若变量存在且值不为NULL,则返回 TURE 若变量存在且其值为NULL或变量不存在,则返回 FALSE 结论 1. 当变量为空字符串、数值0和布尔值false时,isset全部返回true 2. 当变量不存在和变量存在且值为NULL…

Linux——安装NVM

1. 安装命令 官方地址:https://github.com/nvm-sh/nvm curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.3/install.sh | bash2. 安装完成后执行命令 source ~/.bashrc3. 验证 nvm -v

CentOS 7 磁盘阵列搭建与管理全攻略

CentOS 7 磁盘阵列搭建与管理全攻略 在数据存储需求日益增长的今天,磁盘阵列(RAID)凭借其卓越的性能、数据安全性和可靠性,成为企业级服务器和数据中心的核心存储解决方案。CentOS 7 作为一款稳定且功能强大的 Linux 操作系统&am…

C++每日训练 Day 18:构建响应式表单与数据验证(初学者友好)

📘 本篇目标:在前几日协程与事件驱动机制基础上,构建一个响应式表单系统,实现用户输入的异步验证与反馈。通过协程挂起/恢复机制,简化异步逻辑,提升代码可读性。 🔁 回顾 Day 17:响应…

Vue初步总结-摘自 黑马程序员

本文摘自 bilibili 前端最新Vue2Vue3基础入门到实战项目全套教程,自学前端vue就选黑马程序员,一套全通关! 更多详情可参考: https://www.yuque.com/u26161316/pic6n4/heyv8nv8ubfk3fhe?singleDoc# 《Vue》

【基于Qt的QQ音乐播放器开发实战:从0到1打造全功能音乐播放应用】

🌹 作者: 云小逸 🤟 个人主页: 云小逸的主页 🤟 motto: 要敢于一个人默默的面对自己,强大自己才是核心。不要等到什么都没有了,才下定决心去做。种一颗树,最好的时间是十年前,其次就是现在&…

线程池(二):深入剖析synchronized关键字的底层原理

线程池(二):深入剖析synchronized关键字的底层原理 线程池(二):深入剖析synchronized关键字的底层原理一、基本使用1.1 修饰实例方法1.2 修饰静态方法1.3 修饰代码块 二、Monitor2.1 Monitor的概念2.2 Moni…

Linux CentOS 7 安装Apache 部署html页面

*、使用yum包管理器安装Apache。运行以下命令: sudo yum install httpd *、启动Apache服务 sudo systemctl start httpd *、设置Apache服务开机自启 # 启用开机自启动 sudo systemctl enable httpd# 禁用开机自启动 sudo systemctl disable httpd *、验证Apac…

前端设置三行文本省略号,失效为什么?

实际效果:第三行出现省略号,但是第四行依旧显示了部分文字 这个问题通常是由于 CSS 多行文本截断(-webkit-line-clamp)的计算方式或布局冲突导致的。以下是完整解决方案,确保三行文本截断正确显示省略号,并…

git学习之git常用命令

1. 初始化仓库 git init初始化一个新的 Git 仓库。 2. 克隆远程仓库 git clone <repository-url>从远程服务器克隆一个已有仓库到本地。 3. 配置用户名和邮箱 git config --global user.name "Your Name" git config --global user.email "youexampl…

【Spring Boot】深入解析:#{} 和 ${}

1.#{} 和 ${}的使用 1.1数据准备 1.1.1.MySQL数据准备 &#xff08;1&#xff09;创建数据库&#xff1a; CREATE DATABASE mybatis_study DEFAULT CHARACTER SET utf8mb4;&#xff08;2&#xff09;使用数据库 -- 使⽤数据数据 USE mybatis_study;&#xff08;3&#xff…

Poco C++全面开发指南:日期和时间

时间戳 时间戳是指格林威治时间1970年01月01日00时00分00秒&#xff08;北京时间1970年01月01日08时00分00秒&#xff09;起至现在的总秒数。在poco中可以可以使用Timestamp类获取。 #include <Poco/Timestamp.h> #include <iostream>int main() {Poco::Timestam…

水利三维可视化平台怎么做?快速上手的3步指南

分享大纲&#xff1a; 1、了解水利三维可视化平台 2、选择合适的开发平台 3、快速搭建水利三维可视化平台 第一步&#xff1a;了解水利三维可视化平台 水利三维可视化平台是利用大数据、物联网、数字孪生等技术&#xff0c;将物理实体数字化建模&#xff0c;并通过三维可视化技…