HTTP详解
参考文档:https://blog.csdn.net/zhoujian_tank/article/details/105679076
0、从输入URL到页面加载发生了什么
URL解析--->DNS解析---->TCP连接--->发送HTTP请求----->服务器处理请求并返回HTTP报文---->浏览器解析渲染页面---->连接结束
URL解析
地址解析
首先判断你输入的是一个合法的 URL 还是一个待搜索的关键词,并且根据你输入的内容进行自动完成、字符编码等操作。
检查缓存
DNS解析:每次可能返回的IP地址不一致,为什么
DNS解析的过程就是寻找哪台机器上有你需要资源的过程。当你在浏览器中输入一个地址时,例如www.baidu.com,其实不是百度网站真正意义上的地址。互联网上每一台计算机的唯一标识是它的IP地址,但是IP地址并不方便记忆。用户更喜欢用方便记忆的网址去寻找互联网上的其它计算机,也就是上面提到的百度的网址。所以互联网设计者需要在用户的方便性与可用性方面做一个权衡,这个权衡就是一个网址到IP地址的转换,这个过程就是DNS解析。它实际上充当了一个翻译的角色,实现了网址到IP地址的转换。
DNS解析是一个递归查询的过程。
DNS缓存
DNS存在着多级缓存,从离浏览器的距离排序的话,有以下几种: 浏览器缓存
,系统缓存
,路由器缓存
,IPS服务器缓存
,根域名服务器缓存
,顶级域名服务器缓存
,主域名服务器缓存
。
- 在你的chrome浏览器中输入:chrome://dns/,你可以看到chrome浏览器的DNS缓存。
- 在操作系统DNS缓存中搜索
- 系统缓存主要存在/etc/hosts(Linux系统)中:
- 向本地配置的首选DNS服务器发起域名解析请求
DNS负载均衡
不知道大家有没有思考过一个问题: DNS返回的IP地址是否每次都一样?如果每次都一样是否说明你请求的资源都位于同一台机器上面,那么这台机器需要多高的性能和储存才能满足亿万请求呢?其实真实的互联网世界背后存在成千上百台服务器,大型的网站甚至更多。但是在用户的眼中,它需要的只是处理他的请求,哪台机器处理请求并不重要。DNS可以返回一个合适的机器的IP给用户,例如可以根据每台机器的负载量,该机器离用户地理位置的距离等等,这种过程就是DNS负载均衡,又叫做DNS重定向。
TCP连接
数据包封装:每层协议都将在上层协议数据的基础上加上自己的头部信息(链路层还会加上尾部信息),以为实现该层功能提供必要的信息。
应用层:发送 HTTP 请求
应用层一般是我们编写的应用程序,其决定了向用户提供的应用服务。应用层可以通过系统调用与传输层进行通信。
处于应用层的协议非常多,比如:FTP(File Transfer Protocol,文件传输协议)、DNS(Domain Name System,域名系统)和我们本章讨论的HTTP(HyperText Transfer Protocol,超文本传输协议)等。
在前面的步骤我们已经得到服务器的 IP 地址,浏览器会开始构造一个 HTTP 报文,其中包括:
请求报头(Request Header):请求方法、目标地址、遵循的协议等等
请求主体(其他参数)
其中需要注意的点:
传输层:TCP 传输报文(三次握手、HTTPS连接)
传输层通过系统调用向应用层提供处于网络连接中的两台计算机之间的数据传输功能。
在传输层有两个性质不同的协议:TCP(Transmission Control Protocol,传输控制协议)和UDP(User Data Protocol,用户数据报协议)。
传输层会发起一条到达服务器的TCP 连接,为了方便传输,会对数据进行分割(以报文段为单位),并标记编号,方便服务器接受时能够准确地还原报文信息。
在建立连接前,会先进行 TCP 三次握手。
TCP协议三次握手的描述如下:
第一次握手:客户端发送带有SYN标志的连接请求报文段,然后进入SYN_SEND状态,等待服务端的确认。
第二次握手:服务端接收到客户端的SYN报文段后,需要发送ACK信息对这个SYN报文段进行确认。同时,还要发送自己的SYN请求信息。服务端会将上述的信息放到一个报文段(SYN+ACK报文段)中,一并发送给客户端,此时服务端将会进入SYN_RECV状态。
第三次握手:客户端接收到服务端的SYN+ACK报文段后,会想服务端发送ACK确认报文段,这个报文段发送完毕后,客户端和服务端都进入ESTABLISHED状态,完成TCP三次握手。
当三次握手完成后,TCP协议会为连接双方维持连接状态。为了保证数据传输成功,接收端在接收到数据包后必须发送ACK报文作为确认。如果在指定的时间内(这个时间称为重新发送超时时间),发送端没有接收到接收端的ACK报文,那么就会重发超时的数据。
网络层:IP协议查询Mac地址(ARP)
网络层用来处理在网络上流动的数据包,数据包是网络传输的最小数据单位。该层规定了通过怎样的路径(传输路线)到达对方计算机,并把数据包传输给对方。
将数据段打包,并加入源及目标的IP地址,并且负责寻找传输路线。
判断目标地址是否与当前地址处于同一网络中,是的话直接根据 Mac 地址发送,否则使用路由表查找下一跳地址,以及使用 ARP 协议查询它的 Mac 地址。
链路层
链路层用来处理连接网络的硬件部分,包括控制操作系统、硬件设备驱动、NIC(Network Interface Card,网络适配器)以及光纤等物理可见部分。硬件上的范畴均在链路层的作用范围之内。
服务器处理请求
HTTPD
最常见的HTTPD 有 Linux 上常用的 Apache 和 Nginx
,以及 Windows 上的 IIS。
它会监听得到的请求,然后开启一个子进程去处理这个请求。
处理请求
接受 TCP 报文后,会对连接进行处理,对HTTP协议进行解析(请求方法、域名、路径等),并且进行一些验证:
- 验证是否配置虚拟主机
- 验证虚拟主机是否接受此方法
- 验证该用户可以使用该方法(根据 IP 地址、身份信息等)
重定向
假如服务器配置了 HTTP 重定向,就会返回一个 301永久重定向响应,浏览器就会根据响应,重新发送 HTTP 请求(重新执行上面的过程)。
浏览器接受响应
浏览器接收到来自服务器的响应资源后,会对资源进行分析。
首先查看 Response header,根据不同状态码做不同的事(比如上面提到的重定向)。
如果响应资源进行了压缩(比如 gzip),还需要进行解压。
然后,对响应资源做缓存。
渲染页面
1、与HTTP协议密切的协议:IP、TCP、DNS
这其实就是:一次网页访问过程中使用到哪些协议
- DNS:它提供域名到IP地址之间的解析
- TCP:传输控制协议,确保数据的安全到达
- IP:IP间的通信依赖MAC地址,在网络上,通信的双方在一个局域网的情况很少,通常是经过多台计算机和网络设备中转才能连接到对方。而在进行中转时,会利用下一站中转设备的MAC地址来搜索下一个中转目标。这时会采用ARP协议,ARP是一种用以解析地址的协议,根据通信放的IP地址就可以反查出对应的MAC地址。
2、HTTP报文基础
请求报文
:请求报文由请求方法
,请求URI
,协议版本
,可选的请求首部字段
和内容实体
构成。响应报文
:响应报文由协议版本
,状态码(表示请求成功或失败的数字代码)
,用以解释状态码的原因短语
,可选的响应首部字段
以及实体主体
特点:
1、简单快速:客户向服务器请求服务时,只需传送请求方法和路径。由于HTTP协议简单,使得HTTP服务器的程序规模小,因而通信速度很快。
2、灵活:HTTP允许传输任意类型的数据对象。正在传输的类型由Content-Type加以标记。
3、HTTP 0.9和1.0使用非持续连接:限制每次连接只处理一个请求,服务器处理完客户的请求,并收到客户的应答后,即断开连接。HTTP 1.1使用持续连接:不必为每个web对象创建一个新的连接,一个连接可以传送多个对象,采用这种方式可以节省传输时间。
4、无状态:HTTP协议是无状态协议。无状态是指协议对于事务处理没有记忆能力。缺少状态意味着如果后续处理需要前面的信息,则它必须重传,这样可能导致每次连接传送的数据量增大。另一方面,在服务器不需要先前信息时它的应答就较快。
5、支持B/S及C/S模式。
2.1、HTTP请求方法
区分:
幂等
HTTP1.1中新添加的方法
方法 | 说明 | 协议版本 | 幂等 |
---|---|---|---|
GET |
请求访问已被URI 识别的资源,当前网络请求中,绝大部分使用的是GET方法 |
||
POST |
传输实体的主体,主要用来传输数据 | ||
put |
传输文件,由于自身不带验证机制,任何人都可以上传文件 | ||
HEAD |
HEAD方法和GET方法一样,只是不返回报文主体部分 | ||
DELETE |
删除文件,与put 功能相反,并且同样不带验证机制 |
||
OPTIONS |
用来查询针对请求URI指定资源的支持方法,会返回GET,POST,HEAD |
||
Trace |
追踪路径,服务器会将通信路径返回给客户端 |
高并发性下幂等解决方案?
GET与POST的区别?
GET单纯获取应用状态,POST会改变应用状态
1、有无副作用、是否幂等
get
读取一个资源,比如Get到一个html文件。反复读取不应该对访问的数据有副作用。
比如:Get以下,用户就下单了,返回订单已受理·,这是不可取的,没有副作用被称为是
幂等的,因为
GET是读取,就可以对GET
请求的数据做缓存。这个缓存可以做到浏览器本身上(彻底避免浏览器发请求),也可以做到代理上,或者做到Server
端
post
在页面标签会定义一个表单,点击其中的
subnit元素会发出
POST请求让服务器做一件事。这件事往往是有
副作用的,不幂等的`;不幂等也就意味着不能随意多次执行,因此也就不能缓存。
当然,服务器的开发者完全可以把GET
实现为有副作用;把POST
实现为没有副作用;只不过这和浏览器的预期不符合;反过来,把没有副作用的请求用post实现,浏览器该弹框还是会弹框,对用户体验好处改善。
2、携带数据的格式
当浏览器发出一个get
请求时,就意味着要么是用户自己再浏览器的地址栏输入
,要不就是点击html里a标签的href的,浏览器直接发出的GET只能由一个url触发,所以没办法,GET上要在url之外带一些参数就只能依靠
url`上附带quertString。但是Http协议本身并没有这个限制
浏览器的POST请求都来自表单提交
。每次提交,表单的数据被浏览器用编码到HTTP请求的body
里。浏览器在POST
一个表单时,url上也可以带参数
,只要里的url带querystring就行。只不过表单里面的那些用等标签经过用户操作产生的数据都在会在body里。
如果是Ajax或者其他HTTP Client发出去的POST请求,其body格式就非常自由了,常用的有json,xml,文本,csv……甚至是你自己发明的格式。只要前后端能约定好即可。
因此我们一般会泛泛的说“GET请求没有body,只有url,请求数据放在url的querystring中
;POST请求的数据在body中“。但这种情况仅限于浏览器发请求的场景。
3、关于安全性:使用HttpS
我们常听到GET不如POST安全,因为POST用body传输数据,而GET用url传输,更加容易看到。但是从攻击的角度,无论是GET还是POST都不够安全,因为HTTP本身是明文协议。每个HTTP请求和返回的每个byte都会在网络上明文传播,不管是url,header还是body。这完全不是一个“是否容易在浏览器地址栏上看到“的问题。为了避免传输中数据被窃取,必须做从客户端到服务器的端端加密。业界的通行做法就是https---——即用SSL协议协商出的密钥加密明文的http数据。
回到HTTP本身,的确GET请求的参数更倾向于放在url上,因此有更多机会被泄漏。比如携带私密信息的url会展示在地址栏上,还可以分享给第三方,就非常不安全了。此外,从客户端到服务器端,有大量的中间节点,包括网关,代理等。他们的access log通常会输出完整的url,比如nginx的默认access log就是如此。如果url上携带敏感数据,就会被记录下来。但请注意,就算私密数据在body里,也是可以被记录下来的,因此如果请求要经过不信任的公网,避免泄密的唯一手段就是https。
4、关于编码
常见的说法有,比如GET的参数只能支持ASCII,而POST能支持任意binary,包括中文。但其实从上面可以看到,GET和POST实际上都能用url和body。因此所谓编码确切地说应该是http中url用什么编码,body用什么编码。
顺便说一句,尽管在浏览器地址栏可以看到中文。但这种url在发送请求过程中,浏览器会把中文用字符编码+Percent Encode翻译为真正的url,再发给服务器。浏览器地址栏里的中文只是想让用户体验好些而已。
5、携带数据的大小—关于URL的长度
因为上面提到了不论是GET和POST都可以使用URL传递数据,所以我们常说的“GET数据有长度限制“其实是指”URL的长度限制“。HTTP协议本身对URL长度并没有做任何规定。实际的限制是由客户端/浏览器以及服务器端决定的。
为啥要限制呢?如果写过解析一段字符串的代码就能明白,解析的时候要分配内存。对于一个字节流的解析,必须分配buffer来保存所有要存储的数据。而URL这种东西必须当作一个整体看待,无法一块一块处理,于是就处理一个请求时必须分配一整块足够大的内存。如果URL太长,而并发又很高,就容易挤爆服务器的内存;同时,超长URL的好处并不多,我也只有处理老系统的URL时因为不敢碰原来的逻辑,又得追加更多数据,才会使用超长URL。对于开发者来说,使用超长的URL完全是给自己埋坑,需要同时要考虑前后端,以及中间代理每一个环节的配置。此外,超长URL会影响搜索引擎的爬虫,有些爬虫甚至无法处理超过2000个字节的URL。这也就意味着这些URL无法被搜到,坑爹啊。
最后,协议都是人定的。只要客户端和服务器能彼此认同,就能工作。在常规的情况下,用符合规范的方式去实现系统可以减少很多工作量——大家都约定好了,就不要折腾了。但是,总会有一些情况用常规规范不合适,不满足需求。这时思路也不能被规范限制死,更不要死抠RFC。这些规范也许不能处理你遇到的特殊问题。比如:Elastic Search的_search接口使用GET,却用body来表达查询,因为查询很复杂,用querystring很麻烦,必须用json格式才舒服,在请求体用json编码更加容易,不用折腾percent encoding。用POST写一个接口下单时可能也要考虑幂等,因为前端可能实现“下单按键”有bug,造成用户一次点击发出N个请求。你不能说因为POST by design应该是不幂等就不管了。协议是死的,人是活的。遇到实际的问题时灵活的运用
2.2、HTTP报文详解
用于HTTP协议交互的信息被称为HTTP报文;
- 请求段的HTTP报文叫作请求报文
- 响应端的叫作响应报文
2.2.1、请求报文
客户端发送一个HTTP请求报文到服务器的请求消息包括以下格式:
请求行、请求头部、空行和请求数据四个部分组成。
(1)Get请求例子
第一部分:请求行,用来说明请求类型,要访问的资源以及所使用的HTTP版本.
GET说明请求类型为GET,[/562f25980001b1b106000338.jpg]为要访问的资源,该行的最后一部分说明使用的是HTTP1.1版本。
第二部分:请求头部,紧接着请求行(即第一行)之后的部分,用来说明服务器要使用的附加信息
从第二行起为请求头部,HOST将指出请求的目的地.User-Agent,服务器端和客户端脚本都能访问它,它是浏览器类型检测逻辑的重要基础.该信息由你的浏览器来定义,并且在每个请求中自动发送等等
第三部分:空行,请求头部后面的空行是必须的
即使第四部分的请求数据为空,也必须有空行。
第四部分:请求数据也叫主体,可以添加任意的其他数据。
这个例子的请求数据为空。
POST请求例子
第一部分:请求行,第一行明了是post请求,以及http1.1版本。
第二部分:请求头部,第二行至第六行。
第三部分:空行,第七行的空行。
第四部分:请求数据,第八行。
2.2.2、响应报文
一般情况下,服务器接收并处理客户端发过来的请求后会返回一个HTTP的响应消息。
HTTP响应也由四个部分组成,分别是:状态行、消息报头、空行和响应正文。
第一部分:状态行,由HTTP协议版本号, 状态码, 状态消息 三部分组成。
第一行为状态行,(HTTP/1.1)表明HTTP版本为1.1版本,状态码为200,状态消息为(ok)
第二部分:消息报头,用来说明客户端要使用的一些附加信息
第二行和第三行和第四行为消息报头,
Date:生成响应的日期和时间;Content-Type:指定了MIME类型的HTML(text/html),编码类型是ISO-8859-1
第三部分:空行,消息报头后面的空行是必须的
第四部分:响应正文,服务器返回给客户端的文本信息。
空行后面的html部分为响应正文。
2.2.3、报文的首部内容
请求报文和响应报文的首部内容由以下数据组成
请求行
:包含用于请求的方法,请求uri
和HTTP
版本
状态行
:包含表明响应结果的状态码,原因短语和HTTP
版本
首部字段
:包含表示请求和响应的各种条件和属性的各类首部
2.3、状态码
HTTP状态码负责表示客户端HTTP请求的返回结果,标记服务器端的处理是否正常,通知出现的错误等工作
状态码 | 类别 | 原因短语 |
---|---|---|
1xxx | 信息性状态码 | 接受的请求正在处理 |
2xxx | 成功状态码 | 请求正常处理完毕 |
3xxx | 成定向状态码 | 需要附加操作以完成请求 |
4xxx | 客户端错误状态码 | 服务器无法处理请求 |
5xxx | 服务器端错误状态码 | 服务器处理请求出错 |
状态码 | 状态码英文名称 | 中文描述 |
---|---|---|
100 | Continue | 继续。客户端应继续其请求 |
101 | Switching Protocols | 切换协议。服务器根据客户端的请求切换协议。只能切换到更高级的协议,例如,切换到HTTP的新版本协议 |
200 | OK | 请求成功。一般用于GET与POST请求 |
201 | Created | 已创建。成功请求并创建了新的资源 |
202 | Accepted | 已接受。已经接受请求,但未处理完成 |
203 | Non-Authoritative Information | 非授权信息。请求成功。但返回的meta信息不在原始的服务器,而是一个副本 |
204 | No Content | 无内容。服务器成功处理,但未返回内容。在未更新网页的情况下,可确保浏览器继续显示当前文档 |
205 | Reset Content | 重置内容。服务器处理成功,用户终端(例如:浏览器)应重置文档视图。可通过此返回码清除浏览器的表单域 |
206 | Partial Content | 部分内容。服务器成功处理了部分GET请求 |
300 | Multiple Choices | 多种选择。请求的资源可包括多个位置,相应可返回一个资源特征与地址的列表用于用户终端(例如:浏览器)选择 |
301 | Moved Permanently | 永久移动。请求的资源已被永久的移动到新URI,返回信息会包括新的URI,浏览器会自动定向到新URI。今后任何新的请求都应使用新的URI代替 |
302 | Found | 临时移动。与301类似。但资源只是临时被移动。客户端应继续使用原有URI |
303 | See Other | 查看其它地址。与301类似。使用GET和POST请求查看 |
304 | Not Modified | 未修改。所请求的资源未修改,服务器返回此状态码时,不会返回任何资源。客户端通常会缓存访问过的资源,通过提供一个头信息指出客户端希望只返回在指定日期之后修改的资源 |
305 | Use Proxy | 使用代理。所请求的资源必须通过代理访问 |
306 | Unused | 已经被废弃的HTTP状态码 |
307 | Temporary Redirect | 临时重定向。与302类似。使用GET请求重定向 |
400 | Bad Request | 客户端请求的语法错误,服务器无法理解 |
401 | Unauthorized | 请求要求用户的身份认证 |
402 | Payment Required | 保留,将来使用 |
403 | Forbidden | 服务器理解请求客户端的请求,但是拒绝执行此请求 |
404 | Not Found | 服务器无法根据客户端的请求找到资源(网页)。通过此代码,网站设计人员可设置"您所请求的资源无法找到"的个性页面 |
405 | Method Not Allowed | 客户端请求中的方法被禁止 |
406 | Not Acceptable | 服务器无法根据客户端请求的内容特性完成请求 |
407 | Proxy Authentication Required | 请求要求代理的身份认证,与401类似,但请求者应当使用代理进行授权 |
408 | Request Time-out | 服务器等待客户端发送的请求时间过长,超时 |
409 | Conflict | 服务器完成客户端的 PUT 请求时可能返回此代码,服务器处理请求时发生了冲突 |
410 | Gone | 客户端请求的资源已经不存在。410不同于404,如果资源以前有现在被永久删除了可使用410代码,网站设计人员可通过301代码指定资源的新位置 |
411 | Length Required | 服务器无法处理客户端发送的不带Content-Length的请求信息 |
412 | Precondition Failed | 客户端请求信息的先决条件错误 |
413 | Request Entity Too Large | 由于请求的实体过大,服务器无法处理,因此拒绝请求。为防止客户端的连续请求,服务器可能会关闭连接。如果只是服务器暂时无法处理,则会包含一个Retry-After的响应信息 |
414 | Request-URI Too Large | 请求的URI过长(URI通常为网址),服务器无法处理 |
415 | Unsupported Media Type | 服务器无法处理请求附带的媒体格式 |
416 | Requested range not satisfiable | 客户端请求的范围无效 |
417 | Expectation Failed | 服务器无法满足Expect的请求头信息 |
500 | Internal Server Error | 服务器内部错误,无法完成请求 |
501 | Not Implemented | 服务器不支持请求的功能,无法完成请求 |
502 | Bad Gateway | 作为网关或者代理工作的服务器尝试执行请求时,从远程服务器接收到了一个无效的响应 |
503 | Service Unavailable | 由于超载或系统维护,服务器暂时的无法处理客户端的请求。延时的长度可包含在服务器的Retry-After头信息中 |
504 | Gateway Time-out | 充当网关或代理的服务器,未及时从远端服务器获取请求 |
505 | HTTP Version not supported | 服务器不支持请求的HTTP协议的版本,无法完成处理 |
2.4、HTTP首部
HTTP首部字段是构成HTTP报文的要素之一。在客户端与服务器之间以HTTP协议进行通信的过程中,无论是请求还是响应都会使用首部字段,它能起到额外传递重要信息的作用
2.4.1、HTTP1.1 通用首部字段
1、Cache-Control
通过指定首部字段Cache-control
的指令,就能操作缓存的工作原理
指令的参数是可选的,多个指令之间通过,
分隔,首部字段Cache-Control
的指令可用于请求和响应
Cache-control:private,public
:当指定使用public
指令时,则明确表明其他用户也可以利用缓存Cache-control:no-cache
:使用的目的是为了防止从缓存中返回过期的资源Cache:max-age
:当客户端发送的请求中包含max-age
指令时,如果判定缓存资源的缓存时间比指定时间的数值更小,那么客户端就接收缓存的资源。应用HTTP/1.1
版本的缓存服务器遇到同时存在Expires
首部字段的情况时,会优先处理max-age
指令,而忽略掉Expires
首部字段。
2、Connection(管理连接)
Connection首部字段具备如下两个作用:
- 控制不再转发给代理的首部字段
- 管理持久连接
管理持久连接
HTTP/1.1
版本的默认连接都是持久连接,为此,客户端会在持久连接上连续发送请求。当服务器端想明确断开连接时,则指定Connection
首部字段的值为close
HTTP/1.1
之前的HTTP版本的默认连接都是非持久连接。为此,如果想在旧版本的HTTP
协议上维持持续连接,则需要指定Connection
首部字段的值为Keep Alive
3、Date:表明创建HTTP报文的日期和时间
2.4.2、请求首部字段
请求首部字段是从客户端往服务器端发送请求报文中所使用的字段,用于补充请求的附加信息,客户端信息等
字段 | 含义 |
---|---|
ACCEPT | 通知服务器,用户代理能够处理的媒体类型及媒体类型的相对优先级 |
ACCEPT-Chrset | 通知服务器用户代理支持的字符集 |
ACCEPT-Encoding | 通知服务器用户代理支持的内容编码以及内容编码的优先级顺序由 |
ACCEPT-Language | 通知服务器用户代理能够处理的自然语言集 |
Authorized | 用来告知服务器,用户代理的认证 |
IF-Match: | 服务器会比对IF-MATCH 的字段和资源的ETag 值,仅当二者一致时,才会执行请求 |
IF-MODIFIED-Since | 如果在If-Modified-Since字段指定的日期时间后,资源发生了更新,服务器会接受请求 |
Max-Forwards | 服务器在往下一个服务器转发请求前,会将Max-Forwards的值-1后重新赋值,当服务器接受到Max-Forwards 值为0的请求时,则不再进行转发,而是直接返回响应(解决死循环 ) |
Range | 对于只需要获取部分资源的范围请求,包含首部字段Range 即可告知服务器资源的指定范围 |
User-Agent | User-Agent 会创建请求的浏览器和用户代理名称等信息传达给服务器,可以进行反爬虫 |
2.4.3、响应首部字段
字段 | 含义 |
---|---|
Accept-Ranges | 告知服务器是否能处理范围请求,以指定获取服务器端某个部分的资源 |
ETag | 能告知客户端实体标记,它是一种可将资源以字符串形式做唯一标志的方式,服务器会为每份资源分配对应的ETag 值 |
Location | 可以将响应接收方引导只某个与请求URI位置不同的资源 |
SERVER | 告知服务器daunt当前服务器上安装的HTTP服务器应用程序的信息 |
2.4.4、实体首部字段
字段 | 含义 |
---|---|
Allow | 用于通知客户端能够支持Request-URI指定资源的所有HTTP方法 |
Content-Encoding | 告知客户端服务器对实体的主体部分选用的内容编码方式 |
Content-Language | 告知客户端,实体主体使用的自然语言 |
Content-Length | 告知客户端实体主体部分的大小 |
Contemt-Type | 说明实体主体内对象的媒体类型 |
Expire | 首部字段Expires会将资源失效的日期告知客户端.缓存服务器在收到含有首部字段Expires 的响应后,会以缓存来应答请求,在Expires 字段值指定的时间之前,响应的副本会一直被保存。当超过指定的时间后,响应的副本会一直保存。当超过指定的时间后,缓存服务器在请求发送过来时,会转向源服务器请求资源 |
2.4.5、为Cookie服务的首部字段
Cookie的工作机制是用户识别以及状态管理,Web网站为了管理用户的状态会通过Web浏览器,把一些数据临时写入用户的计算机内,接着当用户访问该Web
网站时,可通过通信方式取回之前存放的ookie
首部字段名称 | 说明 |
---|---|
set-Cookie |
开始状态管理所使用的Cookie 信息 |
Cookie |
服务器接收到Cookie信息 |
3、HTTP具体应用
3.1、连接管理
3.1.1、短连接与长连接
传统的HTTP通信的连接是短连接的、
为了解决TCP上述问题,HTTP1.1和一部分的HTTP1.0相处了持久连接的方法。持久连接的特点是:只要任意一端没有明确提出断开连接,则保持TCP连接状态
持久连接的好处在于减少了TCP连接的重复建立和断开锁造成的额外开销,减轻了服务daunt的负载。
长连接只需要建立一次TCP连接就能进行多次HTTP通信
- 从
HTTP/1.1
开始默认是长连接
的,如果要断开连接,需要客户端或者服务端提出断开连接,使用connection:close
- 在
HTTP/1.1
之前默认是短连接的,如果需要使用长连接,则使用Connection:keep-Alive
3.1.2、流水线
管线化使得不用等待响应亦可直接发送下一个请求
HTTP请求是按照顺序出发的,下一个请求只有在当前请求收到响应之后才会被发出。由于收到网络延迟和宽带限制,在下一个请求被发送到服务器之前,可能需要等待很长时间,流水线是在同一条连接上连续发出请求,而不是等待响应返回,这样可以减少延时
3.2、 HTTP如何保存状态–Session与Cookie
HTTP协议1.1虽然是无状态协议,但是实现了期望的保持状态功能
,于是引入了Cookie
技术,有了Cookie
再用HTTP
协议就可以管理状态了
Cookie
是服务器发送到用户浏览器并保存到本地的一小块数据,它会在浏览器之后向同一服务器再次发起请求时被携带上,用于告知服务端两个请求是否来自同一浏览器
。由于之后每次请求都会需要携带Cookie
数据,因此会带来额外的性能开销。
Cookie曾一度用于客户端数据的存储,因为当时并没有其他合适的存储办法而作为唯一的存储手段,但现在随着现代浏览器开始支持各种各样的存储方式,
Cookie渐渐被淘汰,新的浏览器
API已经开发者直接将数据存储到本地,如使用
Web storage API(本地存储和会话存储或者
IndexedDB)
3.2.1、Cookie用途
- 会话状态管理(如用户登录状态、购物车、游戏分数或其他需要记录的信息)
- 个性化设置(如用户自定义设置、主题等)
- 浏览器行为跟踪(如跟踪分析用户行为等)
3.2.2、创建过程
Cookie会根据从服务端发送的响应报文内的一个叫做set-Cookie
的首部字段信息,通过客户端保存Cookie
。当下次客户端再往该服务器发送请求时,客户端会自动在请求报文中加入Cookie
值后发送出去。
服务端发现客户端发送过来的Cookie
后,会去检查究竟从哪一个客户端发来的连接请求,然后对比服务器上的记录,最后得到之前的状态信息。
HTTP/1.0 200 OK
Content-type: text/html
Set-Cookie: yummy_cookie=choco
Set-Cookie: tasty_cookie=strawberry
12345
客户端之后对同一服务器发送请求时,会从浏览器中取出Cookie
信息并通过Cookie
请求首部字段发送给服务器
GET /sample_page.html HTTP/1.1
Host: www.example.org
Cookie: yummy_cookie=choco; tasty_cookie=strawberry
12345
3.2.3、分类
会话期 Cookie
:浏览器关闭之后它会被自动删除,也就是说它仅在会话期内有效持久Cookie
:指定过期时间(Expires)或有效期(max-age)之后就成为了持久性的 Cookie。
Set-Cookie: id=a3fWa; Expires=Wed, 21 Oct 2015 07:28:00 GMT;
12
3.2.4、Set-Cookie字段的属性
属性 | 说明 |
---|---|
NAME=VALUE | 赋予Cookie 的名称和其值 |
expires=DATE | Cookie 的有效期(若不明确指定则默认为浏览器关闭前为止) |
path=PATH | 将服务器上的文件目录作为Cookie 的适用对象(若不指定则默认为文档所在的文件目录) |
domain=域名 | 作为Cookie适用对象的域名 |
Secure | 标记为Secure的Cookie只能通过被HTTPS 协议加密过的请求发送给服务端,但即便设置了Secure 标记,敏感信息也不应该通过Cookie 传输,因为Cookie 有其固有的不安全性 |
3.2.5、Session的一些介绍
除了可以将用户信息通过Cookie
存储在用户浏览器中,也可以利用Session
存储在服务器端,存储在服务器端的信息更加安全。
Session可以存储在服务器上的文件
、数据库
或者内存
中。也可以将Session
存储在Redis
这种内存性数据库中,效率会更高。
使用Session
维护用户登录状态的过程如下:
- 用户进行登录时,用户提交包含用户名和密码的表单,放入
HTTP
请求报文中 - 服务器验证该用户名和密码,如果正确则把用户信息存储到
Redis
中,它在Redis
中的key
称为SessionId
- 服务器端返回的响应报文的
Set-Cookie
首部字段包含了这个Session ID
,客户端收到响应报文之后将该Cookie值存入浏览器中 - 客户端之后对同一服务器进行请求时会包含该
Cookie
值,服务器收到之后会提取出Session Id
,从Redis
中取出用户信息,继续之前的业务操作
应该注意Session Id的安全性问题,不能让他被恶意攻击者轻易获取,那么就不能产生一个容易被猜到的SessionID
值。此外,还需要经常重新生成SessionId
。在对安全性要求极高的场景下,例如转账等操作,除了使用Session
管理用户状态之外,还需要对用户进行重新验证,比如重新输入密码,或者使用短信验证码等方式
3.2.6、 Cookie与Session的区别
1 , session 在服务器端, cookie在客户端(浏览器)
2 , session默认被存在在服务器的一个文件里(不是内存)
3 , session 的运行依赖session id ,而session id是存在cookie中的,也就是说,如果浏览器禁用了cookie ,同时session也会失效(但是可以通过其它方式实现,比如在url中传递session_ id )
4 , session可以放在文件、数据库、或内存中都可以。
5 ,用户验证这种场合-般会用session
因此,维持一个会话的核心就是客户端的唯一 标识,即session id
本来session是一个抽象概念 ,开发者为了实现中断和继续等操作,将user agent和server之间一对一的交互,抽象为”会话”, 进而衍生出”会话状态”, 也就是session的概念。
而cookie是一个实际存在的东西 , http协议中定义在header中的字段。可以认为是session的-种后端无状态实现。
而我们今天常说的"session” , 为了绕开cookie的各种限制,通常借助cookie本身和后端存储实现的, -种更高级的会话状态实现。
所以cookie和session,你可以认为是同一层次的概念也可以认为是不同层次的概念。 具体到实现,session因为session id的存在,通常要借助cookie实现,但这并非必要,只能说是通用性较好的一种实现方案。
3.2.7、禁用Cookie咋处理session?
使用URL
重写技术,将Session ID
作为url的参数进行传递
我们应该思考session
是什么?
服务器生成的一个shit
值,甩给客户端,客户端拿着金牌当令箭,只要客户端有办法把这个金牌令箭告知服务器就好了。
在这样的情况下,你怎么传递这个金牌令箭,就看你个人喜好了。
在这样的认知下,怎样使用session技术就看你客户端和服务器沟通的方式,使用 get、post均可,还可以基于url
3.3、缓存
3.3.1、为什么要使用Http缓存?
- 减少了冗余的数据传输,节省了网费。
- 缓解了服务器的压力, 大大提高了网站的性能
- 加快了客户端加载网页的速度
缓存是指代理服务器和客户端本地磁盘内保存的资源副本。利用缓存可减少对资源服务器的访问,因此也就节省了通信流量和通信时间
缓存服务器的优势在于利用缓存可避免多次从源服务器转发资源。因此用户端可就近从缓存服务器上获取资源,而源服务器也不必多次处理相同的请求
可以认为浏览器存在一个缓存数据库,用于存储数据,需要请求服务器,服务器返回后,将数据存储至缓存数据库中。
常见的http缓存只能缓存get
请求响应的资源,对于其他类型的响应则无能为力。
HTTP缓存有很多种规则,根据需要重新向服务器发起请求来分类,将其分为两大类(强制缓存,对比缓存
)
3.3.2、缓存规则解析—强制缓存和对比缓存
已存在缓存数据时,仅基于强制缓存
,请求数据的流程如下:
已存在缓存数据,仅基于对比缓存
,请求数据的流程如下:
对比缓存,不管是否使用缓存,都需要向服务器发送请求
强制缓存如果生效,不需要在和服务器发生交互,而对比缓存不管是否生效,都需要与服务器端发生交互
==两类缓存规则可以同时存在,强制缓存
优先级高于对比缓存
,也就是说,当执行强制缓存
的规则时,如果缓存失效,直接使用缓存,不再执行对比缓存
规则。
3.3.3、强制缓存(使用字段Expires/Cache-Control)
强制缓存:在缓存数据未失效的情况下,可以直接使用缓存数据,那么浏览器如何判断缓存数据是否失效呢?
对于强制缓存来说,响应header中会有两个字段来标明失效规则:(Expires/Cache-Control
)
3.3.3.1、Expires
Expires的值为服务端返回的到期时间,即下一次请求时,请求时间小于服务端返回的到期时间,直接使用缓存数据。
不过Expires 是HTTP 1.0的东西,现在默认浏览器均默认使用HTTP 1.1,所以它的作用基本忽略。
另一个问题是,到期时间是由服务端生成的,但是客户端时间可能跟服务端时间有误差,这就会导致缓存命中的误差。
所以HTTP 1.1 的版本,使Cache-Control
替代。
3.3.3.2、Cache-Control
Cache-Control 是最重要的规则。常见的取值有private
、public
、no-cache
、max-age
,no-store
,默认为private
。
private
: 客户端可以缓存public
: 客户端和代理服务器都可缓存(前端的同学,可以认为public和private是一样的)max-age=xxx
: 缓存的内容将在 xxx 秒后失效no-cache:
需要使用对比缓存来验证缓存数据(后面介绍)no-store
:所有内容都不会缓存,强制缓存,对比缓存都不会触发
3.3.4、对比缓存(使用Last-Modified/If-Modified-Since;Etag/If-None-Match)
对比缓存:顾名思义,需要进行比较判断是否可以使用缓存,浏览器第一次请求数据时,服务器会将缓存标志与数据一起返回给客户端,客户端将二者备份至数据库中。再次请求数据时,客户端将备份的缓存标志发送给服务器,服务器根据缓存标志进行判断,判断成功后,返回304
状态码,通知客户端比较成功,可以使用缓存数据。
通过两图的对比,我们可以很清楚的发现,在对比缓存
失效时,状态码304
,并且报文大小和请求时间大大减少。原因是,服务端在进行标志比较后,只返回header部分,通过状态码通知客户端,不再需要将报文主体部分返回给客户端。
对于对比缓存
来说,它在请求header和响应header间进行传递
一共分为两种:
Last-Modified/If-Modified-Since
Etag/If-None-Match
3.3.4.1、Last-Modified/If-Modified-Since
- 服务器在响应请求时,告知浏览器资源的最后修改时间:
Last-Modified
- 客户端再次请求服务器时,通过
If-Modified-Since
发送给客户段,服务器收到请求后发现有头If-Modified-Since
则与被请求资源的最后修改时间对比;若资源的最后修改时间大于If-Modifid-Since
,则说明资源被改动过,则响应整片资源内容,返回状态码200
;若资源的最后修改时间小于或等于If-Modified-Since,则说明资源无更新,则响应HTTP304
,通知浏览器继续使用保存的cache
3.3.4.2、Etag/If-None-Match(优先级高于Last-Modified/If-Modifeid-Since)
Etage
:服务器响应请求时,告知浏览器当前资源在服务器的唯一标志(生成规则由服务器决定)If-None-Match
:再请求服务器时,通过此字段通知服务器客户端缓存数据的唯一标志,服务器收到请求后发现有头If-None-Match
则与被请求资源的唯一标志进行对比,不同,说明资源又被改动了,则响应整片资源内容,返回状态码200
’;相同,说明资源无新修改,则响应HTTP 304
,告知浏览器继续使用所保存的cache
3.3.5、总结
- 对于强制缓存,服务器通知浏览器一个缓存时间,在缓存时间内,下次请求,直接用缓存,不在时间内,执行比较缓存策略。
- 对于比较缓存,将缓存信息中的Etag和Last-Modified通过请求发送给服务器,由服务器校验,返回304状态码时,浏览器直接使用缓存。
浏览器第一次请求
浏览器再次请求时:
3.4、通信数据转发
3.4.1、代理
代理服务器接收客户端的请求,并转发给其他客户端 。
使用代理的目的:
- 缓存
- 负载均衡
- 网络访问控制
- 访问日志记录
3.4.2、网关
与代理服务器不同的是,网关服务器会将HTTP转化为其他协议进行通信,从而请求其他非HTTP
服务器的服务
3.4.3、隧道
使用SSL等加密手段,在客户端和服务器之间建立一条安全的通信线路
4、HTTP安全
4.1、HTTP的缺点
HTTP主要有这些不足:
通信使用明文(不加密),内容可能会被窃听
不验证通信双方的身份,因此有可能会遭遇伪装
无法证明报文的完整性,所以有可能已被篡改
4.2、如何加密
解决如下问题:通信使用明文(不加密),内容可能会被窃听
4.2.1、通信的加密
HTTP协议中没有加密机制,但可以通过和SSL(Secure Socket Layer,安全套解层)或TLS(Transport Layer Security,安全传输层协议)的组合使用
,加密HTTP的通信内容。
用SSL建立安全通信线路之后,就可以在这条线路上进行HTTP
通信了。与SSL组合使用的HTTP被称为HTTPS
4.2.2、内容的加密
还有一种将参与通信的内容本身加密的方式。由于HTTP
协议中没有加密机制,那么就对HTTP
协议传输的内容本身加密,即把HTTP报文里所含的内容进行加密处理
解决如下问题:不验证通信双方的身份,因此有可能会遭遇伪装
4.3、验证通信双方的身份
4.3.1、查明对手的证书
虽然使用HTTP协议无法确定通信双方,但如果使用SSL
则可以,SSL
不仅提供加密处理,而且还使用了一种被称为证书的手段,可用于确定通信方。
通过使用证书,以证明通信双方就是意料中的服务器。这对使用者个人来讲,也减少了个人信息泄露的危险性。
4.4、验证报文的完整性
所谓完整性是指信息的准确度。若无法证明其完整性,通常也就意味着无法判断信息是否准确
请求或响应在传输途中,遭攻击者拦截并篡改内容的攻击传给中间人攻击
为了有效防止这些弊端,有必要使用HTTPS
。SSL
提供认证和加密处理及摘要功能。仅靠HTTP确保完整性是非常困难的,因此通过和其他协议组合使用来实现这个目标
5、HTTPS = HTTP+加密+认证+完整性保护
HTTP加上加密处理和认证以完整性保护即是HTTPS
把添加了 加密
以及认证机制
的HTTP称为HTTPS
HTTPS并非是应用层的一种新协议,只是HTTP
通信接口部分用SSL
和TLS
协议替代而已
通常,HTTP
直接和TCP
通信。当使用SSL
时,则演变成先和ssl
通信,再由SSL
和TCP
通信了,简言之,所谓HTTPS
,其实就是身披ssl
这层外壳的HTTP
SSL
是独立于HTTP
的协议,所以不光是HTTP
协议,其他运行在应用层的SMTP
和Telnet
等协议均可配合ssl
协议使用。可以说SSL是当今世界上应用最为广泛的网络安全技术
相互交换秘钥的公开秘钥加密技术
使用共享秘钥----可能被窃听
加密和解密同用一个秘钥的方式称为共享秘钥加密
,也被称为对称秘钥加密
以共享秘钥方式加密时必须将秘钥也发送给对方。可究竟怎样才能安全的转移?在互联网上转发秘钥时,如果通信被监听那么秘钥就可会落入攻击者之手,同时也就失去了加密的意义,可以采用非对称加密将秘钥传给对方
使用两把秘钥的公开秘钥加密
公开秘钥加密使用一对非对称的秘钥。一把叫做私有秘钥
,另一把叫做公开秘钥
.顾名思义,私有秘钥不能被其他任何人知道,而公开秘钥则可以随意发布,任何人都可以获得。
使用公开秘钥加密方式,发送密文的一方使用对方的公开秘钥进行加密处理,对方收到被加密的信息后,再使用自己的私有秘钥进行解密。利用这种方式,不需要发送用来解密的私有秘钥,也不必担心秘钥被攻击者窃听而盗走
HTTPS采用混合加密机制(非对称秘钥加密+对称秘钥加密)
HTTPS采用共享秘钥和公开秘钥加密两者并用的混合加密机制
- 使用公开秘钥加密方式安全的交换再稍后的共享秘钥中加密要使用的秘钥(
使用非对称加密确保安全的把公有秘钥发送到客户端
- 确保交换的秘钥是安全的前提下,使用共享秘钥加密方式进行通信
证明公开秘钥正确性的证书
如何证明公开秘钥本身就是货真价实的公开秘钥?
为了解决上述问题,可以使用由数字证书认证机构和其相关机构颁发的公开秘钥证书。
- 服务器的运营人员向数字证书认证机构提出公开秘钥的申请。数字证书认证机构在判明提出申请者的身份后,会对已申请的公开秘钥做
数字签名
,然后分配这个已签名的公开秘钥,并将该公开秘钥放入公钥证书之后绑定在一起。 - 服务器会将这份由数字认证机构颁发的公钥证书发送给客户端,以进行公开秘钥加密方式通信,
- 接到证书的客户端可使用数字证书认证机构的公开秘钥,对那张证书上的
数字签名
进行认证,一旦验证通过,则可进行通信
此处认证机关的公开秘钥必须安全转移到客户端,使用通信方式时,如何安全转交是一件很困难的事,因此,多数浏览器开发商发布版本时,会事先在内部植入常用的认证机关的公开秘钥
HTTPS的安全通信机制
首先进行三次握手建立TCP连接
上文提到,对称加密算法的复杂度低,所以在传输数据的时候,使用的是对称加密算法
;
但是对称加密算法使用的是相同的秘钥
,为了保证秘钥的安全性,单独对秘钥的传输采用非对称加密
;
同时,数据的传输过程中时候用HASH算法用于验证数据的完整性
。
客户端有公钥,服务器有私钥,客户端用公钥对对称密钥进行加密,将加密后的对称密钥发送给服务器,服务器用私钥对其进行解密,所以客户端和服务器可用对称密钥来进行通信。
公钥和私钥是用来加密密钥,而对称密钥是用来加密数据`,分别利用了两者的优点。
1.浏览器将自己支持的一套加密规则
发送给网站,如RSA加密算法,DES对称加密算法,SHA1摘要算法
2.网站从中选出一组加密算法
与HASH算法,并将自己的身份信息以证书的形式发回给浏览器。证书里面包含了网站地址,``加密公钥
,以及证书的颁发机构等信息(证书中的私钥只能用于服务器端进行解密,在建立SSL连接之后,整个过程中都用到了证书中的公钥和浏览器发送给服务器的随机密码以及对称加密算法
)
3.获得网站证书之后浏览器要做以下工作:
a)验证证书的合法性
(颁发证书的机构是否合法,证书中包含的网站地址是否与正在访问的地址一致等),如果证书受信任,则浏览器栏里面会显示一个小锁头,否则会给出证书不受信的提示。
b) 如果证书受信任,或者是用户接受了不受信的证书,浏览器会生成一串随机数的密码(这其实就是用于之后数据通信的对称加密算法的秘钥
),并用证书中提供的公钥
加密(对秘钥的加密采用非对称
的方法)。
c) 使用约定好的HASH算法计算握手消息,并使用生成的随机数
(对称算法的秘钥)对消息进行加密,最后将之前生成的被公钥加密的随机数密码
,HASH摘要值
(已经被对称算法加密)一起发送给服务器
4.网站接收浏览器发来的数据之后要做以下的操作:
a) 使用自己的私钥将信息解密并取出浏览器发送给服务器的随机密码
(得到了对称加密算法的秘钥),使用密码解密浏览器发来的握手消息
(用对称算法的秘钥解HASH摘要值),并验证HASH是否与浏览器发来的一致。
b) 使用随机密码加密
一段握手消息,发送给浏览器。
5.浏览器解密并计算握手消息的HASH,如果与服务端发来的HASH一致,此时握手过程结束,之后所有的通信数据将由之前浏览器生成的随机密码并利用对称加密算法进行加密
。
从上面的4个大的步骤可以看到,握手的整个过程使用到了数字证书
、对称加密
、HASH摘要
,非对称加密算法
。`
加密算法
对称加密算法
对加密和解密使用相同密钥的加密算法。由于其速度,对称性加密通常在消息发送方需要加密大量数据时使用。对称性加密也称为密钥加密。对称式数据加密的方式的工作原理如图。所谓对称,就是采用这种加密方法的双方使用方式用同样的密钥进行加密和解密。密钥实际上是一种算法,通信发送方使用这种算法加密数据,接收方再以同样的算法解密数据。因此对称式加密本身不是安全的。常用的对称加密有:
DES
、IDEA
、RC2
、RC4
、SKIPJACK
算法等 。
采用单钥密码系统的加密方法,同一个密钥
可以同时用作信息的加密和解密,这种加密方法称为对称加密,也称为单密钥加密。
对称加密核心:数据加密标准+秘钥
优点:效率高
缺点:秘钥不适合在网上传输(可以采用非对称加密)
秘钥维护比较麻烦
非对称加密算法
与对称加密算法不同,非对称加密算法需要两个密钥:公开密钥
(publickey)和私有密钥(privatekey)
。公开密钥与私有密钥是一对,如果用公开密钥对数据进行加密,只有用对应的私有密钥才能解密;如果用私有密钥对数据进行加密,那么只有用对应的公开密钥才能解密。因为加密和解密使用的是两个不同的密钥,所以这种算法叫作非对称加密算法。
Hash算法(摘要算法)
常用的摘要算法包括MD5
,SHA1
,SHA256
消息摘要算法的主要特征是加密过程不需要密钥,并且经过加密的数据无法被解密,只有输入相同的明文数据经过相同的消息摘要算法才能得到相同的密文。消息摘要算法不存在密钥的管理与分发问题,适合于分布式网络相同上使用。由于其加密计算的工作量相当可观,所以以前的这种算法通常只用于数据量有限的情况下的加密
,例如计算机的口令就是用不可逆加密算法加密的。
一般不会去解密摘要算法加密的数据到底是什么,只会比较两个消息的密文是否一致,判断这两个原本的数据是否一致。
HTTP1.1特性
- 默认是长连接
- 支持流水线
- 支持同时打开多个 TCP 连接
- 支持虚拟主机
- 新增状态码 100
- 支持分块传输编码
- 新增缓存处理指令 max-age
HTTP2.0特性
HTTP1.X缺陷
HTTP/1.X实现简单是以牺牲性能为代价的
- 客户端需要使用多个连接才能实现并发和缩短延迟
- 不会压缩请求和响应首部,从而导致不必要的网络流量
- 不支持有效的资源优先级,导致底层TCP练级的利用率低下
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· 【自荐】一款简洁、开源的在线白板工具 Drawnix