网络原理之HTTP

1. 初始HTTP协议

1.1 什么是HTTP

HTTP协议(Hypertext Transfer Protocol)全称超文本传输协议,存在于TCP/IP分层模型的应用程序中,是基于传输层的TCP协议实现的.

image-20220924193901734

要想理解HTTP协议,首先根据其名称依次分析:

  • 超文本

在早期的互联网中传输的数据都是以文本的形式显示,而随着互联网的高速发展,文本已经远远不满足人们的需求,它们想要传输图片,视频,音频等等,而这种被扩大语义的文本就叫做超文本

  • 传输

在网络上不同的主机,二者之间数据的发送和接受过程就是传输的过程

  • 协议

计算机网络中相互通信的对象之间交换信息时所必须遵循的规则

总结一下: HTTP协议规定网络中不同网络对象之间传输超文本数据时所遵循的规则.

1.2 认识HTTP

HTTP协议是以浏览器(Web Borser)为载体,当我们在浏览器中输入URL(即网站)后,DNS域名解析就会得到URL到IP地址的映射,然后根据请求向对应服务器获取响应结果,再由服务器把结果(以HTML格式)返回给浏览器,浏览器解析结果并显示在页面上.

那么我们是如何请求/响应呢?

在Edge浏览器中搜索某个资源,通过F12打开进入开发工具,下面是具体信息,每一个记录对应一个http响应/请求

image-20220924202531882

这里的状态,类型等等是关于http协议的内容,后面会讲解到.

2. HTTP协议格式

和学习IP/TCP协议相同,我们主要学习的是其传输格式与过程,分析其请求/响应时的细节,而我们可以使用抓包工具获取该细节

2.1 抓包工具的使用

