【计算机网络】应用层——HTTP协议基础详解

一.介绍和理解http

下面我们就介绍一下应用层的一个协议http协议,也叫做“超文本传输协议”

这个http协议主要负责服务器和客户端之间传输的内容的正确性

客户端通过http协议将请求发送给服务器,服务器通过http协议将响应返回客户端.这里http起传输消息和保证内容的正确性的作用

另外,它返回的时候,不仅仅会由css,html这些文件,还可能返回一些图片,视频等信息,所以http叫做超文本协议

tcp和ip主要负责的是传输的可靠性,主要负责安全的将请求和服务发送给对方

所以,这个应用层的协议是程序员主要打交道的地方,由程序员来指定双方要交换的内容是什么,这主要是由程序员设计的,其中,应用层设计最好的协议就是http协议了

它已经由三个版本了

http1-------http2--------http3

现在我们主流使用的还是http1

http1和http2对应的传输层协议是tcp

http3对应的传输层协议是udp

二. http的协议格式

http的协议格式和tcp/ip的协议的格式都是不相同的

http协议是文本类型的,所以我们可以直接看懂

tcp/ip是二进制类型的

所以,我们可以直接得到这个协议来观察它的格式

抓包

我们就可以使用代理来抓从客户端和服务器之间的http协议,可以将这些传输过程中的http抓到,相当于协议在代理这里中转了一下

这里我们使用fiddler来进行抓包

https://www.telerik.com/fiddler

使用fiddler的经典款就可以

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4Wqxghqo-1656774783375)(C:\Users\19625\AppData\Roaming\Typora\typora-user-images\image-20220626141133207.png)]

fiddler左侧是抓来的包,双击后右侧的上面是请求,下侧是响应

可以采用raw方式来查看最原始的请求和响应

请求格式

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0OiPYEpS-1656774783376)(C:\Users\19625\AppData\Roaming\Typora\typora-user-images\image-20220626141655644.png)]

上面是我抓到包的一个请求的报文,我们可以看出它由三部分构成。

状态行,请求报文,请求正文

一 状态行:

GET https://v7.dious.cc/20220623/dJ56dXZq/1500kb/hls/wKLYtJ17.ts HTTP/1.1

状态行也由三个部分组成,它们之间使用空格进行分隔

http的方法:这个http的方法就是get,表示想要从服务器得到一些资源,方法就是表明它想要从服务器做些什么

URL地址: url表示统一资源配置符,表示要访问的资源的位置

http版本号: http是什么版本的,现在我们大多数还是使用http1.1的

状态行就是http的第一行的内容,由上面的三个部分组成

1 方法介绍

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-iYXNjoSt-1656774783376)(C:\Users\19625\AppData\Roaming\Typora\typora-user-images\image-20220627213058290.png)]

上面是http1版本的一些常用的方法。

其中get的使用频率最高,占到了80%

其次是post,使用频率占到了10%

最后是其他的方法,使用频率加起来一共是10%

这些方法都是描述这个请求是要干些什么的

GET和POST的区别

首先:

​ 最重要的一点:GET和POST没有本质的区别.

​ GET可以来上传资源,POST也是接收资源,两者没有特别大的区分

但是:GET和POST还是有一些细微的区别的

  1. 语义上的区别

    • GET是获取数据的
    • POST是接收数据的
  2. body和query的区别

    • 一般情况下,GET是通过query String来获取到指定的数据的,所以它的url中必须包含queryString.但是它的请求报文中是不包含body的

    • 一般情况下,POST是将body中的内容上传到服务器上的,所以它的请求报文中是一定包含body的,而不包含queryString

      当然,这都是一般情况下,如果想要对GET和POST报文的格式进行更改也是可以的

  3. GET是幂等的,POST是不幂等的

    • 一般情况下,GET每次得到的内容都是相同的,所以叫做幂等的
    • 一般情况下,POST每次上传的内容不一定是相同的,所以是不幂等的
  4. GET是可缓存的,POST是不可缓存的

    • 正是因为GET是幂等的,结果相同,所以GET得到的结果可以被缓存
    • 正是因为POST是不幂等的,结果不同,所以POST不可以缓存
