当前位置: 首页 > news >正文

05 通信协议设计时的注意事项

通过前面的章节的讨论,相信读者应该对协议设计有一定的了解了。本节我们来讨论一下协议设计时的一些注意事项。

字节对齐

留心的读者一定注意到,前面讨论的协议示例中:

#pragma pack(push, 1) struct userinfo { //版本号 short version; //命令号 int32_t cmd; //用户性别 char gender; //用户昵称 char name[8]; //用户年龄 int32_t age; }; #pragma pack(pop)

有一组成对的#pragma XX指令,其中 #pragma pack(push, n),是告诉编译器接下来的所有结构体(这里就是 userinfo 协议)的每一个字段按 n 个字节对齐,这里 n = 1,按一个字节对齐,即去除任何 padding 字节。这样做的目的是为了内存更加紧凑,节省存储空间。

我们不再需要这个对齐功能后,应该使用#pragma pack(pop)让编译器恢复默认的对齐方式。

注意:#pragma pack(push, n) 与 #pragma pack(pop) 一定要成对使用,如果你漏掉其中任何一个,编译出来的代码可能会出现很多奇怪的运行结果。
显式指定整型字段的长度

对于一个 int 型字段,在作为协议传输时,我们应该显式地指定该类型的长度,也就是说,你应该使用 int32_t、int64_t 这样的类型来代替 int、long。之所以这么做的原因是,对于不同字长的机器,对于默认的 int 和 long 的长度可能不一样,例如 long 型,在 32 位操作系统上其长度是 4 个字节,而在 64 位机器上其长度是 8 个字节。如果不显式指定这种整形的长度,可能因为不同机器字长不同,导致协议解析出错或者产生错误的结果。

涉及到浮点数要考虑精度问题,建议放大成整数或者使用字符串去传输

由于计算机表示浮点数存在精度取舍不准确的问题,例如对于 1.000000,有的计算机可能会得到 0.999999,在某些应用中,如果这个浮点数的业务单位比较大(如表示金额,单位为亿),就会造成很大的影响。因此为了避免不同的机器解析得到不同的结果,建议在网络传输时将浮点数值放大相应的倍数变成整数或者转换为字符串来进行传输。

大小端编码问题

在第四章我们已经详细地介绍大小端的问题(即主机字节序和网络字节序),在设计协议格式时,如果协议中存在整型字段,建议使用同一个字节序。通常的做法是在进行网络传输时将所有的整型转换为网络字节序(大段编码,Big Endian),避免不同的机器因为大小端问题解析得到不同的整型值。

当然,不一定非要转换为网络字节序,如果明确的知道通信的双方使用的是相同的字节序,则也可以不转换。

协议的分类

根据协议的内容是否是文本格式(即人为可读格式),我们将协议分为文本协议和二进制协议,像 http 协议的包头部分和 FTP 协议等都是典型的文本协议的例子。

协议与自动升级功能

对于一个商业的产品,发布出去的客户端一般通过客户端的自动升级功能去获得更新(IOS App 除外,苹果公司要求所有的 App 必须在其 App Store 上更新新版本,禁止热更新)。在客户端与服务器通信的所有协议格式中,自动升级协议是最重要的一个,无论版本如何迭代,一定要保证自动升级协议的新旧兼容,这样做有如下原因:

  • 如果新的服务器不能兼容旧客户端中的自动升级协议,那么旧的客户端用户将无法升级成新的版本了,这样的产品相当于把自己给“阉割”了。对于不少产品,不通过自动升级而让众多用户去官网下载新的版本是一件很难做到的事情,这种决策可能会导致大量用户流失;
  • 退一步讲,对于一些测试不完善,或者处于快速迭代中的产品,只要保证自动升级功能正常,旧版本任何 bug 和瑕疵都可以通过升级新版本解决。这对于一些想投放市场试水,但又可能设计不充分的产品尤其重要。

顺便提一下,一般自动升级功能是根据当前版本的版本号与服务器端新版本的版本号进行比较,如果二者之间存在一个大版本号的差别(如1.0.0 与 2.0.0),即有重大功能更新,则应该强制客户端更新下载最新版本;如果只是一个小版本号的更新(如 1.0.0 与 1.1.0),则可以让用户选择是否更新。当然,如果是新版本修正了前一个版本中严重影响使用的 bug,也应答强制用户更新。

http://www.jsqmd.com/news/1090520/

相关文章:

  • 防火墙双机热备实战:从组网规划到状态切换的完整配置解析
  • MSPM0Lxx低功耗与中断协同设计:从原理到实战优化
  • Three.js 简单3d拓扑图教程
  • 芝麻粒TK版:模块化架构下的蚂蚁森林自动化终极方案
  • Win11Debloat深度解析:Windows系统定制化优化技术方案
  • 如何轻松实现AI智能分层:Layerdivider完整使用教程
  • D3keyHelper终极指南:一键解放双手的暗黑3智能助手
  • Illustrator脚本终极指南:如何用自动化工具提升90%设计效率
  • 无硬件学LVGL:基于Web模拟器+MiroPython速通GUI开发—布局与空间管理篇
  • PCL2启动器性能优化终极指南:彻底解决Minecraft卡顿问题
  • 服务发现——让服务“自动寻址“
  • HS2-HF Patch终极指南:如何通过模块化架构实现Honey Select 2的全面增强
  • 如何用MeEdu快速搭建专属在线网校系统:完整指南
  • 7个技巧让你在Blender中实现机械级精度:CAD_Sketcher参数化设计终极指南
  • 如何5分钟实现STL到STEP格式转换:从网格到实体的专业蜕变指南
  • Blender插件管理终极指南:2000+插件一键掌控的完整解决方案
  • 3个步骤彻底告别XCOM 2模组管理噩梦:AML启动器完整解决方案
  • 终极指南:YgoMaster局域网PvP对战完整教程 - 轻松实现好友联机决斗
  • AFE5805评估板实战指南:从硬件解析到性能测试
  • 3D打印新手必看:BambuStudio终极指南,轻松掌握智能切片与远程控制
  • 082、Flask 进阶:蓝图、上下文栈、g 对象与大规模项目组织
  • 深入解析MSPM0工厂预编程区域:从内存映射寄存器到芯片校准数据实战
  • 大模型记忆容量的物理定律:3.6比特每参数量化原理
  • 从一次端口监听冲突的解决,深入理解127.0.0.1、0.0.0.0与网卡IP的绑定机制
  • Python QQ机器人架构解密:多线程事件驱动模型的技术实现
  • 电影院管理系统(可商用)
  • 从理论到实践:基于同态加密的隐私信息检索方案深度解析
  • 暗黑3技能连点器终极指南:解放双手的智能战斗助手
  • MySQL主从复制报错:UUID冲突导致I/O线程停止的排查与修复
  • 大模型MoE稀疏激活原理与实操:从1.8万亿参数到2%激活的工程真相