以 Fiddler 为例. (下载地址: https://www.telerik.com/fiddler/)

  • 左侧窗口展示所有的HTTP请求/响应,
  • 右侧窗口上方显示某个HTTP请求的报文信息(点击Raw就可以查看详细信息)
  • 右侧窗口下方显示某个http响应的报文内容
  • 如果需要更加清楚的查看请求和报文的内容,可以点击View in Notepad以记事本的形式展示

小贴士: 可以使用ctrl + a 全选记录后,然后 delete删除全部记录

2.2 抓包工具的原理

Fiddler相当于一个代理:

当我们在浏览器中输入baidu.com后,就会把http请求先发送给Fiddler,然后Fiddler把请求发送给baidu.com.随后百度服务器就会把http响应发送给Fiddler,再把数据发送给浏览器

2.3 认识抓包结果

由于https会对请求/响应进行检查,否则浏览器会认为这是一个不安全的操作:

image-20220924214437760

所以要在`Tools -> options -> HTTPS, 全选后一直ok,弹出安全警告按是

image-20220924214709349

1) HTTP请求

image-20220924222545430

  • 首行 : 方法+url+版本
  • 请求报头(header): 其每一行都是由键值对组成,
  • body: 空行后面的内容,其允许为空,如果存在,那么在header中会存在Content-Length表示其长度
    • image-20220924223605048

2) HTTP响应

image-20220924224056287

  • 首行: 版本号+状态码+状态码解释
  • header: 请求的属性,同样是以键值对的形式组织数据,以空行作为结束标志
  • body: 空行后的数据.返回的数据可能是CSS/html/JS/图片等等

3) 总结

HTTP的请求/响应都是由首行+header+空行+body组成.

思考问题:为什么以空行作为header的结束标志

http在传输层依赖的是TCP协议,而TCP协议是面向字节流的,所以要明确包与包之间的界限,否则会出现"粘包"问题

3. HTTP请求(Request)

3.1 认识URL

URL(Uniform Resource Locator 统一资源定位符),即明确了互联网上每一个文件的唯一地址,URL 的详细规则由因特网标准RFC1738 进行了约定. (https://datatracker.ietf.org/doc/html/rfc1738)

image-20220924225338718

对于一个URL地址:

https://v.bitedu.vip/personInf/student?userId=10000&classId=100
  • https : 协议方案名. 常见的有 http 和 https, 也有其他的类型. (例如访问 mysql 时用的jdbc:mysql )
  • user:pass : 登陆信息. 现在的网站进行身份认证一般不再通过 URL 进行了. 一般都会省略
  • v.bitedu.vip : 服务器地址. 此处是一个 “域名”, 域名会通过 DNS 系统解析成一个具体的 IP 地址.(通过 ping 命令可以看到, v.bitedu.vip 的真实 IP 地址为 47.108.71.212)
    • image-20220924230229015
  • 端口号: 上面的 URL 中端口号被省略了. 当端口号省略的时候, 浏览器会根据协议类型自动决定使用哪个端口. 例如 http 协议默认使用 80 端口, https 协议默认使用 443 端口.
  • /personInf/student : 带层次的文件路径.
  • userId=10000&classId=100 : 查询字符串(query string). 本质是一个键值对结构. 键值对之间使用 & 分隔. 键和值之间使用 = 分隔.
  • 片段标识: 此 URL 中省略了片段标识. 片段标识主要用于页面内跳转.也叫做’锚点’

查询字符串(query string)

?作为查询字符串的开始标志,后面的键值对是由程序设计者确定,使用这样的发送可以达到某些需求或者信息

URL中可省略的部分

  • 协议名: 可以省略, 省略后默认为 http://
  • ip 地址 / 域名: 在 HTML 中可以省略(比如 img, link, script, a 标签的 src 或者 href 属性). 省略后表示服务器的 ip / 域名与当前 HTML 所属的 ip / 域名一致.
  • 端口号: 可以省略. 省略后如果是 http 协议, 端口号自动设为 80; 如果是 https 协议, 端口号自动设为 443.
  • 带层次的文件路径: 可以省略. 省略后相当于 / . 有些服务器会在发现 / 路径的时候自动访问/index.html
  • 查询字符串: 可以省略片段标识: 可以省略

3.2 URL encode

对于? =等字符在query string中已经含有其他意义了,如果我们需要使用这些字符,就需要进行转义

一个中文字符由 UTF-8 或者 GBK 这样的编码方式构成, 虽然在 URL 中没有特殊含义, 但是仍然需要进行转义. 否则浏览器可能把 UTF-8/GBK 编码中的某个字节当做 URL 中的特殊符号.

转义规则: 将需要转码的字符转为16进制,然后从右到左,取4位(不足4位直接处理),每2位做一位,前面加上%,编码成%XY格式 .

例如:

image-20220924231659088

这里的+被转义成了%2B

urldecode就是urlencode的逆过程;

urlencode工具:https://tool.chinaz.com/Tools/urlencode.aspx

3.3 请求 “方法”

image-20220924232015257

1. GET方法

GET方法常用功能是获取网络上某个资源(可以根据需要修改其功能,比如让服务器增加/减少/修改一个数据)

  • 触发GET请求的场景:

浏览器中输入URL,HTML标签: a,img,link,script等等,form,Javascript的ajax

  • GET请求的特点
  • URL 的 query string 可以为空, 也可以不为空
  • header 部分有若干个键值对结构
  • body 部分一般为空.
  • 关于 GET 请求的 URL 长度问题

HTTP 协议由 RFC 2616 标准定义, 标准原文中明确说明: “Hypertext Transfer Protocol – HTTP/1.1,” does not specify any requirement for URL length.

没有对 URL 的长度有任何的限制

2. POST 方法

POST 方法多用于提交用户输入的数据给服务器(例如登陆页面).

通过 HTML 中的 form 标签可以构造 POST 请求, 或者使用 JavaScript 的 ajax 也可以构造 POST 请求

例如:登录教务系统

image-20220924233948038

  • POST 请求的特点
  • URL 的 query string 一般为空 (也可以不为空)
  • header 部分有若干个键值对结构.
  • body 部分一般不为空. body 内的数据格式通过 header 中的 Content-Type 指定. body 的长度由 header 中的 Content-Length 指定
  • 经典面试题: 谈谈 GET 和 POST 的区别
  1. 语义不同: GET 一般用于获取数据, POST 一般用于提交数据
  2. GET 的 body 一般为空, 需要传递的数据通过 query string 传递, POST 的 query string 一般为空, 需要传递的数据通过 body 传递
  3. GET 请求一般是幂等的, POST 请求一般是不幂等的. (如果多次请求得到的结果一样, 就视为请求是幂等的).
  4. GET一般是可以被缓存(可以放入收藏夹中),POST一般是不可以被缓存的
  • 补充说明
  1. 关于语义: GET和POST不一定遵守其规则,都可以用于提交/获取数据
  2. 关于幂等性: 一般情况下GET是幂等的,但是也有例外,比如网站的推荐会根据你的爱好推送不同信息,多次请求的内容是不同的
  3. 关于安全性: GET传输数据会在query string中,是可见的,而POST是在body中,所以说"POST比GET安全",这是错误的,安全性取决于对这些信息的加密方式决定的.
  4. 关于传输数据量: 关于GET和POST传输数据量取决于服务器和浏览器之间的实现方式,并没有明确规定
  5. 关于传输数据类型: 关于GET只能传输文本数据这个观念是错误的,因为可以对Query sting的二进制进行urlencode

3. 其他方法

  • PUT 与 POST 相似,只是具有幂等特性,一般用于更新
  • DELETE 删除服务器指定资源
  • OPTIONS 返回服务器所支持的请求方法
  • HEAD 类似于GET,只不过响应体不返回,只返回响应头
  • TRACE 回显服务器端收到的请求,测试的时候会用到这个
  • CONNECT 预留,暂无使用

3.4 请求"报头"

image-20220925171504171

请求的报头数据是以键值对的形式组织的,下面介绍一些常用的

  1. Host

Host: cn.bing.com 代表服务器主机的地址和端口

  1. Content-Length

body部分的长度

  1. Content-Type

text/plain;charset=UTF-8,代表请求body中的数据格式,这里的说文本形式,

其他格式:

  • 数据为urlencode格式
    • application/x-www-form-urlencoded: form
  • 数据为 json 格式
    • application/json

关于 Content-Type 的详细情况: https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types

  1. User-Agent

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

  • Mozilla/5.0 (Windows NT 10.0; Win64; x64)
    • MDN的网站,后面是操作系统的版本信息image-20220925173022645
  • AppleWebKit/Chrome/Safari都是浏览器

对于标识浏览器身份的User-Agent,为什么每个浏览器都有Mozilla字样?

所以User-Agent描述你正在用什么样设备上网,浏览器就会根据这些信息调整内容,例如你是老版浏览器,就会给你推送简单页面;你是PC端.就给你推送pc版的页面

  1. Referer

表示这个页面是从哪个页面跳转过来的.

对于直接输入url或者点击超链接进入,是没有Referer

  1. Cookie

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

image-20220925175812208

往往可以通过这个字段实现 “身份标识” 的功能

每个不同的域名下都可以有不同的 Cookie, 不同网站之间的 Cookie 并不冲突

以登录gittee为例:

移除所有的cookie,刷新后发现登录信息被清空,需要重新登录.

  • 登录请求

image-20220925190247374

  • 登录响应

image-20220925190418031

响应中包含了3个Set-cookie,第三个就代表了当前用户登录的身份标识,是经过加密后的令牌(token)

在后面访问其他页面是请求中就会获取刚才的cookie

image-20220925190812149

所以在后序访问gitee时,就会一直带着这个令牌,直到清除缓存/令牌过期/重新登录

cookie生成和使用过程:

image-20220925191028240

3.5 请求"正文"

正文的内容格式和header中的Content-type/Content-length有关,就不多描述了,大家可以去抓包看看

4. HTTP响应(Response)

4.1 认识"状态码"(Status code)

状态码描述了访问一个页面得到的结果,下面是一些常见的状态码

状态码描述场景
200 OK访问成功
404 Not Found没有找到资源访问网络上不存在的资源
403 Forbidden访问被拒绝访问一些私有地址
405 Method Not Allowed方法不支持网页资源不支持GET, POST, PUT, DELETE 等方法之一
500 Internal Server Error服务器出现内部错误一般是服务器的代码执行过程中遇到了一些特殊情况(服务器异常崩溃)会产生这个状态码
504 Gateway Timeout响应超时当服务器负载比较大的时候, 服务器处理单条请求的时候消耗的时间就会很长, 就可能会导致出现超时的情况.比如双十一的秒杀活动
302 Move temporarily临时重定向就相当于手机号码中的 “呼叫转移” 功能 ,比如原来电话号码不用了,就可以去办理一个呼叫转移业务,别人打原来的号码时就会自动转移到当前的号码
301 Moved Permanently永久重定向当浏览器收到这种响应时, 后续的请求都会被自动改成新的地址

注: 在响应结果中会出现乱码的情况,这是因为HTTP为了节省带宽,对数据压缩,Fiddler可以解压,点击下面黄色框

image-20220925192846910

解压后,内容是很丰富的

image-20220925193029345

  • 状态码总结

image-20220925195157211

4.2 响应"报头"

和请求报头格式一致

重点理解Content-Type Content-Length

  • Content-Type

在响应中常见的取值有如下几种

  • text/html : body 数据格式是 HTML
  • text/css : body 数据格式是 CSS
  • application/javascript : body 数据格式是 JavaScript
  • application/json : body 数据格式是 JSON

关于 Content-Type 的详细情况: https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types

4.3 响应"正文"

响应正文取决于Content-Type,下面是常见的正文

  1. text/html

image-20220925200306856

  1. text/css

image-20220925200654709

  1. application/json

image-20220925200423748

  1. text/javascript

image-20220925200524646

5. 构造HTTP请求

通过代码构造HTTP请求主要有两种方式:

  • 基于form标签
  • 基于ajax

5.1 基于form构造请求

<form action="https://www.sogou.com/index.html" method="GET">
    <input type="text" name="username">
    <input type="password" name="password">
    <input type="submit" value="提交">
</form>

参数介绍:

  • action: 请求的url是什么
  • method: 请求的方法是什么?(form只支持GET和POST)
  • type: 输入数据的类型
  • name: 代表构造出请求的query string中的键值对的键
  • 我们填入输入框中的数据是query string中的键值对中的值,如果是POST就是body的内容
  • GET请求

image-20220925204539951

数据在query string中

  • POST请求

image-20220925204912541

数据在body部分

5.2 基于ajax构造请求

ajax(Asynchronous Javascript And XML),使用JS的异步通信,从服务器中获取XML文档的数据,再更新当前页面的数据,而不必刷新当前页面.

异步通信: 请求发送给服务器后,浏览器不用继续等待响应,而是由服务器把响应发送给浏览器.

通常情况下,其构造HTTP请求的步骤如下:

  1. 创建XMLHttpRequest实例
  2. 发送HTTP请求
  3. 接受服务器传回的数据
  4. 更新当前页面数据

ajax比form构造请求更加强大,它可以发送任何格式的数据(例如二进制)

1) 发送GET请求

<script>
    // 1. 创建XMLHttpRequest对象
    let httpRequest = new XMLHttpRequest();
    // 2. 异步处理数据时,处理当前响应的回调函数
    httpRequest.onreadystatechange = function() {
        if (httpRequest.readyState == 4) {
            // 获取当前http响应状态码
            console.log(httpRequest.status);
            // 获取响应body
            console.log(httpRequest.responseText);
        }
    }
    // 3.调用open方法设置要访问的url
    httpRequest.open('GET', '');
    // 4. 调用 send方法发送请求
    httpRequest.send();
</script>

image-20220929204550621

ajax 只能向同源网址(协议、域名、端口都相同)发出 HTTP 请求,如果发出跨域请求,就会报错,例如百度下的ajax不能访问搜狗的内容,

如果要想强行进行跨域, 则需要服务器进行配合, 在服务器的响应中 “允许跨域” 才可以

在引入ajax后浏览器和服务器的交互过程:

image-20220929205835186

2) JQuery

由于官方的ajax使用起来不方便,于是有许多js的第三方库,对ajax进行封装,最典型的就是JQuery

下载地址: https://code.jquery.com/jquery-3.6.1.min.js,点击进入页面如下:

image-20220929211353375

这些都是封装好的库,直接ctrl+a全选,后ctrl+c复杂,然后新建一个js格式文件,把刚才复制内容放入其中即可

image-20220929211610725

  • 使用方法

下载完成后导入即可:

<script src="./js/jquery.min.js"></script>
<script>
    // $是一个变量名, jquery中的API都是依赖于这个对象实现
    $.ajax({
        type: 'GET',
        url: 'http://www.baidu.com',
        // data: '这是body的内容'
        success: function(body) {
            // body是http响应的正文部分
            console.log(body);
        }
    })
</script>

6.HTTPS

HTTPS和HTTP相同也是应用层协议,只不过HTTPS比HTTP更加安全,因为在传输数据时,HTTP是明文传输的很容易被不法分子截取导致信息泄露,而HTTP引入一个加密层,保证传输的安全性.

6.1 运营商劫持

下载天天动听:

  • 未被劫持的下载网站:

image-20220929214925462

  • 被劫持后的下载网站:

image-20220929214950738

这是因为我们在网络传输的数据是必须经过运营商的网络,而运营商的网络设备就可以解析出你传输数据并进行篡改.

比如上面的例子,点击’下载’后浏览器会向服务器发送HTTP请求,那么运营商就可以截取HTTP响应,修改响应中的下载地址,这样就变成下载’qq浏览器’啦

image-20220929215348803

6.2 加密概念

提到加密就必须谈到密码学: 研究编译密码和破译密码的技术科学,从数学算法的角度看,其分为对称密码算法,非对称密码算法,杂凑算法

下面是一些关于加密解密所涉及的概念

明文: 未被加密过的数据

密文: 被加密算法加工后生成的数据,密文可以转化为明文

密钥: 密钥是一个特殊数据,在明文和密文转化过程中不可缺少的的数据.

例:

83 版 <<火烧圆明园>> ,有人要谋反干掉慈禧太后. 恭亲王奕䜣给慈禧递的折子. 折子内容只是扯一扯家常, 套上一张挖了洞的纸就能看到真实要表达的意思 .

明文: 奏折全文

密文: “当心肃顺, 端华, 戴恒” (这几个人都是当时的权臣, 后来被慈禧一锅端)

密钥: 挖了洞的纸

image-20220929220310632

image-20220929220323529

密码学的奠基人–也是计算机科学的祖师爷之一:艾伦·麦席森·图灵

image-20220929220646811

图灵大佬年少有为, 不光奠定了计算机, 人工智能, 密码学的基础, 并且在二战中大破德军的 Enigma 机, 使盟军占尽情报优势, 才能扭转战局反败为胜,计算机领域中的最高荣誉就是以他名字命名的 “图灵奖”

6.3 HTTPS的加密方式

加密的方式有很多种,对于我们程序员来讲,已经有大量的库将其封装好了,这里主要讲其中的两种方式:对称加密和非对称加密

1) 对称加密

对称加密是发送方和接收方同用一个密钥去加密和解密数据,所以叫做对称加密

image-20221002152852964

所以对于不聪明的黑客去截获数据,得到的是一份加密的数据,然而对于服务器在同一时间会向多个客户端通过数据传输服务,那么对于每个客户端又要去维护其独特的密钥(如果是相同的黑客就可以伪装成客户端去截获密钥).

image-20221002153353144

所以就引入了非对称加密.但是对称加密就没有作用吗?

对称加密的加密解密速度快,适合对大量数据加密

2) 非对称加密

非对称加密在加密和解密过程中使用的密钥是不同的,一个叫公钥,另一个叫私钥,把这一对密钥公开一个’公钥’,另一个不公开(私钥)

此时就可以使用公钥加密,私钥解密,或者使用私钥加密,公钥解密

举个例子:

坤坤家门口有一个邮箱,邮递员可以利用锁(公钥加密)给它上锁,然后坤坤就可以使用钥匙(私钥解密)拿到小黑子给他写的信

image-20221002155613745

  1. 客户端本地生成一对密钥,使用公钥把对称密钥进行加密,就可以把加密后的密文进行传输了
  2. 对于被黑客入侵的设备中截获的数据中只能得到服务器的公钥,而不知道服务器的私钥,无法解密获得对称密钥
  3. 服务器获得密文并解密后获得对称密钥

这里为什么传输的对称密钥?

非对称加密的开销是十分大的,所以使用非对称加密传输对称密钥保证安全,使用对称加密确保了效率

  1. 后续服务器客户端就可以利用对称密钥传输安全的数据

3) 证书

对于上述传输过程并不是完全安全的,会存在中间人攻击的缺陷

举个例子:

缉毒警察抓捕了一个毒贩,毒贩为了从轻发落,想帮助警察抓捕它的同伙A和B,由于毒贩之间交易都是不知道对方身份(确保交易安全),所以毒贩就分别约A和B交易,让警察伪装成毒贩去交易,从而对其一网打尽.这里的警察就是中间人

image-20221002162947986

首先客户端向服务器请求得到其公钥,黑客截获服务器的返回的公钥,虽然黑客不知道公钥的加密方式,但是它可以把这个数据截取,自己生成一对密钥,把自己生成的公钥发送给客户端,客户端不知道这个公钥的真实性,糊里糊涂的利用这个公钥加密传输密文,随后黑客就可以截获这个密文,由于它有私钥所以解密后得到明文此时得到的就是对称密钥,然后对明文使用对称密钥加密传输

为了解决中间人攻击,就引入了证书

在客户端和服务器刚一建立连接的时候, 服务器给客户端返回一个 证书

这个证书包含了刚才的公钥, 也包含了网站的身份信息

证书就相当于身份证,作为这个网站的身份标识. 搭建一个 HTTPS 网站要在CA机构先申请一个证书.但是它比身份证所包含更多的信息

证书发布机构,证书有效期,公钥,证书所有者,签名…

当客户端获取到这个证书之后, 会对证书进行校验(防止证书是伪造的)

  • 判定证书的有效期是否过期
  • 判定证书的发布机构是否受信任(操作系统中已内置的受信任的证书发布机构).
  • 验证证书是否被篡改: 从系统中拿到该证书发布机构的公钥, 对签名解密, 得到一个 hash 值(称为数据摘要), 设为 hash1. 然后计算整个证书的 hash 值, 设为 hash2. 对比 hash1 和 hash2 是否相等.如果相等, 则说明证书是没有被篡改过的.

image-20221002165611189

理解签名:

类似于发票报销,你需要报销,但是老板不能和你一起去,所以你就可以让老板签名,财务部’见字如见人’,就给你报销了

对于不同的人签名是不同的,然而对于密码学中的加密算法生成的签名是几乎不可能相等的.

常见生成签名的算法有MD5和SHA系列

以MD5为例:其特点为:

  • 定长: 无论多长的字符串, 计算出来的 MD5 值都是固定长度 (16字节版本或者32字节版本)
  • 分散: 源字符串只要改变一点点, 最终得到的 MD5 值都会差别很大.
  • 不可逆: 通过源字符串生成 MD5 很容易, 但是通过 MD5 还原成原串理论上是不可能的.

正因为 MD5 有这样的特性, 我们可以认为如果两个字符串的 MD5 值相同, 则认为这两个字符串相同.

Fiddler是如何实现抓包呢?

其实其实现方式就是中间人攻击,在首次勾选其https,提示是否要安装一个xxx证书,这个证书就是为了中间人攻击而使用的证书,安装后浏览器才会信任fiddle.

4) 完整流程

image-20221002170726882

5) 总结

HTTPS工作中涉及到的密钥有三组

  1. 利用非对称加密检查证书是否被篡改

服务器持有私钥:在证书注册时获得私钥

客户端持有公钥: 由操作系统提供可信任的CA认证机构,特殊持有其对应的私钥

服务器利用这个私钥对证书签名加密,客户端获取到密文后利用公钥解密获取到证书签名,随后效验证书是否被篡改.

  1. 利用非对称加密传输对称密钥

服务器生成一组密钥对,然后通过证公钥传输给客户端,然后客户端利用这个公钥给生成的对称加密的密钥加密,传输给服务器,服务器利用私钥加密获得对称加密密钥,从而实现通信双方对对称密钥的确定

  1. 对称加密传输数据

后续通信双方都可以利用这个对称密钥加密解密

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Zzt.opkk

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值