百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术分析 > 正文

WebSocket与Protobuf在现代网络通信中的应用实践

liebian365 2025-02-26 12:41 2 浏览 0 评论

在现代网络通信中,WebSocket和Protobuf已成为构建高效、跨平台通信系统的关键技术。本文将详细介绍如何使用这两种技术来实现一个稳定且高效的网络通信系统。

引言

随着移动设备和Web应用的普及,开发者需要构建能够跨多个平台工作的网络通信系统。WebSocket提供了一种在单个连接上进行全双工通信的方式,而Protobuf(Protocol Buffers)则是一种轻便高效的结构化数据存储格式,广泛应用于数据序列化。

为什么选择WebSocket和Protobuf

WebSocket的优势

WebSocket协议提供了一种在客户端和服务器之间建立持久连接的方法。这种连接允许服务器主动向客户端发送消息,非常适合需要实时数据传输的应用场景。

Protobuf的优势

Protobuf是一种由Google开发的二进制数据格式,它具有以下优点:

  • 高效性:Protobuf的二进制格式比JSON等文本格式更加紧凑,减少了网络传输的负载。
  • 易于使用:Protobuf提供了丰富的库支持,可以轻松地在不同语言之间进行数据交换。
  • 强类型系统:Protobuf支持强类型,有助于在编译时捕捉错误。

系统设计

通信模型

在我们的系统中,通信模型基于请求-响应模式。客户端发送请求,服务器根据请求返回相应的数据。

消息定义

每个通信消息都包含cmdscmd字段,用于标识消息类型。cmd代表模块号,scmd代表模块下的具体子消息号。

例如,系统模块的消息定义如下:

message C_HEART_BEAT {
  int32 cmd = 1 [default = 0];
  int32 scmd = 2 [default = 2];
}

message S_HEART_BEAT {
  int32 cmd = 1 [default = 0];
  int32 scmd = 2 [default = 3];
  int64 serverTime = 3; // 服务器时间
}


代码生成

使用protobuf-cil工具,我们可以从Protobuf定义文件生成相应的代码。这里,我们只生成消息的编码(encode)和解码(decode)方法。

/*eslint-disable block-scoped-var, id-length, no-control-regex, no-magic-numbers, no-prototype-builtins, no-redeclare, no-shadow, no-var, sort-vars*/
"use strict";
if (typeof (window) == 'undefined') {
  var $protobuf = require("protobufjs/minimal");
} else {
  var $protobuf = protobuf;
}
var $Reader = $protobuf.Reader, $Writer = $protobuf.Writer, $util = $protobuf.util;
var $root = $protobuf.roots["default"] || ($protobuf.roots["default"] = {});
$root.SystemPto = (function() {
  var SystemPto = {};
  SystemPto.C_HEART_BEAT = (function() {
    function C_HEART_BEAT(p) {
      if (p)
        for (var ks = Object.keys(p), i = 0; i < ks.length; ++i)
          if (p[ks[i]] != null)
            this[ks[i]] = p[ks[i]];
    }
    C_HEART_BEAT.prototype.cmd = 0;
    C_HEART_BEAT.prototype.scmd = 2;
    C_HEART_BEAT.encode = function encode(m, w) {
      if (!w)
        w = $Writer.create();
      if (m.cmd != null && Object.hasOwnProperty.call(m, "cmd"))
        w.uint32(8).int32(m.cmd);
      if (m.scmd != null && Object.hasOwnProperty.call(m, "scmd"))
        w.uint32(16).int32(m.scmd);
      return w;
    };
    C_HEART_BEAT.decode = function decode(r, l) {
      if (!(r instanceof $Reader))
        r = $Reader.create(r);
      var c = l === undefined ? r.len : r.pos + l, m = new $root.SystemPto.C_HEART_BEAT();
      while (r.pos < c) {
        var t = r.uint32();
        switch (t >>> 3) {
          case 1:
            m.cmd = r.int32();
            break;
          case 2:
            m.scmd = r.int32();
            break;
          default:
            r.skipType(t & 7);
            break;
        }
      }
      return m;
    };
    return C_HEART_BEAT;
  })();
  SystemPto.S_HEART_BEAT = (function() {
    function S_HEART_BEAT(p) {
      if (p)
        for (var ks = Object.keys(p), i = 0; i < ks.length; ++i)
          if (p[ks[i]] != null)
            this[ks[i]] = p[ks[i]];
    }
    S_HEART_BEAT.prototype.cmd = 0;
    S_HEART_BEAT.prototype.scmd = 3;
    S_HEART_BEAT.prototype.serverTime = $util.Long ? $util.Long.fromBits(0,0,false) : 0;
    S_HEART_BEAT.encode = function encode(m, w) {
      if (!w)
        w = $Writer.create();
      if (m.cmd != null && Object.hasOwnProperty.call(m, "cmd"))
        w.uint32(8).int32(m.cmd);
      if (m.scmd != null && Object.hasOwnProperty.call(m, "scmd"))
        w.uint32(16).int32(m.scmd);
      if (m.serverTime != null && Object.hasOwnProperty.call(m, "serverTime"))
        w.uint32(24).int64(m.serverTime);
      return w;
    };
    S_HEART_BEAT.decode = function decode(r, l) {
      if (!(r instanceof $Reader))
        r = $Reader.create(r);
      var c = l === undefined ? r.len : r.pos + l, m = new $root.SystemPto.S_HEART_BEAT();
      while (r.pos < c) {
        var t = r.uint32();
        switch (t >>> 3) {
          case 1:
            m.cmd = r.int32();
            break;
          case 2:
            m.scmd = r.int32();
            break;
          case 3:
            m.serverTime = r.int64();
            break;
          default:
            r.skipType(t & 7);
            break;
        }
      }
      return m;
    };
    return S_HEART_BEAT;
  })();
  return SystemPto;
})();

