写点什么

Rust 元宇宙 10 —— 接入和协议

作者:Miracle
  • 2021 年 12 月 01 日
  • 本文字数:1575 字

    阅读完需:约 5 分钟

Rust 元宇宙 10 —— 接入和协议

做完了所有的准备工作之后,我们需要迈出重要的一步,创建一个真实的玩家,通过客户端进入元宇宙,并看到视野内的一切,包括自己和其他角色(玩家或者宠物/NPC)。

客户端采用什么语言开发是无所谓的,我们只要规定客户端接入元宇宙所使用的的协议和数据格式就行了,比如说 C/C++,rust,Java,JS 都可以,使用 Unity ,Unreal,或者 Cocos 引擎也无所谓。

我们首选需要选择的,是客户端和元宇宙服务器之间使用的通讯协议,大概有下面几种选择:

  • UDP

  • 建立在 UDP 上的可靠传输协议(UDX UDT 等)

  • QUIC(WebRTC 使用的协议)

  • TCP

  • websocket

  • http/https


其中 UDP 是最基础的,不可靠传输协议,游戏内部的语音可以考虑使用这个协议

UDX 和 UDT 等非标准的 可靠 UDP 传输协议基本上都是自己实现了 TCP 的重传算法,以期望在 5G 时代有更好的表现,考虑到元宇宙的开放性和普适性,我们暂时不考虑。

QUIC 是 google 实现的 WebRTC 的底层协议,也是一种 自己实现的可靠 UDP 协议,是下一代 http2 协议的基础,可以作为我们的备选。

TCP 是最传统,最古老的 C/S 协议了,大部分客户端游戏都是采用这个协议的,当然也可以作为我们的备选,最大的问题是,网页的开发语言 Javascrit 没有办法直接支持这个协议。

websocket 是来源于网页,但是客户端也可以使用的协议,建立在 TCP 之上,但是增加了一些加密握手等特性,其弱点就是接入的开销会增加(TLS 握手会消耗 CPU 和内存)。

http/https 是最广泛使用的网站协议,大量的页游,手游都使用了这个协议,这个协议最大的问题就是不支持长链接,客户端很难获得服务器的通知,而且基于文本的 http 协议本身的开销巨大。


综上所述,如果不考虑网页接入,可以选择的有 QUIC WebSocket 和 TCP。

如果考虑支持网页接入,可供选择的只剩下 Websocket 了。


Rust 对 Websocket 的支持还是比较完善的,Actix-web 和 Axum 都对 Websocket 提供了支持

下面是 Axum 提供 Websocket 服务的代码

let app = Router::new().route("/ws", get(ws_handler));let addr = SocketAddr::from(([127, 0, 0, 1], 3000));    tokio::spawn(async move {    axum::Server::bind(&addr).serve(app.into_make_service())});
复制代码


解决了底层传输协议问题之后,我们又面临一个选择,我们怎么规定 客户端和云宇宙服务器之间的消息格式?

这里又有两种基本的方法:

基于 Schema 和不基于 Schema 的方法


基于 Schema 的方法,就是 使用 IDL 来独立描述接口,并生成 接口的代码,比如说

google 的 Protocolbuffer。一个典型的消息定义如下

syntax = "proto3”;//指定proto版本,默认为proto2,而且改行必须是第一行
//message包含多个种类的fieldsmessage SearchRequest { string query = 1; int32 page_number = 2; int32 result_per_page = 3;}
复制代码

Apache 的 thrift

等等

优点是能够生成高性能的接口代码,缺点是不够灵活,任何接口的调整都需要重新编译代码


不基于 Schema 的方法,就是规定消息的二进制规范,但是不跟接口挂钩起来,应用层自己根据翻译出来的消息内容,决定如何处理。

比如说 使用 MessagePack 对消息进行序列化处理。


这两种方案各有优劣,对于大型项目来说,基于 Schema 的方案更加严谨和方便协调,对于小型个人项目来说,不基于 Schema 更灵活,实现更快。


综上所述,我们的元宇宙采用 不基于 Schema 的 messagepack 方案。


我们在 share::msg 里面定义 WorldRequest 如下

#[derive(Debug, Serialize, Deserialize, Clone)]pub enum WorldRequest {    Exit,    Enter(u32, Position),    Leave(u32),    Move(u32, Position, f32),    GetRoles(i32, i32),     }
复制代码


使用 messagepack 我们可以方便的序列化,代码如下

let mut buf = Vec::new();let msg = WorldRequest::Move(101, Position{x: 10.0, y: 20.0}, 20.0);msg.serialize(&mut Serializer::new(&mut buf)).unwrap();println!("{:?}", buf);
复制代码


序列化的结果输出如下


用户头像

Miracle

关注

三十年资深码农 2019.10.25 加入

还未添加个人简介

评论

发布
暂无评论
Rust 元宇宙 10 —— 接入和协议