网络游戏中通信消息的组织
不同进程间通信都需要一些标识来定义本次通讯的意义及数据格式,而这些标识常常被称为消息,接下来讨论如何组织这些标识及与之对应的数据格式。
传统的是的使用常量(枚举或宏)来定义消息编号,使用结构体来定义各消息的数据格式。在解析时switch-case常量来使用相应的结构体解析数据并进行处理,这种原生的方式会带来各种问题:使switch-case处理函数会变得相当长导而致难以维护;消息编号和其数据格式结构体映射混乱;不利于新增消息的分流等等。而后又演变出分段、消息结构体继承等思想以缓解这种情况。今天我探寻的是延续这些思想并进行扩展,使消息的定义、使用更方便。
在解决"大"问题的时候,使用分治法往往都能收到很不错的效果,面对数以千记的消息,果断地将它们分段,分段上再应用点小策略:从逻辑上来看各分段形成一颗树形结构,该树的每个节点要么是具体的消息,要么是一个分段。每个节点都有一个值和类型(核心思想),其中节点值在兄弟间唯一的,节点类型是继承自父节点的类型。仅有分段才能具有子节点。如图:
_root_
/ \
protocola _segmentA_
/ | \
_segmentAA_ _segmentBB_ protocolaa
... ...
[root为消息的根节点,segment为消息段,protocol为消息编号]
消息值和消息类型都在其父节点的域内,如访问消息protocola为_root_::protocola。这样做主要是防止命名冲突,如组队系统中有同意邀请消息,而副本系统也有同意邀请消息。
Ok,话了这么多,直接上码:
/
typedef unsigned char PROT_TYPE; // 消息编号类型
// 根消息段。
namespace msg_ROOT_sg
{
struct ROOT_dt { PROT_TYPE No() { return _no; } protected: PROT_TYPE _no; };
typedef ROOT_dt MyDT;
}
// 开始定义一个段
// belong所属段名 segment当前段名
#define BEGIN_DEFINE_SEGMENT(belong , segment) \
namespace msg_##segment##_sg \
{ \
typedef msg_##belong##_sg::segment##_dt MyDT;
// 结束定义一个段
#define END_DEFINE_SEGMENT(segment) }
// 在段的定义中声明一个段
// segment段名 value段的值(该值必须是父亲直接孩子中唯一的)
#define DECLEAR_SEGMENT(segment , value) \
static PROT_TYPE const segment = value ; \
struct segment##_dt: public MyDT \
{ \
segment##_dt() { MyDT::_no = segment; } \
PROT_TYPE No () { return _no ; } \
protected: PROT_TYPE _no; \
};
// 在段的定义中声明一个消息
// protocol消息名 value消息的值(该值必须是父亲直接孩子中唯一的)
#define DECLEAR_PROTOCOL(protocol , value) static PROT_TYPE const protocol = value;
// 段的定义外开始定义一个消息
// belong所属段 protocol消息名
#define BEGIN_DEFINE_PROTOCOL(belong , protocol) \
namespace msg_##belong##_sg \
{ \
struct protocol##_dt : public MyDT \
{ \
protocol##_dt() { MyDT::_no = protocol; }
// 段的定义外结束定义一个消息
// protocol消息名
#define END_DEFINE_PROTOCOL(protocol ) }; }
// 在段的定义中定义一个消息
// protocol消息名 value消息的值(该值必须是父亲直接孩子中唯一的)
#define DEFINE_PROTOCOL(protocol , value) \
static PROT_TYPE const protocol = value ; \
struct protocol##_dt : public MyDT \
{ protocol##_dt() { MyDT::_no = protocol; } };
BEGIN_DEFINE_SEGMENT(ROOT, ROOT) // 根节点的所属段依旧是根。而且段名必须是ROOT
DECLEAR_SEGMENT(CHAT, 1) // 声明聊天段
DEFINE_PROTOCOL(GC_HEART_DETECT, 100) // 定义GC_HEART_DETECT消息,该消息不附带额外的数据。
END_DEFINE_SEGMENT(ROOT)
// 定义聊天消息段
BEGIN_DEFINE_SEGMENT(ROOT, CHAT)
DECLEAR_PROTOCOL(CS_GMCHAT, 10) // 声明GM聊天消息
END_DEFINE_SEGMENT(CHAT)
// 定义GM聊天消息
BEGIN_DEFINE_PROTOCOL(CHAT, CS_GMCHAT)
char szContext[128]; // 内容
unsigned char byType; // 类型
END_DEFINE_PROTOCOL(CS_GMCHAT)
/
#include <iostream>
#include<windows.h>
void Recv(msg_ROOT_sg::ROOT_dt *pData); // 模拟全局消息处理函数
void MsgProc_Chat(msg_ROOT_sg::CHAT_dt *pData); // 处理聊天系统的消息函数(模拟具体的消息段)
int _tmain(int argc, _TCHAR* argv[])
{
msg_CHAT_sg::CS_GMCHAT_dt data;
strcpy(data.szContext, "this a test");
data.byType = 100;
void *pData = &data; // 模拟网络传输后
Recv((msg_ROOT_sg::ROOT_dt *)pData);
msg_ROOT_sg::GC_HEART_DETECT_dt detect;
pData = &detect; // 模拟网络传输后
Recv((msg_ROOT_sg::ROOT_dt *)pData);
system("pause");
return 0;
}
void Recv(msg_ROOT_sg::ROOT_dt *pData)
{
switch (pData->No())
{
case msg_ROOT_sg::CHAT:
MsgProc_Chat((msg_ROOT_sg::CHAT_dt*)pData);
break;
case msg_ROOT_sg::GC_HEART_DETECT:
std::cout << "Msg: GC_HEART_DETECT" << std::endl;
break;
default:break;
}
}
// 处理聊天系统的消息函数
void MsgProc_Chat(msg_ROOT_sg::CHAT_dt *pData)
{
switch (pData->No())
{
case msg_CHAT_sg::CS_GMCHAT:
{
msg_CHAT_sg::CS_GMCHAT_dt *pChat = (msg_CHAT_sg::CS_GMCHAT_dt*)pData;
std::cout << "Msg:CS_GMCHAT ";
std::cout << "Chat:" << pChat->szContext << " Type:" << pChat->byType << std::endl;
}
break;
}
}
PS: 几个项目一步一步总结出来的,现也运用在项目中~但未必完美,望不吝赐教!
排版貌似乱了,我是直接从偶的demo中拷贝出来的,懒得排了....感兴趣自己拷到.h/.cpp中去看。
网络游戏中通信消息的组织相关推荐
- DELPHI 中 Window 消息大全使用详解
Window 消息大全使用详解 导读: Delphi是Borland公司的一种面向对象的可视化软件开发工具. Delphi集中了Visual C++和Visual Basic两者的优点:容易上手.功能 ...
- 深度解析VC中的消息(上)
消息是指什么? 消息系统对于一个win32程序来说十分重要,它是一个程序运行的动力源泉.一个消息,是系统定义的一个32位的值,他唯一的定义了一个事件,向Windows发出一个通知,告诉应用程 ...
- 如何在优雅地Spring 中实现消息的发送和消费
本文将对rocktmq-spring-boot的设计实现做一个简单的介绍,读者可以通过本文了解将RocketMQ Client端集成为spring-boot-starter框架的开发细节,然后通过一个 ...
- OSSIM中分布式消息队列应用
OSSIM中分布式消息队列应用 1. 消息队列处理 企业日志数量正在以指数级形式高速增长,日志数据的具有海量.多样.异构等特点,基于传统的单一节点混合式安装的OSSIM平台(指OSSIM 4.4及以 ...
- JMS中的消息通信模型
1. MQ简介: 消息队列(Message Queue,简称MQ),是应用程序与应用程序之间的一种通信方法.应用程序通过发送和检索出入列队的针对应用程序的数据 - 消息来通信,而无需专用连接来链接它们 ...
- 如何在优雅地Spring 中实现消息的发送和消费 1
本文将对rocktmq-spring-boot的设计实现做一个简单的介绍,读者可以通过本文了解将RocketMQ Client端集成为spring-boot-starter框架的开发细节,然后通过一个 ...
- 网络游戏中网络模块浅析
在网络游戏中,不论是服务端还是客户端都需要网络通讯的功能模块,而一个优秀的成熟的网络通讯模块,又可以用于多个游戏产品中. 于是,在学习的过程中,设计和实现一个可复用的网络通讯模块,变得非常的有意义. ...
- java中的消息队列
消息队列:可以看做是一个存储消息的容器,它是分布式系统中的重要组件之一. 目的是: 1.为了通过异步处理来提高系统的性能来减少系统响应的时间 一般的步骤是客户端发起请求给服务端,服务端在请求给数据库, ...
- Android中的消息推送
转载于Android中的消息推送 前段时间做了一个应用,需要用到服务器端向Android或者是Iphone终端主动发送命令.随后客户端做出相应的反应.当时没有找到最佳的方案,一直搁置着.今天看到网上有 ...
最新文章
- 用UltraIso刻录XP到U盘安装是不行的
- 你知道Linux里D进程会搞事吗?
- 二叉树介绍与代码实现
- python基础--局部变量与全局变量
- Blockchain.com,Eden Block,DACM等知名公司加入Pocket生态
- Cocosd-x”设计模式“之五 :防御式编程”模式“
- 用安卓软件MT管理器破解元气骑士内购,小白照着也可以成功!
- which在C语言用法,A,B, and C, which ... which指代的是它们三个还是只有C呢?
- 区分统一社会信用代码、组织机构代码、注册号
- 这可能是全网最全的车载OS整理
- 2020年12月电子学会Python等级考试试卷(一级)考题解析
- Android相关简述题
- java poi row cell,使用POI进行Excel操作的总结一——创建Workbook,Sheet,Row以及Cell
- 如何把两段即以上视频合并成一个
- JSX介绍-基本使用
- 【nmon】nmon :服务器性能结果报告分析 —— 报表参数详解
- 马云再次成功了!刚刚,阿里巴巴正式宣布再出两大产品!
- tradingView--K线图 使用
- AmbiguityVis: Visualization of Ambiguity in Graph Layouts
- 马云告诉你在农村做什么赚钱
热门文章
- 无法找到将您的计算机恢复至原始状态,如何解决电脑未找到操作系统的问题
- 小窗终曲对中国游戏产业策划的小评(转)
- blob没权限 ie_IE浏览器对象不支持Blob属性或方法,IE浏览器不支持canvas toBlob()方法的Polyfill...
- MarkDown语法学习--代码区块,链接
- 【STM32H7】第22章 ThreadX GUIX窗口图标滑动操作实现方法
- android的模拟器打不开了,解决Android模拟器打不开的问题!...
- 【51CTO学院三周年】我在学院不得不说的收获
- 为IT程序员量身定制的12个目标
- Bad Header Pool 0x00000019
- dedecms标签大全(都是整理后的经典)