2 URL介绍

URL的全称是:uniform resourse locator 也就是统一资源定位符

我们访问的网址的官方的学名就是url.

下面介绍一下url的具体名称:

https://cn.bing.com/search?q=aps&PC=U316&FORM=CHROMN#ch1

  1. 身份认证:user:pass,这个现在不常见了,这个是为了标识用户的名字和密码,进行身份认证

  2. 协议方案名:是https还是http

  3. 域名/ip地址:cn.bing.com就是一个域名,通过DNS就可以知道它的服务器所在的ip地址,我们就是去这里来获取资源

  4. 端口号:这个现在一般都被省略了,表示去服务器的那个程序来获取资源.但是对于http的协议来说,端口号一般都是80;对于https的协议来说,端口号一般都是443

  5. 带层次的文件路径:/search就是文件路径,他就会到对应服务器的程序的search文件夹中来获取资源

  6. 查询字符串:q=aps&PC=U316&FORM=CHROMN 就是查询的字符串,它和带层次的文件路径之间使用?来进行分隔.对于这个字符串来说,也是一个键值对的结构.键值对之间使用&进行分隔,键和值之间使用=进行分隔

    q=aps&

    PC=U316&

    FORM=CHROMN

  7. 片段标识符:ch1,片段标识符和查询字符串之间使用#进行分隔.这个是为了标识当前的页面的位置的.我们前几年可能会经常看到一些新闻的页面,不是一下子全都显示完,而是分4个页面进行显示.片段标识符就是为了它们而显示的

虽然说,上面url的内容很多,但是最重要的还是下面的内容:

  1. ip地址(域名)
  2. 端口号
  3. 带层次的文件路径
  4. 查询字符串
3 URL encode和decode

对于查询字符串来说,有的时候我们要查询的内容是一些特殊符号和中文字符,为了避免歧义和解析失败,我们一般传输的过程中对这些内容进行encode,到服务器端的时候再进行decode

比如说我们搜索C++的时候,这个搜索页面的url的查找字符串的内容++就是被encode后的

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-YDXxRcRF-1656774783376)(C:\Users\19625\AppData\Roaming\Typora\typora-user-images\image-20220626230922589.png)]

++被转义为%2B,因为+的ASCII码的16进制的值是2B,所以它在url中被转义为%2B

%开头表示是一个转义的字符

对于这种特殊的字符,一定要进行转义,要不然就会解析失败

decode就是encode的逆过程,将转义的字符转化为原始的字符

二 请求头(head)

请求报文是由一系列的键值对组成的,键和值之间使用"冒号和空格"来进行分隔