消息发送与接收

发送消息

当客户端需要发送一个心跳包给服务器时,可以创建一个心跳消息对象,并使用封装的socket发送。

let heart = new SystemPto.C_HEART_BEAT();
socket.send(ProtoBufEncoder.encode(heart));

消息编码

ProtoBufEncoder统一处理消息的编码和解码。客户端发送的消息以C_开头,服务器响应的消息以S_开头。使用Map数据结构存储消息类。

public static encode(message: IGameMessage): Buffer {
  if (!message) {
    return;
  }
  let index = ProtoBufEncoder.getComposeCmdId(message.cmd, message.scmd);
  let messageClass = ProtoBufEncoder.protoBufClass.get(index);
  ProtoBufEncoder.protoBufWriter.reset();
  const writer = messageClass.encode(message, ProtoBufEncoder.protoBufWriter);
  return writer.finishWithSysCmd(message.cmd, message.scmd);
}

消息解码

服务器返回的buffer中包含消息号,根据消息号获取对应的消息类,并解码获取消息对象数据。

public static decode(buffer: Buffer, offset: number): IGameMessage {
  if (buffer.length < 8) {
    console.error("protobuf decode err! buffer长度小于8");
    return;
  }
  let dataView = new DataView(buffer);
  const cmd = dataView.getInt32(offset);
  const scmd = dataView.getInt32(offset + 4);
  if (Buffer.isBuffer(buffer) === false) {
    buffer = Buffer.from(buffer);
  }
  const messageIndex = ProtoBufEncoder.getComposeCmdId(cmd, scmd);
  const messageClass = ProtoBufEncoder.protoBufClass.get(messageIndex);
  if (!messageClass) {
    console.error("无法获取协议:", cmd, scmd);
    return null;
  }
  ProtoBufEncoder.protoBufReader.buf = buffer;
  ProtoBufEncoder.protoBufReader.pos = offset + 8;
  ProtoBufEncoder.protoBufReader.len = buffer.length;
  let result = null;
  try {
    result = messageClass.decode(ProtoBufEncoder.protoBufReader);
  } catch (e) {
    console.error("解码消息失败 :", messageClass.name);
  }
  return result;
}

扩展功能

心跳机制

为了保持连接的活跃性,我们实现了一个心跳机制。客户端定期发送心跳包,服务器收到后返回心跳响应。

// 客户端发送心跳
let heartBeat = new SystemPto.C_HEART_BEAT();
heartBeat.cmd = 0;
heartBeat.scmd = 2;
socket.send(ProtoBufEncoder 

Kimi: .encode(heartBeat));

// 服务端接收心跳
socket.on('message', (message) => {
  let decodedMessage = ProtoBufEncoder.decode(message);
  if (decodedMessage && decodedMessage.cmd === 0 && decodedMessage.scmd === 2) {
    let response = new SystemPto.S_HEART_BEAT();
    response.cmd = 0;
    response.scmd = 3;
    response.serverTime = Date.now();
    socket.send(ProtoBufEncoder.encode(response));
  }
});

