元宇宙(Metaverse)是利用科技手段进行链接与创造的,与现实世界映射与交互的虚拟世界,具备新型社会体系的数字生活空间。
元宇宙本质上是对现实世界的虚拟化、数字化过程,需要对内容生产、经济系统、用户体验以及实体世界内容等进行大量改造。但元宇宙的发展是循序渐进的,是在共享的基础设施、标准及协议的支撑下,由众多工具、平台不断融合、进化而最终成形。
以上是百度百科的内容,看不懂也没关系。一句话概括,无论多么复杂的宇宙,都是从无到有的过程。那么我们现在就从最原始最基础的“游戏开发”做起。
开发过程初步规划为以下几个阶段:
1.先开发一个功能多元化的网络游戏。
2.为游戏增加区块链虚拟货币系统。
3.开发NFT认证系统,并能兼容传统游戏。
4.开发虚拟与现实接轨的互动接口。
5.接纳原有传统游戏的生态体系,使之成为元宇宙游戏的一部分。
什么是功能多元化的网络游戏呢?与传统网络游戏不同的是,其功能极其集中化,比如虚拟社区、虚拟经济、虚拟商城、虚拟办公等等新兴的互联网形式,都是在其各自的领域,独立发展的。而功能多元化的游戏就是把众多元素集中于一个系统下。
可能还会有很多人不太明白具体意思。就比方说,以前开发一个游戏,会有专门负责用户登陆的“登陆服务器”(LoginServer),专门负责游戏处理的游戏服务器(GameServer),专门负责游戏公告宣传和用户注册等等功能的网站服务器(WebServer),专门负责数据存取传输的数据服务器(SQLServer、MySQLServer),至于商城社区、动态均衡、服务器集群等等就不再列举了。
而功能多元化的游戏就只能有一个功能集中于一体的服务程序。比如以前开发网站可以用ASP、PHP、JSP、ASP.NET等脚本语言去开发。而多元游戏服务程序就不能使用任何已知的脚本语言来开发,必须由自身来完成。最终的服务器端只有一个服务程序,包含元宇宙游戏所能涉及到的全部功能,自我完成多分区多线路的游戏分发和调配工作。
为什么一定要强调功能集中化呢?因为元宇宙是要求虚拟与现实相结合的全新宇宙时空。就比如说我们现实生活的宇宙,所有物理定律、公理常数等等,一旦定义就不能再被更改。而传统的很多编程和脚本语言都是无法跨平台的,即使能跨平台,也是它执行它的,你执行你的。元宇宙游戏要求极高的瞬时性,如果还在使用传统的编程开发方式,用一个个独立的程序代码去执行,是无法完成瞬间统一协调的。
然而目前能够满足元宇宙游戏的基础设施还没有出现。比如在软件开发方面,还没有一种编程软件可以完成所有人类使用程序的应用需要。而在硬件方面,即使是5G全方位覆盖也无法满足元宇宙游戏的带宽需求。
但是我们也不能因为这些不足就不去做了呀,现在离真正的元宇宙还十分遥远。我们可以先初步开发相对接近于元宇宙的游戏,然后还要不断的调试、改进、完善和升级。就比如我们现在的宇宙在“大爆炸”那一瞬间就产生了一切发展的定数、天理、常规等等统称为“道”的东西。但是在“大爆炸”之前呢?谁知道设计和创造宇宙的那个“无名”,经历了多少次的修改,多少次的重启,才有了我们现在能认识到的精美绝伦的宇宙。
现在规划元宇宙都是在制定统一的标准协议,但是现有的各种操作系统根本无法“统一标准”。所以元宇宙是要求设计全新概念的操作系统。我们要等到这些齐备了,岂不是要等到猴年马月?
为什么“他们”说要全新概念的操作系统呢?网上的解释多了去了,我只从现在即将面临的一些问题,来反映一下具体的情况。就比如说游戏在跨平台时,最基本的文字传输就面临标准不统一的问题。新兴的平台多数采用UTF-8这个标准,但是在Win系统下的软件都只有ANSI和UNICODE这两个标准。如果你使用可以跨平台的编程或脚本语言,一定是采用统一的一个标准。而我们实际上必须所有标准都要兼顾,否则就只能等到全球人类都换成统一的一个操作系统时,才能开发元宇宙了。
废话不多说,我们开始干活。首先我们要自己设计网站服务器,可不是用传统脚本语言。是不用任何现成的工具,自己去实现。这在现有编程体系里,都没资料说网站服务器如何自己写。那么我们只能采用最新的编程软件——SEC中文编程来开发,因为它是不依赖于任何现有编程语言的框架,可以从零开始搭建完全独立且又开放兼容的软件程序。下载 z5x.cn/Sec.rar
这个网站服务器可不是基于任何你听说过的脚本语言,所以你现在不要去想你以前会的网站代码怎么写。那么我们先来熟悉一下WebSocket协议吧,先来热热身,醒醒脑。提前预告一下,这个协议我猜你短时间看不懂,今天时间有限,明天我开始实战教学。在SEC编程里,开发WebSocket的网站服务器,核心代码大约十几行,包括封包处理、协议解析、SHA1加密、ANSI和UNICODE到UTF-8的相互转换,密文解密和传输等等,我都会逐一介绍,你只要复制我提供的代码,直接编译即可。
还有汪诘老师有专门科普元宇宙的详细视频,他主要涉及的是元宇宙的基本原理和概念知识,比如会提及区块链、SHA256签名加密等等相关知识点,但不会讲实际应用的部分。我可能讲得没他那么生动,但是我每给你一段代码都会尽量详细描述它的作用原理和如何应用。
通常 Web 应用的交互模式是由客户端向服务端发送 HTTP 请求, 服务端根据客户端的的请求返回相应的数据, 在这样的交互模式下, 通信双方并不是对等的, 因为所有的请求都是由客户端主动发起, 对于 HTTP/1.x 协议 [RFC 1945], [RFC 2616] 来说, 协议本身并不提供服务端向客户端主动推送数据的机制, 因此基于 HTTP/1.x 的 Web 应用, 若需要获取服务端的数据或状态只能采用不断轮询 (Long Polling) 的方式, 最典型的例子如持续集成软件 Jenkins, 在 Job 构建过程中需要在浏览器向用户展示实时的 Console Output, 如果你在构建过程中进入浏览器的开发者模式便可以看到 Jenkins 采用周期性地向服务端发送请求以拉取实时的 Console 输出数据, 再例如一些基于 Web 的网络游戏, 例如 FPS 类游戏, 客户端需要知道当前实时的全局状态, 如其它玩家当前的坐标, 装备等, 如果使用 HTTP/1.x 协议则只能采用不断轮询服务器的方式以获得最新的状态数据, 这种方式一方面效率不高, 而且不够实时, 消息的实时性取决于两次轮询的时间差 (Gap), 最坏情况下需要晚于 1 个 Gap 才能拉到最新的数据, 另一方面频繁地轮询也增加了服务端额外的负载, 客户端需要单独维持一个连接用于轮询服务器状态, WebSocket 协议便是为了解决这个问题, WebSocket 协议提供了一种全双工的通信机制, 服务端可以主动向客户端推送数据, WebSocket 协议采用了 HTTP 协议来握手, 与 HTTP 使用相同的默认端口, 这一切都是为了兼容现有的 HTTP 组件或代理, 但 WebSocket 与 HTTP 是相互独立的协议, 二者并不存在上下的层级关系, WebSocket 的正式协议文档为 [RFC 6455], 本文全面讨论 WebSocket 协议的设计与工作原理
WebSocket 协议主要为了解决基于 HTTP/1.x 的 Web 应用无法实现服务端向客户端主动推送的问题, 为了兼容现有的设施, WebSocket 协议使用与 HTTP 协议相同的端口, 并使用 HTTP Upgrade 机制来进行 WebSocket 握手, 当握手完成之后, 通信双方便可以按照 WebSocket 协议的方式进行交互
WebSocket 使用 TCP 作为传输层协议, 与 HTTP 类似, WebSocket 也支持在 TCP 上层引入 TLS 层, 以建立加密数据传输通道, 即 WebSocket over TLS, WebSocket 的 URI 与 HTTP URI 的结构类似, 对于使用 80 端口的 WebSocket over TCP, 其 URI 的一般形式为 ws://host:port/path/query 对于使用 443 端口的 WebSocket over TLS, 其 URI 的一般形式为 wss://host:port/path/query
在 WebSocket 协议中, 帧 (frame) 是通信双方数据传输的基本单元, 与其它网络协议相同, frame 由 Header 和 Payload 两部分构成, frame 有多种类型, frame 的类型由其头部的 Opcode 字段 (将在下面讨论) 来指示, WebSocket 的 frame 可以分为两类, 一类是用于传输控制信息的 frame (如通知对方关闭 WebSocket 连接), 一类是用于传输应用数据的 frame, 使用 WebSocket 协议通信的双方都需要首先进行握手, 只有当握手成功之后才开始使用 frame 传输数据
当客户端想要使用 WebSocket 协议与服务端进行通信时, 首先需要确定服务端是否支持 WebSocket 协议, 因此 WebSocket 协议的第一步是进行握手, WebSocket 握手采用 HTTP Upgrade 机制, 客户端可以发送如下所示的结构发起握手 (请注意 WebSocket 握手只允许使用 HTTP GET 方法):
GET /chat HTTP/1.1Host: server.example.comUpgrade: websocketConnection: UpgradeSec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==Origin: http://example.comSec-WebSocket-Protocol: chat, superchatSec-WebSocket-Version: 13
在 HTTP Header 中设置 Upgrade 字段, 其字段值为 websocket, 并在 Connection 字段指示 Upgrade, 服务端若支持 WebSocket 协议, 并同意握手, 可以返回如下所示的结构:
HTTP/1.1 101 Switching ProtocolsUpgrade: websocketConnection: UpgradeSec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=Sec-WebSocket-Protocol: chatSec-WebSocket-Version: 13
我们来详细讨论 WebSocket 的握手细节, 客户端发起握手时除了设置 Upgrade 之外, 还需要设置其它的 Header 字段
服务端若支持 WebSocket 协议, 并同意与客户端握手, 则应返回 101 的 HTTP 状态码, 表示同意协议升级, 同时应设置 Upgrade 字段并将值设置为 websocket, 并将 Connection 字段的值设置为 Upgrade, 这些都是与标准 HTTP Upgrade 机制完全相同的, 除了这些以外, 服务端还应设置与 WebSocket 相关的头部字段:
服务端若接收客户端的握手, 便按上述所表述的规则向客户端返回握手响应, 客户端对服务端返回的握手响应做校验, 若校验成功, 则 WebSocket 握手成功, 之后双方就可以开始进行双向的数据传输。客户端在发起握手后必须处于阻塞状态, 换句话说, 客户端必须等待服务端发回响应之后才允许开始数据传递, 客户端对服务端的握手响应的校验机制如下:
若客户端校验服务端的握手响应通过, 则 WebSocket 握手阶段完成, 接下来双方就可以进行 WebSocket 的双向数据传输了
WebSocket 以 frame 为单位传输数据, frame 是客户端和服务端数据传输的最小单元, 当一条消息过长时, 通信方可以将该消息拆分成多个 frame 发送, 接收方收到以后重新拼接、解码从而还原出完整的消息, 在 WebSocket 中, frame 有多种类型, frame 的类型由 frame 头部的 Opcode 字段指示, WebSocket frame 的结构如下所示:
该结构的字段语义如下:
RFC 6455 规定所有由客户端发往服务端的 WebSocket frame 的 Payload 部分都必须使用掩码覆盖, 这是为了避免代理缓存污染攻击 (更多细节见 RFC 6455 Section 10.3), 若服务端接收到没有使用掩码覆盖的 frame, 服务端应立即终止 WebSocket 连接, 掩码覆盖只针对 frame 的 Payload 部分, 掩码覆盖不会改变 Payload 的长度, 掩码覆盖的算法如下:
我们以 original-octet-i 表示未覆盖前的 Payload 的第 i 个字节, 以 transformed-octet-i 表示覆盖后的 Payload 的第 i 个字节, 以 masking-key-octet-j 表示 Masking-Key 的第 j 个字节, 那么上述算法的操作可以用如下两个式子表示:
j = i % 4transformed-octet-i = original-octet-i XOR masking-key-octet-j
服务端收到客户端的 frame 后, 首先检查 Mask 标志位是否为 1, 若不是则应立即终止握手, 然后根据 Masking-Key 字段的值重复上述操作便可以得到原先的 Payload 数据
当要发送的一条消息过长或者消息是实时产生并不能预测具体的长度时, 客户端可将消息进行分片, 构成一个 frame 后便可以发往服务端, 分片的另一个考虑是为了复用底层的 TCP 连接, 当客户端有多份相互独立的数据需要发送时, 消息分片可以实现在一条 TCP 链路上的复用, 多份数据可以并发地发往服务端, 如果读者了解过 HTTP/2 [RFC 7540] 便可以知道这里也是 HTTP/2 的做法, 但 RFC 6455 并没有具体指出如何实现 WebSocket 分片消息的并发传送, 在 HTTP/2 中, 并发传送是通过 Stream 来关联的, 根据 Stream Identifier, 接收方可以知晓哪些消息是逻辑上连续的消息, 在 WebSocket 中, 若不引进额外机制, 则并发传送时服务端无法区分哪些消息段在逻辑上是同属于一个消息的, 这里需要通过额外的 WebSocket 扩展机制实现, 此处不深入讨论, 下面所讨论的分片场景都是在不并发传送的前提假设下的
消息分片主要利用 frame Header 的 FIN 和 Opcode 字段来实现, 对于未分片的消息, 一个 frame 便承载了完整的消息, 此时它没有后续的 frame, 因此其 FIN 字段为 1, Opcode 根据该消息是文本消息还是二进制消息分别选择 0x1 或 0x2, 而对于分片的消息, 我们以文本消息为例, 文本消息的 Opcode 为 0x1, 若不进行分片, 则 frame 的 FIN 字段为 1, 同时 Opcode 字段为 0x1, 若进行分片, 则第一个分片的 frame 的 FIN 字段为 0, Opcode 为 0x1, 但从第二个直到倒数第二个分片, 其 FIN 字段为 0, 并且 Opcode 字段的值为 0x0 (0x0 代表这是一个 continuation frame), 对于最后一个分片的消息, 其 FIN 字段为 1, 并且 Opcode 字段的值为 0x1, 对于分片消息, 发送端必须按序发送, 因此 TCP 保证交付给上层的数据是有序的, 因此接收端也将按发送端发送的顺序收到消息, 它可以按序拼接分片得到完整的消息
控制类的 frame (如 Ping frame, Pong frame, 将下面讨论) 可以被允许插入在分片消息的发送过程中, 如果不允许, 则对于过长的消息, 其分片数很多, 发送耗时比较长, 控制类的消息需要一直等待消息发送完成而不能及时传递给对方, 将会产生一系列问题 (将在下面讨论)
控制类 frame 主要用来传输一些连接控制信息 (如 Close frame 用来关闭 WebSocket 连接), RFC 6455 总共定义了三种控制类 frame, 分别是 Close frame, Ping frame, Pong frame
RFC 6455 将连接关闭表述为 Closing Handshake, 我更倾向于将其表述为挥手, 以便与建立连接的握手区分开, WebSocket 的连接关闭分为 CLOSING 和 CLOSED 两个阶段, 当发送完 Close frame 或接收到对方发来的 Close frame 后, WebSocket 连接便从 OPEN 状态转变为 CLOSING 状态, 此时可以称挥手已启动, 通信方接收到 Close frame 后应立即向对方发回 Close frame, 并关闭底层 TCP 连接, 此时 WebSocket 连接处于 CLOSED 状态
与 HTTP 不同, WebSocket 在进行数据传输的时候正常情况下都以 frame 为传输单元, 不像 HTTP 协议那样每一次交互都有 Status Code, WebSocket 本身也有状态码, 但只用在 Close frame 中, 用于指示连接关闭的原因 (可能是正常关闭也可能是因为发生了错误)
RFC 6455 定义了多个 WebSocket 状态码:
...