websocket协议规范

WebSocket入门和帧协议详解 WebSocket学习笔记 简介 WebSocket 是一种基于 TCP 的网络协议。在 2009 年诞生,于 2011 年被 IETF 定为标准 RFC 6455 通信标准,并由 RFC7936 补充规范。WebSocket API 也被 W3C 定为标准。 WebSocket 也是一种 全双工通信 的协议,既允许客户端向服务器主动发送消息,也允许服务器主动向客户端发送消息。在 WebSocket 中,浏览器和服务器只需要完成一次握手,两者之间就可以建立持久性的连接,进行双向数据传输。 该协议的特点是: 阅读详情

以下内容转载自 https://www.huaweicloud.com/articles/4157e9b5a58ef15e29d71f76b08e1b92.html

websocket协议规范

作者:二郎666 时间: 2019-11-02 08:06:49

标签:网络通信协议websocketwebsocket协议客户端vbwebsocket教程

《websocket协议详解》教程分三篇:

  1.  什么是websocket
  2. websocket协议规范
  3.  用vb编写websocket客户端示例(每秒百万弹幕吞吐量)

文章上方有详细的规范、源码链接。邮箱:952125505@qq.com ,QQ交流群:715895604


本文与websocket协议规范有冲突的地方,请以规范为准。(文章上方有websocket中文协议规范的链接)

握手

首先由客户端发起连接请求,服务端验证客户端握手数据并返回服务器握手数据,客户端验证服务端握手数据,双方完成握手。

客户端:请求协议升级

首先,客户端发起协议升级请求。(采用标准 HTTP 报文格式,且只支持GET方法。)

GET / HTTP/1.1
Upgrade: WebSocket
Connection: Upgrade
Host: [LocalHost:80]
Origin: [LocalIP]
Pragma: no -cache
cache -Control: no -cache
Sec-WebSocket-Key: w4v7O6xFTi36lq3RNcgctw==
Sec-WebSocket-Version: 13
Sec -WebSocket - Extensions: x -webkit - deflate - Frame;permessage-deflate; client_max_window_bits
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.2171.99 Safari/537.36

各参数含义:

  • Connection: Upgrade:表示要升级协议
  • Upgrade: websocket:表示要升级到 websocket 协议。
  • Sec-WebSocket-Version: 13:表示 websocket 的版本。
  • Sec-WebSocket-Key:阻止无效websocket协议连接。

需要指出Sec-WebSocket-Key的生成算法,伪代码:base64编码(16个随机字节)

其它部分没有什么好介绍的,跟http头信息一样,可以查看http相关资料。

服务端:响应协议升级

HTTP/1.1 101 Switching Protocols
Connection:Upgrade
Upgrade: websocket
Sec-WebSocket-Accept: Oy4NRAQ13jhfONC7bP8dTKb4PTU=

返回状态码101表示协议升级成功(从http升级到websocket)。此后,所有的数据都按照websocket协议进行。如果返回其他状态码代表请求失败,必须要关闭连接(不要问为什么,规定)。具体状态码含义可以查看http相关资料。

Sec-WebSocket-Accept用于客户端验证服务器的合法性,如果验证数据不一致,客户端就要关闭连接。伪代码:base64编码(sha1加密( Sec-WebSocket-Key +"258EAFA5-E914-47DA-95CA-C5AB0DC85B11")  ),其中258EAFA5-E914-47DA-95CA-C5AB0DC85B11是固定的,代表websocket全局唯一标识符。 

至此,客户端与服务端已经完成协议升级。

数据帧

websocket中,数据是由一个个帧组成的,帧是组成一条数据的基本单位。

数据帧的大小是由数据帧类型决定的,而不是统一固定大小的数据单位。怎么理解,一个数据帧可以是一个bit大小,也可以N个bit大小。不同类型的帧大小可能是不一样的,但同一类型的帧大小是固定的。一条数据中包含多个帧,它们是由一个个不同类型的帧组成的,但不能含有两个或两个以上相同类型的帧。也就是说一个帧类型,在一条数据中只能出现一次。

每一种帧类型都代表一种特定含义,它们大小固定,顺序固定。

如下图,FIN就是一个数据帧类型,大小是1bit,位置是第1位。而opcode也是一种数据帧类型,它的大小却是4bit,位置是第5位。它们的大小和位置顺序是websocket定义好的,不能改变。

下面这幅图很好的说明了WebSocket 数据帧的格式,第一行代表数据大小,每一小格代表1bit单位。数据帧按照图中从左到右,从上到下的顺序排列。

websocket协议规范1

每一条websocket数据,都是按照上图所示的规则进行排列的。在本文中,我们把一条数据叫做消息。下面我们结合上图,详细的讲解各帧的含义。下文中未标注可以为空值的数据帧,都不能为空值。(如果讲解与websocket规范有冲突,请以规范为准)

FIN:1 bit

如果是0,表示这不是消息的最后一个分片。

如果是1,表示这是消息的最后一个分片。


分片我们可以理解为一条消息的一个部分,只要你愿意,可以随意将一条消息分为N个分片。比如“这是一条消息”是一条消息,则我们可以随意把它划分为 “这是”、“一”、“条”、“消息”,共4个分片。但是注意,websocket协议中没有分片顺序标识符,为了保证接收端能接收到正确的消息,分片要按严格按照前后顺序发送。

只有一个分片的消息不叫分片发送,很好理解,因为此时分片就是消息本身,一次即可发送完毕。只有包含两个或两个分片以上的消息才叫分片发送。

FIN用来判断消息是否接收完,如果是1,表示是一条消息的接收完毕。

RSV1, RSV2, RSV3:每个 1 bit

必须是0,除非一个扩展协商为非0值定义含义。如果收到一个非0值且没有协商的扩展定义这个非0值的含义,接收端就必须断开连接。

一般情况下,这三个帧全部定义为0就完事了。因为非0的情况下,对方会告诉你这些自定义值的含义,比如RSV1=1,代表对面很蛋疼的含义。当然如果你也要蛋疼自定义一些非0值,当我啥也没说。

Opcode: 4 bits

定义了“负载数据”的解释。如果收到一个未知的操作码,接收端点必须断开连接。

Opcode值含义(16进制)
值含义
0继续帧,表示消息分片模式
1文本帧,表示文本格式传输
2二进制帧,表示二进制格式传输
3-7保留(目前还未定义的意思),用于定义未来的非控制帧
8关闭帧,表示关闭连接
9Ping帧,一般主动发送ping给对方,确认对方状态
APong帧,一般发送了ping给对方,对方就回复pong
B-F保留,用于定义未来的控制帧


负载数据,可以理解为我们要传送的文字、视频、图片等数据。比如上面说的“这是一条消息”就是负载数据,对于分片发送模式,负载数据就是当前的分片,比如“这”。

关闭帧:只要收到关闭帧,必须立即关闭连接。发送关闭帧时,可以在扩展数据中附带状态码和应用数据中添加自定义数据。状态码含义见文章最后列表。这里你只需记住收到关闭帧就必须断开连接,其他的概念不理解也不要紧,下面我们有详细介绍。

客户端和服务端建立连接之后,可能遇到,双方长时间没有数据往来,被消息中间件断开连接的情况,或一方发生其它意外连接关闭而没通知对方的情况。为保证通讯的正常进行,就需要不定时的确认对方状态。websocket为我们提供了ping pong 心跳机制来避免这种意外的发生。

  • 发送方 ->接收方:ping
  • 接收方 ->发送方:pong

着重说一下Opcode=0,继续帧,表示当前分片消息不是消息的第一个分片,是一个继续分片。也就是说一条消息有两个或两个以上分片,才能出现Opcode=0的情况。结合FIN帧,我们就推导出当前消息是否是是分片发送,第一个分片,中间分片,最后一个分片。公式如下:

一条消息发送:

FIN=1且Opcode<>0

分片消息发送:

  1. 第一个分片:      FIN=0      Opcode=1 或 Opcode=2
  2. 中间的分片:      FIN=0      Opcode=0
  3. 最后一个分片:   FIN=1      Opcode=0

Mask: 1 bit

如果0,不需掩码处理。(不掩码处理也就无需掩码键,所以消息中masking-key为空值,也就是不包含masking-key帧)

如果1,需要掩码处理。(掩码处理必须要有掩码键,所以masking-key帧不能为空值)

根据websocket定义:

客户端发送数据需要进行掩码处理,接收数据无需反掩码操作

服务端发送数据无需进行掩码处理,接收数据需要反掩码操作

掩码与反掩码采用一样的算法,伪代码为:

j =i mod 4  

返回数据(i)=原始数据 (i) xor masking-key(j)

i分别代表返回数据和原始数据的第i个字节(byte),j代表masking-key(掩码键)的第j个字节。

掩码键生成算法见下面Masking-key介绍

Payload length: 7 bits, 7 + 16 bits 或者 7 + 64 bits

“负载数据”的长度,单位是字节(byte).

如果 0-125,这是负载长度。

如果 126,之后的2个字节(16 位)表示负载数据长度。

如果 127,之后的8个字节(64位)表示负载数据长度。而且最高有效位必须是0。

要确定负载数据长度,首先先判断第一个字节的值,如果>0且<125,那么这个值就是负载数据的长度,为0时候,就代表负载数据长度为0,也就是不包含负载数据。如果=126,那么就取这个字节后面的两个字节作为负载数据的长度。如果=127,就取后面8个字节表示数据的长度。

需要注意多字节长度数量以网络字节顺序来表示,也就是大端模式(按字节顺序的从左向右读取数据)。以第一个字节等于126为例,根据上面介绍,我们知道要取后面两个字节作为负载数据长度,假设两个字节的值分别为A0,01(16进制)。那么负载数据长度就是A001

Masking-key: 0 or 32 bits

指掩码键,可以为空值。客户端发送到服务器的所有帧必须通过一个包含在帧中的 32 位值来掩码。

如果 mask 位设置为 1,则该字段存在。

如果 mask 位设置为 0,则该字段缺失。

详细信息见上面介绍的Mask帧介绍

Masking-key的生成算法也很简单,就是由4个随机字节组成。

Payload data: (x+y)  bytes

指负载数据,可以为空值

“负载数据”=“扩展数据”+“应用数据”。

以上是负载数据的权威解释,前面说负载数据可以理解为文字、图片、视频的解释不确切,这样解释只是为了让读者更好的进入状态,通过这里我们知道,它只是代表无扩展数据时候的负载数据。

Extension data: x bytes

指“扩展数据”,排在负载数据最前面。可以为空值

通常是0字节,除非已经协商了一个扩展。任何扩展必须指定“扩展数据” 的长度,或长度是如何计算的,以及扩展如何使用,必须在打开阶段握手期间协商。

如果存在,“扩展数据”包含在总负载数据长度中。

其实就是自定义一个协议,如果有扩展数据,扩展数据就加在应用数据前面,并且要协商好扩展数据长度如何计算。

Application data: y bytes

指“应用数据”,“扩展数据”之后帧的剩余部分,可以为空值。

“应用数据”  = “负载数据”  -  “扩展数据” 

状态码

当收到一个关闭帧的时候,可能附带关闭的状态码,含义如下表

状态码名称描述
0–999 保留段, 未使用.
1000CLOSE_NORMAL正常关闭; 无论为何目的而创建, 该链接都已成功完成任务.
1001CLOSE_GOING_AWAY终端离开, 可能因为服务端错误, 也可能因为浏览器正从打开连接的页面跳转离开.
1002CLOSE_PROTOCOL_ERROR由于协议错误而中断连接.
1003CLOSE_UNSUPPORTED由于接收到不允许的数据类型而断开连接 (如仅接收文本数据的终端接收到了二进制数据).
1004 保留. 其意义可能会在未来定义.
1005CLOSE_NO_STATUS保留.  表示没有收到预期的状态码.
1006CLOSE_ABNORMAL保留. 用于期望收到状态码时连接非正常关闭 (也就是说, 没有发送关闭帧).
1007Unsupported Data由于收到了格式不符的数据而断开连接 (如文本消息中包含了非 UTF-8 数据).
1008Policy Violation由于收到不符合约定的数据而断开连接. 这是一个通用状态码, 用于不适合使用 1003 和 1009 状态码的场景.
1009CLOSE_TOO_LARGE由于收到过大的数据帧而断开连接.
1010Missing Extension客户端期望服务器商定一个或多个拓展, 但服务器没有处理, 因此客户端断开连接.
1011Internal Error客户端由于遇到没有预料的情况阻止其完成请求, 因此服务端断开连接.
1012Service Restart服务器由于重启而断开连接.
1013Try Again Later服务器由于临时原因断开连接, 如服务器过载因此断开一部分客户端连接.
1014 由 WebSocket 标准保留以便未来使用.
1015TLS Handshake保留. 表示连接由于无法完成 TLS 握手而关闭 (例如无法验证服务器证书).
1016–1999 由 WebSocket 标准保留以便未来使用.
2000–2999 由 WebSocket 拓展保留使用.
3000–3999 可以由库或框架使用.不应由应用使用. 可以在 IANA 注册, 先到先得.
4000–4999 可以由应用使用.

 

 

Linux下websocket协议实现 websocket协议的实现 阅读详情

相关推荐

解析网络与通信中Websocket的协议规范

在传统的网络通信中,HTTP协议是一种无状态的请求 - 响应协议,这意味着服务器无法主动向客户端发送数据,只能在客户端发起请求后进行响应。这种模式在一些实时性要求较高的应用场景中,如在线聊天、实时金融数据更新、游戏等,显得力不从心。Websocket协议的出现就是为了解决这个问题,它提供了一种在单个TCP连接上进行全双工通信的机制,允许服务器和客户端在连接建立后随时向对方发送数据。

AI算力网络与通信的博客 936

Nginx Websocket 配置

Websocket 简介 WebSocket 是一种基于 TCP 连接的全双工通信的协议,其工作在应用层,建立连接的时候通过复用 Http 握手通道,完成 Http 协议的切换升级,即切换到 WebSocket 协议,协议切换成功后,将不再需要客户端发起请求,服务端就可以直接主动向客户端发送数据,实现双向通信。 和 Http 相比,WebSocket有以下优点: WebSocket 是双向通信协议,可以双向发送或接受信息。HTTP是单向的,只能由客户端发起请求时,服务器才能响应,服务器不能主动向客户端发送

cr7258的博客 2108

若依集成MyBatis-Plus深度适配指南

MyBatis-Plus作为主流Java持久层增强框架,其Lambda表达式、自动填充、分页插件等特性显著提升开发效率;但当与若依(RuoYi)这类企业级权限框架集成时,因二者在SqlSessionFactory构建、拦截器执行顺序、数据权限注入机制及实体字段处理逻辑上存在深层耦合冲突,极易引发数据权限失效、分页异常、自动填充覆盖、代码生成器兼容性断裂等问题。技术价值在于实现MyBatis-Plus能力与若依权限体系的无缝协同——既复用若依的数据范围(DataScope)、租户隔离(TenantLine)、

weixin_34310369的博客 368

WebSocket通信安全概览

WebSocket协议旨在取代使用HTTP作为传输层的现有双向通信技术,以从现有基础设施(代理、过滤、身份验证)中获益,因为HTTP最初并不打算用于双向通信,所以这项技术也被视为提高效率和可靠性之间的权衡。

stopys6的博客 1430

半导体设备上位机开发:我的经验与新人指导

半导体设备上位机开发是一项复杂而富有挑战性的工作,需要扎实的技术基础和丰富的经验。如果你对半导体设备上位机开发感兴趣,欢迎随时与我交流,我会尽力帮助你!

qq_43518667的博客 2726

SigmaStar(星宸科技)SSC33X系列

目前,SSC33X系列已经量产了接近两年,SDK成熟稳定,功耗表现良好,图像效果出色,广泛应用在AI枪机、AI球机、AI云台机、多目摄像机、停车场闸机、人脸门禁、人脸锁、门铃猫眼等产品上。

Comake Online的博客 1万+

HTTP:协议升级机制(WebSocket)

HTTP协议提供了一种特殊的机制,这一机制允许将一个已建立的连接升级成新的、不相容的协议。 通常来说这一机制总是由客户端发起的(不过也有例外,比如说可以由服务端发起升级到传输层安全协议(TLS)),服务端可以选择是否要升级到新协议。借助这一技术,连接可以以常用的协议启动(比如HTTP/1.1),随后再升级到HTTP2甚至是WebSockets. 注意:HTTP/2 明确禁止使用此机制,这个机制只属于HTTP/1.1 升级HTTP/1.1的链接 协议升级请求总是由客户端发起的;暂时没有服务端请求协议更改的

OceanStar的博客 1万+

gtsam 学习十(ISAM2 理论)

翻译自:iSAM2: Incremental Smoothing and Mapping Using the Bayes Tree 摘要 提出了一种新型的贝斯树处理稀疏矩阵,在转化为因子图,可以更好的求解平方根信息和映射问题。 文中提出了三个概念 使用概率密度项进行矩阵分解 介绍如何将矩阵分解转换为贝叶斯数和条件概率密度 基于贝叶斯树提供了一种新的系数非线性优化:ISAM2(进行重新排序和重新线性化,避免了批量更新) 问题描述 文章聚焦与如何用增量实时解决非线性问题, 利用增量即新的测量值实时更新估

weixin_37781153的博客 8407

【译】 WebSocket 协议第十二章——使用其他规范中的WebSocket协议

概述 本文为 WebSocket 协议的第十二章,本文翻译的主要内容为如何使用其他规范中的 WebSocket 协议。 使用其他规范中的WebSocket协议(协议正文) WebSocket协议旨在由另一规范使用,以提供动态作者定义内容的通用机制。例如,在定义脚本 API 的规范中定义 WebSocket 协议。 例如一个规范首先需要建立...

weixin_33691817的博客 110

从零编写linux0.11 - 第二章 内核初始化

编程环境:Ubuntu Kylin 16.04 代码仓库:https://gitee.com/AprilSloan/linux0.11-project linux0.11源码下载(不能直接编译,需进行修改) 1.添加内核 进入内核后也不是马上进入main函数,上一章只是暂时设置gdt,此外还要设置idt和其他的一些初始化操作,这些内容也要使用汇编进行编写,此处采用AT&T格式的汇编。 以下为head.s的内容,为了简单起见,内容还是一个死循环。 .globl startup_32 startup_3

皓空Fly的博客 1263

基于COMSOL电弧动态仿真模型优化:高收敛性可修改吹弧方案解析

在多物理场耦合分析中,电弧动态仿真历来是数值稳定性的难点:温度从室温跃升至数万开尔文,电导率随温度呈数量级跳变,流动、传热与电磁场相互牵制,极易引发求解器不收敛或初始值不一致等问题。如何构建高收敛性的数值模型,成为开关设备灭弧性能优化的重要技术基础。通过合理的几何简化、全耦合求解器配置、辅助扫描初始化策略以及参数化建模手段,可以实现对吹弧方案的灵活调整与批量寻优,从而系统性评估气流速度、喷口形状、触头间距等关键因素对熄弧性能的影响。这类仿真方法广泛应用于断路器、接触器等高压电器产品研发,为灭弧室结构设计提供

weixin_29758911的博客 350

[连载 1] 如何将协议规范变成开源库系列文章之 WebSocket

这是系列文章的第一篇,也是非常重要的一篇,希望大家能读懂我想要表达的意思。 系列文章开篇概述 相对于其他编程语言来说,Python 生态中最突出的就是第三方库。任何一个及格的 Python 开发者都使用过至少 5 款第三方库。 就爬虫领域而言,必将用到的例如网络请求库 Requests、网页解析库 Parsel 或 BeautifulSoup、数据库对象关系映射 Motor 或 SQLAlche...

nightteam的博客 265

企业微信——智能表格学习

企业微信报表

qq_15138049的博客 1万+

SPI与I2C串行协议深度解析:从物理原理到实战避坑指南

在嵌入式系统设计中,芯片间的通信往往比片内总线更考验工程师的功底。与片内并行总线不同,片间通信采用串行方式以换取引脚效益和连接效率,而SPI与I2C正是这个领域的绝对主力。SPI依托移位寄存器实现全双工高速传输,适合Flash、ADC等数据密集场景;I2C则以开漏输出和上拉电阻构建多设备总线,通过地址、ACK与时钟拉伸机制优雅地管理低速外设。理解这些底层逻辑,工程师才能避开模式配置错误、总线锁死、片选悬空、DMA时序错位等经典陷阱。从EEPROM读写到高精度ADC采集,从TF卡SPI模式到OLED控制,本文