安全性考虑

在实际应用中,通信的安全性也是非常重要的。我们可以通过以下方式增强系统的安全性:

  1. 使用WSS(WebSocket Secure):使用WSS协议可以保证数据在传输过程中的加密。
  2. 消息签名:在消息中加入签名,服务器在接收到消息后验证签名,确保消息的完整性和来源的合法性。

性能优化

为了提高通信效率,我们还可以进行以下优化:

  1. 压缩数据:在发送数据前对数据进行压缩,减少传输的数据量。
  2. 连接复用:尽量复用现有的WebSocket连接,避免频繁地建立和关闭连接。

结论

WebSocket和Protobuf的结合为现代网络通信提供了一个高效、跨平台的解决方案。通过合理的设计和优化,我们可以构建出稳定且高效的通信系统。


相关推荐

20 个 2020 年软件开发趋势预测

企业上云已成不可逆的趋势,全面云计算时代宣告来临,微服务已成软件架构主流,Kubernetes将会变得更酷,2020年还有哪些技术趋势值得观察?一起来看!1.基础设施:条条道路通云端对于云厂商来...

目录发布!安徽这些紧缺人才急需

《安徽省5G产业急需紧缺人才目录(2020-2025)》(以下简称目录)近日正式发布。本次调研调查了216家代表企业、6家头部企业,获取了426份有效问卷,分析安徽省5G产业紧缺人才需求现状,其中产品...

AI树莓派——构建树莓派大脑(NCNN环境搭建)

前言镜像已经做好了,传到百度网盘中了(请大家及时保存,不定期删除!)...

把远程进程通讯grpc引入到Spring boot maven项目中

1、参考链接:gRPC官网:https://grpc.io/HTTP2:https://http2.github.io/...

面向数据的架构

在软件架构中,有一种模式虽鲜为人知的,但值得引起更多的关注。面向数据的架构(Data-OrientedArchitecture)由RajiveJoshi在RTI的2007年白皮书中首次提出,...

Go语言11岁了,网友:他喵的,终于确定出「泛型」了

金磊发自凹非寺量子位报道|公众号QbitAI比Python更快,比Java更简洁,还有C++没有的GC...

深度剖析数据库国产化迁移之路

作者|吴夏,腾讯云TDSQL高级工程师责编|唐小引头图|CSDN下载自东方IC出品|CSDN(ID:CSDNnews)随着国家有关部门近年来陆续出台相关政策指导文件,推动探索安...

一文掌握物体检测库TensorFlow 2.x Object Detection安装

...

团队协作-代码格式化工具clang-format

环境:clang-format:10.0.0前言统一的代码规范对于整个团队来说十分重要,通过git/svn在提交前进行统一的ClangFormat格式化,可以有效避免由于人工操作带来的代码格式问题。...

嵌入式大杂烩周记 第 9 期:nanopb

大家好,我是杂烩君。...

开源鸿蒙 OpenHarmony 3.1 Beta 版本发布:系统基础能力增强

IT之家1月2日消息,OpenAtom社区已于12月31日发布了OpenHarmony-v3.1-Beta版本。版本概述当前版本在OpenHarmony3.0LTS的基础...

零基础物联网开发,踩坑无数,得到这份宝典 | 原力计划

作者|Haor.L责编|王晓曼出品|CSDN博客笔者最近参加了校内的一场物联网开发竞赛,从零开始,踩坑无数,感觉很多时候事情都不像预料的一样发展,离开了美好的IDE,太多事情要在板子上一步...

gRPC:Google开源的基于HTTP/2和ProtoBuf的通用RPC框架

gRPC是一个高性能、通用的开源RPC框架,其由Google主要面向移动应用开发并基于HTTP/2协议标准而设计,基于ProtoBuf(ProtocolBuffers)序列化协议开发,且支持众多开发...

搜狗开源srpc:自研高性能通用RPC框架

今年7月底,搜狗公司开源了内部的工业级C++服务器引擎Workflow,一路收获业内许多认可和关注。9月15日,作为Workflow最重要的生态项目——srpc,一个基于其打造的轻量级RPC框架,也在...

WebSocket与Protobuf在现代网络通信中的应用实践

在现代网络通信中,WebSocket和Protobuf已成为构建高效、跨平台通信系统的关键技术。本文将详细介绍如何使用这两种技术来实现一个稳定且高效的网络通信系统。...

取消回复欢迎 发表评论: