发布于2023-06-20 19:52 阅读(1288) 评论(0) 点赞(30) 收藏(3)
哈喽,大家好~我是你们的老朋友:保护小周ღ
谈起 HTTP 协议(超文本传输协议),不知道大家第一次是从什么地方了解到这个协议的呢?在真实的网络环境中网络协议的种类非常多,其中有一些耳熟能详的协议,网络层的 IP 协议, 传输层的 TCP, UDP 协议, 以及应用层的 HTTP/HTTPS. 站在 webapp(网站)的开发角度接触到的最多的就是 HTTP 协议,HTTP 究竟是一个什么样的协议呢,一起来看看叭~
本期收录于博主的专栏:网络编程_保护小周ღ的博客-CSDN博客
适用于编程初学者,感兴趣的朋友们可以订阅,查看其它 “网络基础知识”。
更多精彩敬请期待:保护小周ღ *★,°*:.☆( ̄▽ ̄)/$:*.°★* ‘
在真实的网络环境中采用 TCP/IP 五层网络传输模型这样的结构传输.
物理层 -> 数据链路层 -> 网络层 -> 传输层 -> 应用层
1. 应用层: 应用层是模型的最顶层,它为用户提供了一种与网络进行通信的方法。应用层包含了各种应用程序,包括 Web 浏览器、电子邮件、文件传输协议等,这些应用程序可以通过构建应用层的数据报,并调用传输层传输数据.
2. 传输层: 管理两个节点之间的数据传输,负责可靠的传输,传输层不关注中间传输的路径,只关注起点和终点并确保数据被可靠的传送到目标地址。
3. 网络层: 地址管理,记录源目标主机的ip 和 目的主机ip , 路由转发:负责给两个节点选择一条可靠的高效的传输路径——路径规划。
4. 数据链路层:负责连接设备之间传输数据帧,主要关注两个相邻设备节点之间的传输,例如通过网线 / 光纤 / 网络接口,连接的交换机、网卡之间的数据传输。
5. 物理层:网路通信的基础设施设备:网络、光纤等一系列传输介质。
上层协议调用下层协议, 下层协议为上层协议提供服务, 不可以跨层调用。
如此数据就是按照这种属性协议格式在网络中传输的。
HTTP 协议属于 TCP /IP 五层网络协议模型的应用层
HTTP 协议目前有三个大版本:
HTTP / 1 和 HTTP / 2 都是基于TCP 传输控制协议传输数据。最新版本的 HTTP / 3 是基于 UDP 协议的。当前互联网上传输数据大部分使用的版本是 HTTP / 1.1
HTTP 协议具体是什么? ——应用层协议又是什么?
应用层协议可以理解为是组织数据的一种格式,需要发送方和接收方约定相应的格式.
举个例子(假设):QQ的应用层数据报
应用层协议,大部分场景下,是程序猿根据实际的需求场景来自行设计的,但是有一种场景就是在程序员的圈子里,专业技术水平不一,有的程序猿设计的应用层协议非常好用,有些程序猿设计的应用层协议bug 连连,在这种情况下,就有大佬发明了一些很好用的协议,其他程序猿就可以参考大佬的设计的协议来传输数据,在使用大佬协议的同时也可以根据实际需要,让协议传输各种自定义的数据信息。其中非常出名的就是 HTTP 协议,描述的应用层是如何组织数据的。
协议格式—— 数据具体是怎么组织的。
例如:UDP 传输协议: 报头(源端口, 目的端口,长度,校验和)+ 载荷
TCP 传输协议:
UDP / TCP / IP 协议都属于 “二进制” 的协议,需要站在二进制的角度理解数据。
HTTP 协议是一个文本格式的协议(会转换为二进制传输),所以我们可以只需要理解文本的格式。
如何看到 HTTP 的报文格式呢?
我们可以借助一些 “抓包工具” 来获取设备交互的数据,可以抓取 http 协议的数据包。
如果只是针对 HTTP/HTTPS 协议的数据进行抓包,可以使用 Fiddler 这个工具,如果需要抓取 UDP / TCP / IP / mac 地址 …… 可以使用 wireshark 这个工具。
Fiddler (下载地址: https://www.telerik.com/fiddler/)
抓包工具的原理:数据在网络中传输,会经过许多中转设备,比如:路由器寻址,数据会经过这些设备传输,抓包工具就可以认为是一个中转设备,当数据经过该设备时自然可以进行“捕获”,数据传输又是按照标准的协议来传输,也就能按照一定的格式来解析,当然https 会对数据进行加密传输,虽然也可以捕获https 的数据包,如果没有一定的“手段”是看不懂数据的(下篇文章解析),只有数据传输的双端通过约定好的密文解析。
HTTP(Hypertext Transfer Protocol)是一种基于请求-响应模式的应用层协议,用于在Web中传输数据。HTTP请求是客户端向服务器发送的请求,请求中包含了请求行、请求头和请求体三部分。
例如:使用搜狗搜索引擎搜索一个 “小狗” , 那么浏览器就会自动将这条搜索记录打包成一条 http 数据包,作为请求发给搜狗地服务器,搜狗的服务器,拿到 客户端的i请求,解析后可以得到“小狗” 这个关键字,服务器就能将小狗作为关键字对数据库的进行模糊匹配,从数据库拿到数据后,就可以根据拿到的数据继续处理,比如说构建一个页面啊,最后将数据打包为 http 格式,作为响应返回给客户端,于是我们就可以页面上看到关于小狗的信息。
请求:
query = 后的数据就是我们向服务器请求的关键字“小狗”,这里只是将这些字符进行转义了,本质上就是把要转义的字符的以 Ascll 码取出来,用16 进制表示,同时加上%每个字节都是这个处理,所以是这样的一个结果(详细内容下文讲述)。
http 的请求分为四个部分: 1. 请求首行,2. 请求头, 3. 空行,4. 请求正文(body),根据不同的请求格式,不一定有。
首行包含三个部分:
HTTP定义了一些方法(也称为动作或动词),用于描述客户端与Web服务器之间的通信类型。
以下是常见的一些方法。
请求中的方法大概描述了这个请求数据报想干什么,比如说,GET 请求:想要从服务器上获取某些资源,很自然的想象到了搜索框这个场景。
HTTP协议的方法有很多种,最常用的是 GET 和 POST 方法。
URL代表统一资源定位符,详细的描述了要访问的网络上的资源具体在哪里,
既要明确资源主机是谁,也要明确主机上的资源,通过浏览器,打开网页搜索的时候,地址栏里填写的这个“网址”。
1. 协议方案名
描述当前 URL 是在那个协议的组织下的格式,通过指定协议,用户的浏览器或者其他应用程序可以根据协议规定的方式,正确地打开和加载资源。例如,如果URL的协议方案是HTTP,则浏览器会使用HTTP协议与服务器建立连接,获取资源并呈现到用户屏幕上。如果协议方案是FTP,则浏览器会使用FTP协议访问资源。URL中的协议方案名对于正确访问和加载资源非常重要。
2. 登录信息
在早期的 http 格式中,会在URL 中显示用户名,密码,用于验证身份,现在基本上都使用了 https 协议,针对传输的数据进行ssl 加密。
3. 服务器地址
在网络中传递资源,实实在在的少不了 IP 地址, IP 地址是设备在网络中的唯一身份标识,两个设备建立连接就需要在茫茫网络世界中找到彼此。一般情况下我们看到的都是域名,比如:www.baidu.com , 这是百度服务器的 IP 地址,这涉及到DNS域名解析,本质上将域名(一定的格式) 跟 IP 地址(192.168.255.255)形成映射 ,因为使用域名对于用户来讲友好一点,方便记忆,从某种角度来看,相较于 IP 那一串数字,也更显得美观,域名最终还是会被 DNS 翻译成 IP 地址使用的。
4. 端口号
端口号表示当前设备上的那个应用程序,例如:http 服务器的端口号:80,http 服务器本质上也是代码编辑的应用程序,方便发送和解析 http 格式的数据报,Java 这一块较为常用的是 tomcat,他就是Java代码编写的http 服务器,默认绑定端口号是 8080,提供的一组操作 http 协议的 API(接口)——servlet
每个联网的应用程序,在启动的时候就会自动地绑定一个 端口号,用作设备上应用程序的身份标识,例如:qq 的消息,不会被 微信接收,微信的消息也不会被 qq 接收,但是这两个应用程序的消息一定会被我们使用的设备所接收,然后根据解析获取端口号,才能进一步的决定将数据交由那个应用程序来解析。
端口号在大部分情况下是省略不写的,原因是浏览器会根据协议方案名自动赋予一个默认的端口,对于 http 开口的URL 默认是使用 80 端口作为默认值,对于 https 开头的URL 默认是使用 443 端口。
5.带层次的文件路径
服务器作为一个应用程序,存储资源的方式可以是硬盘,或者数据库,就比如说,百度浏览器搜索“小狗”, 百度服务器,就从本地寻找关于小狗的相关信息,或者是从硬盘?—— 文件路径:D:/ 图片/ 小狗.jpg ,或者是从数据库?数据库/ 数据表/ 字段(小狗)。
上述的 IP地址 + 端口 + 带层次的文件路径,其实就可以描述一个网络上具体的资源。
6. 查询字符串
本质上是浏览器/ 客户端,给服务器传递的自定义的信息,常用于描述想要进一步获取资源,例如:上述实例中的搜索小狗,小狗就会作为查询关键字,由服务器决定将那些数据响应给客户端,查询字符串的内容,是键值对的结构——key : value 例如:姓名 = 张三,根据键可以获取相应的值,这些键值对的结构,完全是程序猿自定义决定。
比如说:登录界面总会由:用户账号,密码
程序员就可以针对这个登录界面设置一组键值对,username = ?, password = ?
当用户输入数据后,程序就可以拿到用户数据来构造这个键值对,服务器那边有一种获取数据的方式就是通过写死的键值(key)username ,来获取 value 值 "张三", password = 123456。
查询字符串的内容,是键值对的结构,键和值之间用 “=” 分割,键值对之间由 “ & ”分割, 查询字符串与 路径之间采用 “?” 分割。
7. 片段标识符
用于标识资源中的特定位置或片段,通常是在网页内部使用,能控制浏览器滚动到相关的位置。
URL 小结:
按照标准的URL 的结构来看确实比较复杂,但是跟程序开发关系最紧密的有四个部分:
1. IP 地址/ 域名
2. 端口号(一般不显示)
3. 带层次结构的路径
4. queryString (查询字符串)
1~3 描述了网络中具体的资源,4 描述的在此资源基础上提出进一步的要求
当queryString 中包含了特殊字符,例如:/ , : ,?,&,= 这些字符都是在 URL 中具有特殊含义的,所以就需要对特殊字符进行转义,避免服务器解析错误,转义的过程我们可以使用 url encode的编码方式。在解析的时候针对查询字符串使用 url decode 进行反转义。
URLEncode是一种编码方式,用于对URL中的特殊字符进行编码。URL地址中有一些特殊字符,如“空格”、“&”、“#”等,在提交过程中需要对这些特殊字符进行编码,否则可能会导致URL地址无法正确解析。使用URLEncode对URL进行编码后,可以将特殊字符替换成“%”加上其对应ASCII码的十六进制表示形式。
例如,对于字符串“hello world”,URLEncode编码后的结果是“hello%20world”,其中“%20”表示空格的ASCII码16进制表示“0x20”。
对于 http 的 GET 方法请求,其实就是利用 URL 中的 queryString 给服务器传递一些信息,所以在开发中一定要有对特殊字符进行转义的意识。
HTTP协议的版本号是在请求和响应中出现的,用于表示请求或响应所使用的HTTP协议的版本。这是因为不同的HTTP协议版本采用不同的协议规范,因此请求及响应头部的格式、支持的方法、状态码等都有可能不同,因此指定HTTP协议版本可以确保客户端和服务器使用的HTTP协议版本一致,避免因版本不同导致的错误。此外,通过在请求和响应中包含协议版本号也可以方便地识别出所使用的协议版本,便于协议的维护和演化。
在HTTP/1.1中,请求和响应头部中都包含了HTTP版本号信息。
举个简单的例子,HTTP 协议有个三个版本, HTTP1 和 HTTP2 基于 TCP 协议实现,HTTP3 基于 TCP 协议实现,所以一个 http 数据报中包含 版本号是很有必要的
请求头中包含了很多行,每一行都是一个键值对,键和值之间使用 “ :”
HTTP协议的请求头是客户端发给服务端的一种键值对的结构,它包含了客户端发送请求的一些相关信息,如请求方式、请求的资源地址、HTTP版本、请求时的时间、请求的编码方式等。这些请求头信息能够帮助服务器更好地理解客户端请求的意图和需要,对应的回复也会更加精准和高效。这里的键值对个数是不固定的,不同的键值对表示的含义也不同。
以下键值对可以参考博主上下文中的 GET 或 POST 请求抓包的图片。
Host : www.baidu.com Host 描述 IP地址+ 端口号
域名通过 DNS 解析转换成 IP 地址, IP 跟 域名是映射关系。
Content-Length : 描述了 正文body 中的数据长度, 根据这条信息,就能够精确的从 body 中读取完整的数据,涉及到 IO 流的处理,采用 TCP 协议传输,是建立在字节流的基础之上。
TCP 是一个面向字节流的协议,解决粘包问题 -> 合理设计应用层协议,来明确包和包之间的边界。
使用分隔符
使用长度
如果当前有若干个 GET 请求,到了 TCP 接收缓冲区中了,
应用程序读取请求的时候就以 空行 为分隔符。
如果当前是有若干个 POST 请求,到了 TCP 缓冲区了~~
这个时候,空行后面还有body ~~ 当应用程序读到了空行之后,就需要按照 Content-Length 表明的长度,继续读取若干个长度的数据~~
Content-Type:表示请求的body 中的数据格式,常用的是 json 格式,或者是前端的 form 表单提交,也可以同时指定 字符集: UTF-8
User-Agent( UA):
表示的是,当前用户使用啥样的设备来上网的,主要描述了操作系统的信息和浏览器的信息
在早期的网站开发中设置 UA 的目的是为了解决页面的兼容性问题,客户端发出请求后,服务器跟据不同的操作系统,不同的浏览器版本,决定返回怎样的页面——HTML + CSS + JavaScript ,例如尺寸呐,界面布局啊。
到了互联网高速发展的今天,主流的浏览器的功能差别已经很小了,十年前浏览器的兼容性,还是前端开发要考虑的大问题,现在前端开发依然需要考虑兼容性问题,但是相对之前来讲确实是方便了很多——前端开发中的响应式布局,UA 在如今有了新的使命,就是可以用来区分 PC 端和手机端,这两个设备上的浏览器展示的页面效果可是很明显的,有一种做法是前端会做两张页面,根据请求的 UA 判断设备,然后决定响应那张页面。
通过 Content-Type : 浏览器就可以判断使用那种格式来识别数据, 如果是前端代码, 就可以直接渲染页面,展示在用户界面上。
Referer : 描述当前的页面是从拿个页面跳转过来的
Referer : 这个键值对不一定有,如果是从地址栏直接输入地址,或者是点击收藏夹,这个时候就没有 Referer 。
如果在搜狗的搜索引擎中搜索 “小狗” ,此时的请求中 referer 字段就可以完美展示出该请求来自 搜狗
那这个键值对有什么用呢?有一个很常用的功能,广告系统,按照点击来计费,浏览页面有广告链接,点击之后,就会触发请求,服务器就可以根据收到请求的 Referer 键值对来识别,是从那个平台发来的请求,从而达到计数的功能。比如说,搜狗的平台,我投放的广告,被多少用户看到了,点击了,统计出来后我需要给搜狗平台交钱~
当然 http 的请求有没有可能被别人截获了,并篡改了 Referer 中的内容呢,这个是可以的,毕竟一次点击可都是钱~ 但是如今都使用 https 协议来传输数据,就难以实现了,https 会对数据进行加密,可以被截获,几乎不可能被篡改。
Cookie 是浏览器给页面提供的一种持久化存储数据的机制,浏览器为了用户数据安全,默认不允许前端的代码访问用户设备上的数据,防止恶意代码,爬取用户数据,或者植入病毒,恶意删除数据等情况,但是有时候需要页面持久化存储一些数据,方便以后访问网页,比如:账户信息,细心的朋友可以发现,一些网站登录过一次之后,下次访问就不需要登录,直接就进入的主页,其实这个验证的过程是一直存在的,只是在用户看不到的地方,发送的验证请求,免去了用户重复登录的繁杂,提高了用户体验感。
首先不同浏览器有不同的 Cookie , Cookie 会针对每一个 域名(例:www.baidu.com)在硬盘上分配一块空间,这一块空间是以键值对的方式来组织数据,也与其他硬盘空间具有一定的隔离性,保证代码只能够对 Cookie 中的数据进行处理。
Cookie 的数据从哪里来,用户登录网页,发出请求,网站服务器就可以拿到用户的账号信息,首先会判断账户是否存在,其次校验账号密码的正确性,校验成功后,就可以反馈一些用户私有的信息,例如,头像,昵称,详细信息,历史记录等,账户信息的背后是一个键值对,账户信息为键,其他与用户相关的信息为值。这个键值对就是 session(会话) ,服务器管理着很多的 session。
初次登录服务器会根据账号创建一个新的会话,键值对,账户信息为 key , 然后服务器会将账户信息返回客户端, 客户端将数据存储在 Cookie 中存储, 从此以后,用户通过该域名(例如百度)发送的请求,都会附带账户信息,服务器这边就可以直接拿到用户账号信息自动进行校验,就免去了每次都需要用户登录验证的过程。通过 session(会话),就可以拿到用户的个人信息。
请求正文(body)是 http协议 使用POST 方法传递数据的位置,如果是采用 GET 方法传递数据只需要使用 URL 中的 queryString(查询字符串) 传递即可,所以请求正文(body)是可选的,不一定有哦。
注意: 请求头(header) 和 请求正文(body) 之间有空行进行分割, 这里的空行相当于 header 的结束标志。
1. 优先定论:GET 和 POST 没有本质上的区别
2. 细节上的区别
3. GET 请求一般是幂等的,POST 请求一般是不幂等的(不是强制要求,幂不幂等只是建议)
幂等:
每次相同的输入,得到的输出结果是确定的,什么意思呢?GET 请求数据,服务器给予响应,如果我每次都向服务器请求相同的数据,这个时候,站在整个网络传输的角度来说无疑是带来无效的开销,所以,GET 发出的请求,然后服务器给予的响应是可以缓存的,例如:我在搜索框输入小狗,然后服务器将小狗的信息返回给浏览器,浏览器在将结果展示在页面上,此时小狗的信息被浏览器缓存,下次我再继续在搜索框中搜索小狗,这个时候结果是直接从缓存中拿出来展示在页面上,就避免了网络传输的开销,那如果我就是想要他每次都从服务器去拿最新的数据,怎么办呢,要么是重新进入网页,要么键盘 Ctrll + F5 浏览器强制刷新——直接从服务器获取数据。
不幂等:
每次相同的输入,得到的输入结果是不确定的,每次都直接从服务器获取数据。
拓展知识:
GET 方法传递的数据长度没有上限,POST 方法传递数据也没有上限。
HTTP 官方标准文档,明文规定,不限制 URL 的长度,GET 方法通过 URL 中的queryString 传递数据。当然这是建立在遵守HTTP 协议的标准的情况下。
如果使用 GET 传输大量的数据,势必会导致 URL 很长,整个地址栏就非常的不雅观。
如果使用 POST 传输数据,用户是不能在界面上看不到body 中的数据,也就是说不管body 中传输了什么数据,对用户的影响是微乎其微的。
HTTP 协议的响应首行包含了三个部分:
1. 版本号:HTTP/1.1, HTTP/2, HTTP/3.0 …… 描述了协议的版本
2. 状态码:(这个一般人是不认识的)表示请求页面的结果,是成功了,还是失败了,还是其他的一些原因……
3. 状态码的描述:一段英文单词,简单的向用户描述了状态。
200 OK
浏览器获取到了想要的信息
302 Move temporarily (临时重定向)
重定向就描述了接下来要跳转到哪里,常常用在登录界面,用于实现登录成功后自动跳转到主页
响应报文的 header 部分会包含一个 Location 字段,表示要跳转到那个页面
理解重定向:
相当于手机号通信中的 “呼叫转移” 功能,比如我换号,但是来不及提醒之前我通讯录的好友,为了避免,好友给我打电话联系不上,于是我就去营业厅办理了一个 呼叫转移,当我的好友给我旧号码打电话的时候,就自动的转接到我新的手机号上。
301 Moved Permanently (永久重定向)
当浏览器收到这种响应后,后续的请求都会自动的改成新的跳转地址,比较极端,301 也是通过 Location 字段来表示要重定向到的新地址。
404 not Found
客户端向服务器发出请求后, 服务器无法解析请求, 或者没有收到请求(URL 输入错误), 大部分情况是想要访问的资源不存在。
403 forbidden
表示想要要访问的资源存在,但是没有权限访问,这是一个悲伤的故事~~
405 Method Not Allowed
前面我们已经学习了 HTTP 中所支持的方法, 有 GET, POST, PUT, DELETE 等.
假设客户端使用 GET 方法URL中的 queryString 传输数据,服务器如果只能解析 POST 方法的HTTP 数据报,从 正文(body)中拿数据,这就有点张冠李戴的感觉了,服务器不支持解析该数据报,所以就会响应一个 405 的状态码,这种情况在现实情况下是很少见的,一般前后端都已经约定好的通信方式,还会经历一系列的调试,测试,但是如果是我们自己写网站呢,一个不小心就可能会写出这样的代码。
原因:例如尝试使用 GET 来访问人家的服务器,但是可能人家只支持 POST ,于是返回 405
500 internal server Error
服务器自己出现了问题,意味着出现了 bug, 导致服务器异常崩溃,无法继续正确运行,同样是现实少见,自己写代码,还是很容易看到的。
504 Gateway Timeout
当服务器负载比较大的时候,服务器处理单条请求的时候消耗的时间就会很长,就可能导致出现超时的情况。简单的理解就是多个客户端同时访问服务器,造成服务器处理不过来过载的情况(并发执行,多线程相关的知识),同样是现实少见,如果在购物狂欢节,或者春节抢票的时候,这些时候服务器并发处理请求就容易过载,服务器繁忙。
状态码小结
也是键值对结构,每个键值对占一行,每个键和值之间使用: 空格来分割,响应头中的键值对个数,也是不确定的,不同的键值对表示不同的含义。可以参考请求的响应头部分。
空行表示分割 header 和 正文(body)
响应正文是服务器返回给客户端具体的数据,正文的具体格式取决于 Content-Type 字段
1. text/html :表示返回一个网页文本,浏览器可以解析为网页
2. text/css :描述了前端的 样式文件
3. application/javascript :描述了网页的动作~ 前端三件套
4. application/ json :
JSON格式指的是JavaScript对象表示法(JavaScript Object Notation),是一种轻量级的数据格式,用于数据交换,是一种键值对的结构。不是有一句话是 前端 + json + 后端 = 全栈嘛,足以可见 json 格式的数据中在实际开发中的地位~
基本JSON格式结构如下:
- {
- "key": "value",
- "key2": "value2",
- "key3": {
- "key4": "value4",
- "key5": "value5"
- }
- }
其中可以包含对象、数组、字符串、数字、布尔等数据类型。示例:
- {
- "name": "John",
- "age": 30,
- "city": "New York",
- "pets": [
- {
- "name": "Fluffy",
- "species": "cat"
- },
- {
- "name": "Fido",
- "species": "dog"
- }
- ],
- "isMarried": true
- }
好了,到这里,网络编程中的 “【HTTP 协议】掌握 Web 的核心技术” 博主已经分享完了,希望对大家有所帮助,如有不妥之处欢迎批评指正。
下期预告:详解 Cookie 与 session
感谢每一个观看本篇文章的朋友,更多精彩敬请期待:保护小周ღ *★,°*:.☆( ̄▽ ̄)/$:*.°★*
遇见你,所有的星星都落在我的头上……
原文链接:https://blog.csdn.net/weixin_67603503/article/details/130809579
作者:听说你很拽
链接:http://www.qianduanheidong.com/blog/article/528910/8907c0885cbaa2f693c1/
来源:前端黑洞网
任何形式的转载都请注明出处,如有侵权 一经发现 必将追究其法律责任
昵称:
评论内容:(最多支持255个字符)
---无人问津也好,技不如人也罢,你都要试着安静下来,去做自己该做的事,而不是让内心的烦躁、焦虑,坏掉你本来就不多的热情和定力
Copyright © 2018-2021 前端黑洞网 All Rights Reserved 版权所有,并保留所有权利。 京ICP备18063182号-3
投诉与举报,广告合作请联系vgs_info@163.com或QQ3083709327
免责声明:网站文章均由用户上传,仅供读者学习交流使用,禁止用做商业用途。若文章涉及色情,反动,侵权等违法信息,请向我们举报,一经核实我们会立即删除!