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

从零开始手写mmo游戏从框架到爆炸(七)— 消息封装

导航:从零开始手写mmo游戏从框架到爆炸(零)—— 导航-CSDN博客      

    上一篇,我们初步把消息handler 注册到了服务中,在进行后续工作之前我们需要再做一些准备工作。

        第一:把之前自己管理的bean放到spring中去管理,后面大部分的bean都通过spring来管理。

        第二:为了方便路由消费,我们要创建一个消息体方便byte字节数组传输。

Spring        

先把spring上下文变量工具整好

SpringContextHelper.java

package com.loveprogrammer.base.factory;import org.springframework.context.ApplicationContext;public class SpringContextHelper {private static ApplicationContext ac;public static void setApplicationContext(ApplicationContext ac) {SpringContextHelper.ac = ac;}public static ApplicationContext getContext() {return ac;}public static Object getBean(String name) {return ac.getBean(name);}public static Object getBean(Class clazz) {return ac.getBean(clazz);}}

CommonBootConfig.java

import com.loveprogrammer.base.factory.SpringContextHelper;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.annotation.Configuration;@Configuration
public class CommonBootConfig implements ApplicationContextAware {@Overridepublic void setApplicationContext(ApplicationContext applicationContext) throws BeansException {SpringContextHelper.setApplicationContext(applicationContext);}
}

修改 NetworkListener.java

@Component
public class NetworkListener implements INetworkEventListener {

 修改 TcpMessageStringHandler.java

@Component
public class TcpMessageStringHandler extends SimpleChannelInboundHandler<String> {private static final Logger logger = LoggerFactory.getLogger(TcpMessageStringHandler.class);@Autowiredprivate INetworkEventListener listener;//    public TcpMessageStringHandler(INetworkEventListener listener) {
//        this.listener = listener;
//    }

修改TcpServerStringInitializer.java

public class TcpServerStringInitializer extends ChannelInitializer<SocketChannel> {@Overrideprotected void initChannel(SocketChannel ch) {ChannelPipeline pipeline = ch.pipeline();pipeline.addLast("framer",new DelimiterBasedFrameDecoder(8192, Delimiters.lineDelimiter()));pipeline.addLast("decoder", new StringDecoder());pipeline.addLast("encoder", new StringEncoder());TcpMessageStringHandler handler = (TcpMessageStringHandler) SpringContextHelper.getBean(TcpMessageStringHandler.class);pipeline.addLast(handler);}}

 消息封装

创建一个类 StringMessage

package com.loveprogrammer.pojo;import com.alibaba.fastjson2.JSON;/*** @ClassName StringMessage* @Description string类型的请求的请求体* @Author admin* @Date 2024/1/31 10:35* @Version 1.0*/
public class StringMessage {/****         topicId 路由主键对应class*         tagId 路由副健,对应method*         statusC内容的长ode主要是返回内容是告诉客户端消息的状态,成功为1,其他不同的错误使用不同的错误码*         length是度,内容的长度是不可控制的,所以使用一个长度进行定义*         body是具体的内容*/private int topicId;private int tagId;private int statusCode;private int length;private String body;public StringMessage() {}//    public StringMessage(short messageId) {
//        this.messageId = messageId;
//    }public static StringMessage create(int topicId,int tagId) {StringMessage stringMessage = new StringMessage();stringMessage.setTopicId(topicId);stringMessage.setTagId(tagId);return stringMessage;}public static StringMessage create(String origin) {StringMessage stringMessage = JSON.parseObject(origin, StringMessage.class);return stringMessage;}public static StringMessage create(int length, int topicId,int tagId , int statusCode, String content) {return new StringMessage(length, topicId, tagId, statusCode, content);}private StringMessage(int length, int topicId,int tagId, int statusCode, String body) {this.length = length;this.topicId = topicId;this.tagId = tagId;this.statusCode = statusCode;this.body = body;}public int getTopicId() {return topicId;}public void setTopicId(int topicId) {this.topicId = topicId;}public int getTagId() {return tagId;}public void setTagId(int tagId) {this.tagId = tagId;}public int getStatusCode() {return statusCode;}public void setStatusCode(int statusCode) {this.statusCode = statusCode;}public int getLength() {return length;}public void setLength(int length) {this.length = length;}public String getBody() {return body;}public void setBody(String body) {this.body = body;}@Overridepublic String toString() {return "StringMessage{" + "messageId=" + topicId + ", statusCode=" + statusCode + ", length=" + length+ ", body='" + body + '\'' + '}';}
}