Accept: */*
Origin: https://jx.huishij.com
Sec-Fetch-Site: cross-site
Sec-Fetch-Mode: cors
Sec-Fetch-Dest: empty
Referer: https://jx.huishij.com/
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9

键值对的数目没有具体的要求

几个常用的报头
host

表示主机的ip地址和端口号

Host: www.zhihu.com

这个域名就可以被转换为ip地址

content-length

表示body中的数据的长度.

那为什么要说明这个报文的body的长度呢?

在tcp协议的时候,它接收的报文都是黏在一起的,也就是粘包问题.传输层不负责解决这个问题,而是应用层来解决.

对应有body的报文来说,就是指定报文的长度,这样就可以将包分隔开

对于没有body的报文来说,就是使用分隔符分隔,也就是请求报文中的空格

content-type

表示body中数据的格式

主要有下面的几种格式:

Content-Type: application/json

​ json格式,就是键值对格式:

​ {“video_id”:“1517519444352282624”}

Content-Type: application/x-www-form-urlencoded form 表单提交的内容

​ www-form格式,就是和GET报文的query String的格式一样.只是POST报文将这些内容放到了报文中,防止放到url中占据太长的空间.

​ 和queryString一样,都是使用&将键值对之间进行分隔,使用=将键和值进行分隔,还使用urlencode进行解析

encrypt_key=password**&**utf8=%E2%9C%93**&**authenticity_token=Q1jASYfzKodKIzMjYJhbATxxP7t5lkCSVArOHnxdipoX%2FDApewAHrnzydDt%2BTwx6kRf2MveQYaSBNcXLWrATUw%3D%3D&redirect_to_url=**&**user%5Blogin%5D=15948760681**&**encrypt_data%5Buser%5Bpassword%5D%5D=ZTjYRFTIX6TcjCUZNYwpFEXIAvK3u3hn94iwpPHyoR8hM9OFfboR6iybGaf5ghxFBe%2F6I3VWw40Myn3m3xYvt6z2v1MuxbPOySzTQJ5wa8Dti5eNKPfPi4pyGiVTBuJM5E4Wntv9ryFHqsM0mNAMrazKOjELeTrhjJl%2FjRAdk%2FA%3D&user%5Bremember_me%5D=0&user%5Bremember_me%5D=1

Content-Type:multipart/form-data: form 表单提交的数据格式(在 form 标签中加上

​ 通常用于提交图片/文件

​ title ------WebKitFormBoundaryrGKCBY7qhFd3TrwA

​ Content-Disposition: form-data; name=“file”; filename=“chrome.png”

​ Content-Type: image/png

user-Agent

表示用户使用的设备是什么类型的

User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/101.0.0.0 Safari/537.36

主要显示操作系统信息和浏览器信息

其中Windows NT 10.0; Win64; x64表示操作系统信息

AppleWebKit/537.36 (KHTML, like Gecko) Chrome/101.0.0.0 Safari/537.36表示浏览器信息

那么为什么我们要指定这样的信息呢?

在互联网的上古时期,因为有些浏览器支持的功能不一,所以服务器就会根据浏览器的类型来返回不同的页面.所以是哦那个user-agent来表示当前的浏览器信息

在互联网成熟的现在,几乎所有的浏览器支持所有的功能了,只是又出现了移动互联网.即手机和电脑的区别,这两者的页面也是很大的,所以还是得需要user-agent来显示是什么样的类型的浏览器

referer

表示当前的网页是从哪里跳转过来的

: https://www.baidu.com/baidu.php?url=a00000KEJeCxDFezE8iyP5xXpqM2Zu2nl7h6Sxi5hJKA1rcNZpQrbHyqYUH

这个网页就代表是从百度跳转过来的.

这个有什么用处呢? 这个可以帮助广告主了解那一搜索引擎的流量大,就是通过这个referer来查看来源是那个网页的

cookie

cookie是一个很重要的一个值,它保存了一个网址的向前浏览过的信息的记录,并将这个数据保卫存在本地的磁盘中,如一个网站的登录信息:登录账号和登录密码,之后就不用重新登录了就是因为cookie的存在.

在这里插入图片描述

来源: 一种是js直接写入,还有一种就是通过http响应报文中的set-cookie将数据返回给服务器保存.

当我们首次登录这个码云页面的时候,先发送一个POST请求给服务器中,将我们的用户ID和密码上传.服务器返回响应报文的时候,就会通过set-cookie将cookie的内容返回

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xS3NxfT8-1656774783377)(C:\Users\19625\AppData\Roaming\Typora\typora-user-images\image-20220628225639374.png)]

cookie和session

  1. cookie里面存的是以往的信息,但是这个信息不是存在cookie里面,而都是存在服务器中的,cookie里面存放的是一个编号,通过这个编号,我们就可以查询到以往的记录
  2. 这个cookie中存放的编号就是sessionID.
  3. 我们将一个一个的行为叫做session或会话.这个会话记录着我们的每一个的对话,行为.每一个会话都有一个ID.就是sessionID.cookie中保存的就是这个

Cookie: oschina_new_user=false; user_locale=zh-CN; tz=Asia%2FShanghai; sajssdk_2015_cross_new_user=1; **gitee-session-**n=b0J0WTFCUVhMY0wvQ2dVR09hVkYxTUVMcWRoZEhpUkllRTE2bXJUejYzSUQ0R21pQ3QrakdHTUs5WDUrcVVuMUFSOENGeXM0Vk9jN1ZUbFYrQytSekFCSDJCTlBCNmlWckVmbUxia3VrNHFaMzFMSUlWdVUxcnRQR2czT2d2MDdJWEM1RHEwTUVuVldscTlvQmJlRktRVysrMGRFTFhqWFFQRXhZNG0xelBxaW14Z21rTE9HTXk3Q3J2UHMraE5kLS1JdUdLRnZwUXB4OVJ2SU9XaUdoNXZBPT0%3D--db8de56e8f1121d4d647dae70e741280a1c419c9

上面的gitee-session 就是可能cookie中保存的id

三 空格

请求头的下方是一个空格,用来分隔请求头和请求正文

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Kc0X8Boq-1656774783378)(C:\Users\19625\AppData\Roaming\Typora\typora-user-images\image-20220626224600181.png)]

两行文字之间的空格就是为了表示请求头的结束

四 请求正文(body)

这个请求正文是可有,可无的,

响应格式

一 状态码

状态码是为了描述这个响应的状态是什么样的,是成功还是失败,是服务器的问题,还是客户端问题

常用的状态码有下面的几种

状态码状态解释
200OK访问成功
404Not Found客户端访问的资源不存在
403Forbidden客户端无权限访问
405Method Not Allow不支持请求的方法某些GET,POST,PUT等方法
500Internal Server Error服务器执行过程中出现了bug ,程序有错误
504Gateway Timeout服务器执行的时间过长超时
302Move temporarily重定向,表示请求发送之后跳转的状态和location匹配
301Move Permanently永久的重定向,无论是什么请求都跳转的状态和location匹配
418I am a teapot小彩蛋

总结:

2xx 表示成功访问

3xx 重定向访问

4xx 客户端访问错误

5xx 服务器访问错误

二 header

content-type

描述了body中的格式

类型描述
text/cssbody 数据格式是 CSS
text/html : bodybody数据格式是 HTML
application/javascriptbody 数据格式是 JavaScript
application/jsonbody 数据格式是 JSON

三. 构造http请求

上面我们了解到了http的一些格式,下面我们就来自己来构造出一个http请求

构造http请求的方式一共有下面三种:

前端:
form表单

ajax

后端:

socket api

我们主要使用前端的方式来进行构造,因为http请求本身就是从浏览器发出的所以还是使用前端的构造方法更好一点

form表单

使用form表单构造http请求

使用get报文

我们的html代码就这样写:

form表单中有input标签,text标签,password标签,submit标签,通过submit标签进行提交

    <form action="http://www.baidu.com" method="get">
        <input type="text" name="username">
        <input type="password" name="password">
        <input type="submit" value="提交">
    </form>

因为我们使用的是get形式的报文,所以,最后的url就会跳转到:

https://www.baidu.com/?username=luoxy&password=123

这些提交的信息,就会以queryString的方式显示在url上面.

name属性表示key,输入的内容是value.其中name属性是一专门用来键值对表示的一个属性,不是用来描述样式的

使用post报文

	<form action="http://www.baidu.com" method="post">
        <input type="text" name="username">
        <input type="password" name="password">
        <input type="submit" value="提交">
    </form>

同样的代码,只不过我们更改了方法的属性为post

我们再来看一下这个时候的请求报文

POST https://www.baidu.com/ HTTP/1.1

body里面的内容:

username=luoxy5520&password=123

我们观察到,query String的不出现url中了,反而是出现到了body中.

ajax

从前端的角度来说,还有一个很好的方法就是使用Ajax,ajax的全名是:

Asynchronous javaScript and xml, 翻译过来就是异步的使用JavaScript来构造的请求

这个ajax可以不刷新页面/跳转页面

下面浅谈一下异步和同步

同步分为堵塞性和非堵塞性

​ 对于堵塞性同步就是调用者一直原地等待着被调用者返回的数据,期间不去做任何的事情,只是等待这一件事情

​ 对于非堵塞性同步就是调用者是隔一段时间去查看被调用的对象是否返回数据.而不是一直死守,就是非堵塞性同步

堵塞性和非堵塞性的区别就是是否一直在等待被调用者

异步:

​ 异步是调用者不主动的去看被调用者是否返回数据.而是一直在做其他的事情,等到被调用者通知它,它采取处理这件事情

异步和同步的区别就是调用者是否是主动的查被看调用者的状态

这次的ajax我们就是采用的异步的状态,当浏览器发送完http请求之后,浏览器就不再管http响应什么时候回来,而是等待http响应回来的时候通知浏览器.这时候接到通知后在去处理响应.

使用jQuery来构造

只使用JavaScript的话太麻烦,我们可以使用jQuery来帮助我们构造http请求.

引入jQuery的方法:

  1. 进入jQuery cdn官网
  2. 找到相关链接,并打开它
  3. 赋值全文并保存到本地中
  4. 在script标签中引入它
<!-- 引入jquery -->
    <script src="./jquery.js"></script>
    <script>
        // $是js中一个特殊的变量,是jQuery的核心变量,各种api都是通过$进行触发的
        $.ajax(
            {
                // 表明方法类型
                type:'get',
                // 请求的目标地址
                url:'http://www.baidu.com/index.html',
                success:function(body){
                    //这是一个回调函数
                    //如果成功接收到了响应的话,就触发success中的函数,
                    //回调函数中的body就是响应的返回的body
                    console.log("获取响应成功"+body);
                },
                error : function(){
                    //这是没有接收到响应的回调参数
                    console.log("获取响应失败");
                }
                
            }
        );

构造出的响应报文:

返回的响应报文:

控制台的输出:

分析上述结果,明明返回的报文的结果是200 表示OK的,但是为什么控制台中显示响应失败呢?

​ 控制台的警报说,不可以跨域访问,不可以从本机地址到服务器的地址.而必须发出请求的地址和要到达的地址必须是相同的才行

三. https

http在传输的时候,黑客就可以直接进行抓包,对请求的数据进行篡改,获取客户端和服务器之间的传输,所以为了保护传输过程中的内容,我们就需要给内容进行加密.这就是https

下面明确几个概念:

明文:要传输的明确信息

密文:加密后的模糊信息

密钥:将明文变为密文,将密文变为明文的钥匙

https中引入了一个加密层,就是在这一层将明文变为密文

这一层也叫做SSL,TLS

对称式加密

SSL总有两种加密方式,一个是对称性加密,一个是非对称性加密

对称性加密就是加密的密钥和解密的密钥是同一个

image-20220630221018838](https://img-blog.csdnimg.cn/a22b472d0cfa4b1b8a9819880822aed5.png)

客户端将http的请求报文经过密钥加密后变为密文,所以传输的过程中就报文就会以密文的方式传输,别人就无法得知真正的明文是什么,保证了安全性

服务器接到密文之后,就使用相同的密钥进行解密,得到可以看得懂的明文


但是上面的机制有一些弊端:

  1. 多个客户端对应多个密钥

    每一个客户端的密钥是不同的,因为如果客户端的密钥设置相同的话,破解了一个其他的客户端也就破解了

    因为密钥是对称性的,每一个客户端对应一个密钥,所以服务器就保存了多个密钥.

  2. 密钥在客户端和服务器之间使用网络传输

    正是因为每一个客户端对应一个密钥,并且服务器还要保存这个密钥.这个密钥的传输的时候是通过网络进行传输的.

    但是在传输的时候,也会发生密钥的泄露风险.所以在传输的时候还要对密钥进行加密,这个就是对称性加密的缺点

非对称性加密

这个非对称加密就是为了解决上面的对称性加密的密钥的泄露的问题的

因为密钥容易丢失,所以我们就再次对密钥也进行加密.

非对称加密分为公钥和私钥

公钥人人都可以知道

私钥只有服务器可以知道

服务器生成了公钥和私钥,公钥都发出去,只有私钥自己留着.

  1. 首先,客户端那里对明文还是使用正常的对称性加密生成对称性密文
  2. 对于对称性密钥,使用公钥进行加密,生成密文密钥
  3. 黑客拦截到这个密文密钥的时候,因为他不知道私钥,所以他不会破解
  4. 只有服务器有私钥,可以解开密钥的密文,得到明文,再一次得到对称明文

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xneJnbnd-1656774783378)(C:\Users\19625\AppData\Roaming\Typora\typora-user-images\image-20220630225348934.png)]


对称性加密和非对称性加密的关系

  1. 非对称加密是在对称性加密的基础上进行补充的.

  2. 非对称性加密主要是为了解决密钥的传输问题的,将密钥明文加密为密钥密文传输

  3. 一般来说,客户端和服务器只有第一次传输的时候使用非对称性传输.因为这次之后,服务器就或得了对称密钥的明文,再之后的传输都是使用对称传输.因为密钥明文已经知道

  4. 不全部使用非对称性加密的原因是非对称性加密的效率远大于对称性加密,所以我们使用非对称性加密+对称性加密相结合的方式

中间人拦截

但是上面有一个安全隐患,就是服务器将公钥发送给客户端的时候,可能会被黑客劫持.

  1. 中间人得到了服务器像客户端发送的公钥,就将这个公钥私吞.
  2. 中间人再自己生成一个公钥2和私钥2,将这个公钥2发送给客户端
  3. 客户端的对称密钥和公钥2加密之后得到对称密钥密文,中间人通过私钥2就将密文破解,得到了密钥的明文,
  4. 这样中间人就可以用对称密钥对对称密文进行破译,得到请求明文了

所以,万一中间人拦截了原来的公钥,再自己生成一个新的公钥,密钥之后,那么最重要的对称密钥就被破解了,那所有的加密对黑客来说都没有意义了,这个还是非常可怕的

正常情况下:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UToB7eW4-1656774783379)(C:\Users\19625\AppData\Roaming\Typora\typora-user-images\image-20220630231541218.png)]

服务器正常的将公钥发送给客户端,没有中间人拦截

中间人拦截

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-PPFzrTDi-1656774783380)(C:\Users\19625\AppData\Roaming\Typora\typora-user-images\image-20220630232752828.png)]

中间人拦截了公钥1的传递,并伪造了公钥2,私钥2,并成功的破译了对称密钥

证书

那为了解决中间人拦截伪造公钥的问题,我们就想了一个办法,就是返回一个经过公信机构验证的,带有证书的公钥.

这样,中间人伪造的公钥格式和官方的不对,也没有官方的认证,所以就被认为是假公钥,客户端就不会再相信这个公钥了.

  1. 服务器再最开始提供服务的时候,提供一些资源证据,去公信机构申请 一个证书,这个证书里面就包含了公钥
  2. 然后就将这个带有公钥的证书发给了客户端
  3. 客户端和本地的公信机构系统进行查询,如果是合格的,说明是正确的公钥;如果查询不合格,说明是一个伪造的公钥,浏览器就弹窗警告,说明是一个不合法的公钥

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-46QH8awz-1656774783380)(C:\Users\19625\AppData\Roaming\Typora\typora-user-images\image-20220630235040827.png)]


但是,我们是怎么通过fiddler来查看https的请求和响应的呢?

其实fiddler充当的是一个我们相信的中间人.

fiddler同样会拦截服务器发来的证书,并返回fiddler自己的证书

但是返回的这个证书不会报警告,因为我们已经授权了相信fiddler的证书.

所以,后续客户端发来的密钥密文就可以被fiddler破解,可以被抓包显示到了

但是前提是我们要授权相信fiddler的证书

将上面的三个选项全部勾选,就是同意了fiddler的证书

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值