weixin_33795806的博客 380

〖Python接口自动化测试实战篇④〗- 接口自动化测试详解

[接口测试] 是相对来说和我们的功能测试 [最为接近的一种自动化测试手段] ,所以我们首先来学习接口测试的内容。 实现接口的开发与接口的测试,在我们的思维上、想法上、面向的态度上都是不一样的。所以大家要有一个思维上的转换,要认识了解一下 接口测试到底是做什么的、它的功能、意义在哪里。...

易编橙 · 终身成长社群,相遇已是上上签! 5万+

Modbus RTU 详解 + FreeMODBUS移植(附项目源码)

本文全面解析Modbus RTU通信协议,详细介绍其报文格式、功能码定义及典型应用场景,并手把手指导如何将FreeMODBUS协议栈移植到嵌入式平台。内容涵盖从底层硬件驱动配置、协议栈移植到主机/从机通信测试的全流程,重点讲解移植过程中的常见问题及解决方案。文章最后提供经过实测的完整项目源码,帮助开发者快速实现稳定的Modbus RTU通信功能。

Bit_Dong的博客 2926

websocket 发送图片_如何将协议规范变成开源库系列之 WebSocket (一)

这是系列文章的第一篇,也是非常重要的一篇,希望大家能读懂我想要表达的意思。一、系列文章开篇概述相对于其他编程语言来说,Python 生态中最突出的就是第三方库。任何一个及格的 Python 开发者都使用过至少 5 款第三方库。就爬虫领域而言,必将用到的例如网络请求库 Requests、网页解析库 Parsel 或 BeautifulSoup、数据库对象关系映射 Motor 或 SQLAl...

weixin_39683163的博客 147

TMS_WebGMaps_1.9.8.0_Full_Source

TMS_WebGMaps_1.9.8.0_Full_Source 全源码 delphi cbuilder

如何将协议规范变成开源库系列之 WebSocket (一)

这是系列文章的第一篇,也是非常重要的一篇,希望大家能读懂我想要表达的意思。一、系列文章开篇概述相对于其他编程语言来说,Python 生态中最突出的就是第三方库。任何一个及...

测试0901-1 329

efm32 入门.pdf

本PDF共计22页,介绍的是操作驱动API,下载与否自己斟酌。

Data-Structures-and-Algorithm-Analysis-in-Java.pdf

作者: Weiss, Mark A. 出版社: Addison Wesley Data Structures and Algorithm Analysis in Java is an “advanced algorithms” book that fits between traditional CS2 and Algorithms Analysis courses. In the old ACM Curriculum Guidelines, this course was known as CS7. This text is for readers who want to learn good programming and algorithm analysis skills simultaneously so that they can develop such programs with the maximum amo...

WebSocket协议规范浅析

初识WebSocket WebSocket是一个持久化应用层协议,随HTTP2.0协议标准发布,客户端(浏览器)和服务端可以维持一个长连接,双方均可自由地发送、接收数据(全双工),从而真正实现双向通信。WebSocket不像HTTP协议是请求——响应模式,需要客户端主动请求才能返回响应,HTTP1.1的长连接(keep-alive机制)实际上是一个伪长连接,它是无状态的,并且会在短时间完成数据请求...

APlus 1381

精雕软件(JDpaint) 5.5 绿色版

精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版

websocket官方文档_[连载 1] 如何将协议规范变成开源库系列文章之 WebSocket

这是系列文章的第一篇,也是非常重要的一篇,希望大家能读懂我想要表达的意思。系列文章开篇概述相对于其他编程语言来说,Python 生态中最突出的就是第三方库。任何一个及格的 Python 开发者都使用过至少 5 款第三方库。就爬虫领域而言,必将用到的例如网络请求库 Requests、网页解析库 Parsel 或 BeautifulSoup、数据库对象关系映射 Motor 或 SQLAlchemy、...

weixin_39812224的博客 164
上一篇: printf的归宿-数据打印到哪儿了
下一篇: C++ socket 网络编程 即时通信系统
shankusu2017
博客等级 码龄15年 155粉丝 155原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值