  创建两个编解码类 

  MessageDecoder.java

package com.loveprogrammer.codec;import com.loveprogrammer.constants.ConstantValue;
import com.loveprogrammer.pojo.StringMessage;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.LengthFieldBasedFrameDecoder;/*** @ClassName MessageDecoder* @Description 消息解码器* @Author admin* @Date 2024/1/31 10:43* @Version 1.0*/
public class MessageDecoder extends LengthFieldBasedFrameDecoder {//判断传送客户端传送过来的数据是否按照协议传输,头部信息的大小应该是 int+int+int = 4+4+4 = 12private static final int HEADER_SIZE = 12;private int topicId;private int tagId;private int statusCode;private int length;private String body;/***** @param maxFrameLength 解码时,处理每个帧数据的最大长度* @param lengthFieldOffset 该帧数据中,存放该帧数据的长度的数据的起始位置* @param lengthFieldLength 记录该帧数据长度的字段本身的长度* @param lengthAdjustment 修改帧数据长度字段中定义的值,可以为负数* @param initialBytesToStrip 解析的时候需要跳过的字节数* @param failFast 为true,当frame长度超过maxFrameLength时立即报TooLongFrameException异常,为false,读取完整个帧再报异常*/public MessageDecoder(int maxFrameLength, int lengthFieldOffset, int lengthFieldLength, int lengthAdjustment, int initialBytesToStrip, boolean failFast) {super(maxFrameLength, lengthFieldOffset, lengthFieldLength, lengthAdjustment, initialBytesToStrip, failFast);}@Overrideprotected Object decode(ChannelHandlerContext ctx, ByteBuf in) throws Exception {if(in == null){return null;}if(in.readableBytes() < HEADER_SIZE) {throw new Exception("可读信息段比头部信息都小");}// 注意在读的过程中,readIndex的指针也在移动topicId = in.readInt();tagId = in.readInt();statusCode = in.readInt();length = in.readInt();if(in.readableBytes() < length) {throw new Exception("body获取长度" + length + ",实际长度没有达到");}ByteBuf buf = in.readBytes(length);byte[] req = new byte[buf.readableBytes()];buf.readBytes(req);body = new String(req, ConstantValue.PROJECT_CHARSET);StringMessage stringMessage = StringMessage.create(length, topicId, tagId, statusCode, body);return stringMessage;}
}

MessageEncoder.java

package com.loveprogrammer.codec;import com.loveprogrammer.constants.ConstantValue;
import com.loveprogrammer.pojo.StringMessage;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.MessageToByteEncoder;import java.nio.charset.Charset;/*** @ClassName MessageEncoder* @Description 消息编码器* @Author admin* @Date 2024/1/31 10:40* @Version 1.0*/
public class MessageEncoder extends MessageToByteEncoder<StringMessage> {@Overrideprotected void encode(ChannelHandlerContext ctx, StringMessage msg, ByteBuf out) throws Exception {if(null == msg) {throw new Exception("msg is null");}String body = msg.getBody();byte[] bodyBytes = body.getBytes(Charset.forName(ConstantValue.PROJECT_CHARSET));out.writeInt(msg.getTopicId());out.writeInt(msg.getTagId());out.writeInt(msg.getStatusCode());out.writeInt(bodyBytes.length);out.writeBytes(bodyBytes);}
}

 下一章我们来实现byte数组传输数据

上一篇:从零开始手写mmo游戏从框架到爆炸(六)— 消息处理工厂-CSDN博客

全部源码详见:

gitee : eternity-online: 多人在线mmo游戏 - Gitee.com

分支:step-06

相关文章:

从零开始手写mmo游戏从框架到爆炸(七)— 消息封装

导航&#xff1a;从零开始手写mmo游戏从框架到爆炸&#xff08;零&#xff09;—— 导航-CSDN博客 上一篇&#xff0c;我们初步把消息handler 注册到了服务中&#xff0c;在进行后续工作之前我们需要再做一些准备工作。 第一&#xff1a;把之前自己管理的bean放到spring中…...

从Unity到Three.js(画线组件line)

JavaScript 0基础&#xff0c;只是照着官方文档临摹了下&#xff0c;之后有时间再进行细节学习和功能封装。 import * as THREE from three; //引入threejsconst renderer new THREE.WebGLRenderer();//创建渲染器 //设置渲染范围&#xff0c;当前撑满全屏,屏幕左上角是&…...

LCP 30. 魔塔游戏 - 力扣(LeetCode)

题目描述 小扣当前位于魔塔游戏第一层&#xff0c;共有 N 个房间&#xff0c;编号为 0 ~ N-1。每个房间的补血道具/怪物对于血量影响记于数组 nums&#xff0c;其中正数表示道具补血数值&#xff0c;即血量增加对应数值&#xff1b;负数表示怪物造成伤害值&#xff0c;即血量减…...

数据结构——单向链表和双向链表的实现(C语言版)

目录 前言 1. 链表 1.1 链表的概念及结构 1.2 链表的分类 2. 单链表接口实现 2.1 数据结构设计与接口函数声明 2.2 创建结点&#xff0c;打印&#xff0c;查找 2.3 尾插&#xff0c;头插&#xff0c;尾删&#xff0c;头删 2.4 插入或删除 2.4.1在指定位置后 2.4.2在…...

TCP和UDP相关问题(重点)(4)——4.使用TCP的协议有哪些?使用UDP的协议有哪些?

4.使用TCP的协议有哪些&#xff1f;使用UDP的协议有哪些&#xff1f; 使用TCP的协议有&#xff1a;HTTP3.0之前的HTTP协议、HTTPS、FTP、SMTP、SSH... 使用UDP的协议有&#xff1a;HTTP3.0、DNS、DHCP......

Python进阶--爬取美女图片壁纸(基于回车桌面网的爬虫程序)

目录 一、前言 二、爬取下载美女图片 1、抓包分析 a、分析页面 b、明确需求 c、抓包搜寻 d、总结特点 2、编写爬虫代码 a、获取图片页网页源代码 b、提取所有图片的链接和标题 c、下载并保存这组图片 d、 爬取目录页的各种类型美女图片的链接 e、实现翻页 三、各…...

[office] excel如何计算毛重和皮重的时间间隔 excel计算毛重和皮重时间间隔方法 #笔记#学习方法

excel如何计算毛重和皮重的时间间隔 excel计算毛重和皮重时间间隔方法 在日常工作中经常会到用excel&#xff0c;有时需要计算毛重和皮重的时间间隔&#xff0c;具体的计算方式是什么&#xff0c;一起来了解一下吧 在日常工作中经常会到用excel&#xff0c;在整理编辑过磅数据…...

Pandas 对带有 Multi-column(多列名称) 的数据排序并写入 Excel 中

Pandas 从Excel 中读取带有 Multi-column的数据 正文 正文 我们使用如下方式写入数据&#xff1a; import pandas as pd import numpy as npdf pd.DataFrame(np.array([[10, 2, 0], [6, 1, 3], [8, 10, 7], [1, 3, 7]]), columns[[Number, Name, Name, ], [col 1, col 2, co…...

如何为Kafka加上账号密码(一)

Kafka认证基本概念 一直以来&#xff0c;我们公司内网的Kafka集群都是在裸奔&#xff0c;只要知道端口号&#xff0c;任何人都能连上集群操作一番。直到有个主题莫名消失&#xff0c;才引起我们的警觉&#xff0c;是时候该考虑为它添加一套认证策略了。 认证和授权就是一对孪生…...

Elasticsearch的Index Lifecycle Management(ILM)

Elasticsearch的Index Lifecycle Management&#xff08;ILM&#xff09;功能提供了一种自动化管理索引生命周期的方式。ILM使得用户可以基于特定的条件&#xff08;如索引的年龄、大小等&#xff09;来自动执行如回滚、删除等操作&#xff0c;进而优化存储和提高查询性能。ILM…...

2、学习 Nacos 注册中心

学习 Nacos 注册中心 一、使用Nacos作为注册中心1、父pom.xml文件配置SpringCloudAlibaba的dependency-management依赖2、在微服务中添加Nacos客户端依赖3、配置Nacos服务地址 二、服务的分级存储模型1、配置实例的集群属性2、权重配置 三、命名空间 一、使用Nacos作为注册中心…...

Java 如何操作 nginx 服务器上的文件?

随着Java技术的不断发展&#xff0c;越来越多的开发人员开始使用Java来操作服务器上的文件。其中&#xff0c;如何操作nginx服务器上的文件也是许多Java开发人员所关注的重点之一。本文将介绍Java操作nginx服务器上文件的基本方法。 一、使用Java的File类 Java的File类可以用…...

时序预测 | MATLAB实现基于CNN-GRU-AdaBoost卷积门控循环单元结合AdaBoost时间序列预测

时序预测 | MATLAB实现基于CNN-GRU-AdaBoost卷积门控循环单元结合AdaBoost时间序列预测 目录 时序预测 | MATLAB实现基于CNN-GRU-AdaBoost卷积门控循环单元结合AdaBoost时间序列预测预测效果基本介绍模型描述程序设计参考资料 预测效果 基本介绍 1.MATLAB实现基于CNN-GRU-AdaBo…...

中创ET4410 台式LCR数字电桥 简单开箱测评

最近买了一台LCR电桥&#xff0c;完善一下自己实验室的设备&#xff0c;选了中创ET4410&#xff0c;这款性价比高一点。 1199元在PDD买的&#xff0c;好像胜利的VC4090C也是找中创代工的。 ET4410介绍 本系列LCR数字电桥是采用自动平衡电桥原理设计的元件参数分析仪&#xf…...

格式化dingo返回内容

dingo api返回的内容中添加code 和 message &#xff0c;保持与异常返回的内容格式相一致。 失败会存在code 和 message &#xff0c;我们只需要关注成功的情况 非分页返回&#xff0c;可以创建一个父类controller&#xff0c;通过调用sucess方法来返回 class Controller ext…...

QGIS编译(跨平台编译)之四十六:minizip编译(Windows、Linux、MacOS环境下编译)

文章目录 一、minizip介绍二、minizip下载三、Linux下编译四、MacOS下编译五、Windows下编译一、minizip介绍 Minizip 是一个用于处理 ZIP 文件的开源库,它基于 zlib 库构建。zlib 是一个广泛使用的、免费的、开源的压缩库,提供数据压缩和解压缩功能。Minizip 扩展了 zlib 的…...

MySQL进阶查询篇(1)-索引的类型与创建

MySQL数据库索引是提高查询效率的重要手段之一。索引是一种特殊的数据结构&#xff0c;用于快速定位数据。通过创建索引&#xff0c;可以大大提高查询性能&#xff0c;减少数据库的IO操作。 MySQL数据库支持多种不同类型的索引&#xff0c;常用的索引类型包括&#xff1a; B-…...

【STL】list模拟实现

vector模拟实现 一、接口大框架函数声明速览二、结点类的模拟实现1、构造函数 三、迭代器类的模拟实现1、迭代器类存在的意义2、迭代器类的模板参数说明3、构造函数4、运算符的重载&#xff08;前置和后置&#xff09;&#xff08;1&#xff09;前置&#xff08;2&#xff09;后…...

常用的文件系统、存储类型小整理

最近接触到了五花八门的文件系统、存储类型&#xff0c;名词听得头大&#xff0c;趁假期整理学习一番~ 名称OSSFastDFSJuiceFSCIFSCephFSEFSNFS全称Object Storage Service (对象存储服务)Fast Distributed File System (快速分布式文件系统)Juice File System (Juice 文件系统…...

Java写标准输出进度条

学Java这么久了&#xff0c;突发奇想写一个 进度条 玩玩&#xff0c;下面展示一下成功吧&#xff01; Java代码实现如下 public class ProcessBar {public static void main(String[] args) {//进度条StringBuilder processBarnew StringBuilder();//进度条长度int total100;/…...

Flask RESTful 示例

目录 1. 环境准备2. 安装依赖3. 修改main.py4. 运行应用5. API使用示例获取所有任务获取单个任务创建新任务更新任务删除任务 中文乱码问题&#xff1a; 下面创建一个简单的Flask RESTful API示例。首先&#xff0c;我们需要创建环境&#xff0c;安装必要的依赖&#xff0c;然后…...

dedecms 织梦自定义表单留言增加ajax验证码功能

增加ajax功能模块&#xff0c;用户不点击提交按钮&#xff0c;只要输入框失去焦点&#xff0c;就会提前提示验证码是否正确。 一&#xff0c;模板上增加验证码 <input name"vdcode"id"vdcode" placeholder"请输入验证码" type"text&quo…...

C++ 基础特性深度解析

目录 引言 一、命名空间&#xff08;namespace&#xff09; C 中的命名空间​ 与 C 语言的对比​ 二、缺省参数​ C 中的缺省参数​ 与 C 语言的对比​ 三、引用&#xff08;reference&#xff09;​ C 中的引用​ 与 C 语言的对比​ 四、inline&#xff08;内联函数…...

Mac软件卸载指南,简单易懂!

刚和Adobe分手&#xff0c;它却总在Library里给你写"回忆录"&#xff1f;卸载的Final Cut Pro像电子幽灵般阴魂不散&#xff1f;总是会有残留文件&#xff0c;别慌&#xff01;这份Mac软件卸载指南&#xff0c;将用最硬核的方式教你"数字分手术"&#xff0…...

优选算法第十二讲:队列 + 宽搜 优先级队列

优选算法第十二讲&#xff1a;队列 宽搜 && 优先级队列 1.N叉树的层序遍历2.二叉树的锯齿型层序遍历3.二叉树最大宽度4.在每个树行中找最大值5.优先级队列 -- 最后一块石头的重量6.数据流中的第K大元素7.前K个高频单词8.数据流的中位数 1.N叉树的层序遍历 2.二叉树的锯…...

Linux C语言网络编程详细入门教程:如何一步步实现TCP服务端与客户端通信

文章目录 Linux C语言网络编程详细入门教程&#xff1a;如何一步步实现TCP服务端与客户端通信前言一、网络通信基础概念二、服务端与客户端的完整流程图解三、每一步的详细讲解和代码示例1. 创建Socket&#xff08;服务端和客户端都要&#xff09;2. 绑定本地地址和端口&#x…...

Yolov8 目标检测蒸馏学习记录

yolov8系列模型蒸馏基本流程&#xff0c;代码下载&#xff1a;这里本人提交了一个demo:djdll/Yolov8_Distillation: Yolov8轻量化_蒸馏代码实现 在轻量化模型设计中&#xff0c;**知识蒸馏&#xff08;Knowledge Distillation&#xff09;**被广泛应用&#xff0c;作为提升模型…...

阿里云Ubuntu 22.04 64位搭建Flask流程(亲测)

cd /home 进入home盘 安装虚拟环境&#xff1a; 1、安装virtualenv pip install virtualenv 2.创建新的虚拟环境&#xff1a; virtualenv myenv 3、激活虚拟环境&#xff08;激活环境可以在当前环境下安装包&#xff09; source myenv/bin/activate 此时&#xff0c;终端…...

AD学习(3)

1 PCB封装元素组成及简单的PCB封装创建 封装的组成部分&#xff1a; &#xff08;1&#xff09;PCB焊盘&#xff1a;表层的铜 &#xff0c;top层的铜 &#xff08;2&#xff09;管脚序号&#xff1a;用来关联原理图中的管脚的序号&#xff0c;原理图的序号需要和PCB封装一一…...

Spring AOP代理对象生成原理

代理对象生成的关键类是【AnnotationAwareAspectJAutoProxyCreator】&#xff0c;这个类继承了【BeanPostProcessor】是一个后置处理器 在bean对象生命周期中初始化时执行【org.springframework.beans.factory.config.BeanPostProcessor#postProcessAfterInitialization】方法时…...