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

从零开始手写mmo游戏从框架到爆炸(八)— byte数组传输

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

Netty帧解码器

        Netty中,提供了几个重要的可以直接使用的帧解码器。

LineBasedFrameDecoder

       行分割帧解码器。适用场景:每个上层数据包,使用换行符或者回车换行符做为边界分割符。发送端发送的时候,每个数据包之间以换行符/回车换行符作为分隔。在这种场景下,只需要把这个解码器加到 pipeline 中,Netty 会使用换行分隔符,把底层帧分割成一个一个完整的应用层数据包,发送到下一站。

FixedLengthFrameDecoder

        固定长度帧解码器。适用场景:每个上层数据包的长度,都是固定的,比如 100。在这种场景下,只需要把这个解码器加到 pipeline 中,Netty 会把底层帧,拆分成一个个长度为 100 的数据包 (ByteBuf),发送到下一个 channelHandler入站处理器。

DelimiterBasedFrameDecoder

        自定义分隔符帧解码器 。DelimiterBasedFrameDecoder 是LineBasedFrameDecoder的通用版本。不同之处在于,这个解码器,可以自定义分隔符,而不是局限于换行符。如果使用这个解码器,在发送的时候,末尾必须带上对应的分隔符。

LengthFieldBasedFrameDecoder

        自定义长度帧解码器。这是一种基于灵活长度的解码器。在数据包中,加了一个长度字段(长度域),保存上层包的长度。解码的时候,会按照这个长度,进行上层ByteBuf应用包的提取。

        我们之前使用的是自定义分隔符帧解码器,现在我们改用自定义长度帧解码器。LengthFieldBasedFrameDecoder有几个参数,我们看一起看一下:

  • lengthFieldOffset 长度域的偏移量,简单而言就是偏移几个字节是长度域
  • lengthFieldLength : 长度域的所占的字节数
  • lengthAdjustment : 长度值的调整值
  • initialBytesToStrip : 需要跳过的字节数

参考:LengthFieldBasedFrameDecoder 详解-CSDN博客  

现在我们就来修改编解码器,同时修改相关的类。

byte数组传输

        前两章我们已经完成了对消息体的封装和编解码器,然后我们来修改相关的代码,其实就是把原来string类型改为StringMessage类型

TcpMessageStringHandler.java


import com.loveprogrammer.base.network.listener.INetworkEventListener;
import com.loveprogrammer.base.network.support.SessionManager;
import com.loveprogrammer.pojo.StringMessage;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;/*** @ClassName TcpMessageStringHandler* @Description tcp消息处理类* @Author admin* @Date 2024/2/4 15:16* @Version 1.0*/
@Component
@Scope("prototype")
public class TcpMessageStringHandler extends SimpleChannelInboundHandler<StringMessage> {private static final Logger logger = LoggerFactory.getLogger(TcpMessageStringHandler.class);@Autowiredprivate INetworkEventListener listener;//    public TcpMessageStringHandler(INetworkEventListener listener) {
//        this.listener = listener;
//    }@Overridepublic void exceptionCaught(ChannelHandlerContext ctx, Throwable throwable) {listener.onExceptionCaught(ctx,throwable);}@Overridepublic void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {super.channelRead(ctx, msg);}@Overrideprotected void channelRead0(ChannelHandlerContext ctx, StringMessage msg) {listener.channelRead(ctx,msg);// ctx.writeAndFlush(result);}@Overridepublic void channelActive(ChannelHandlerContext ctx) throws Exception {listener.onConnected(ctx);}@Overridepublic void channelInactive(ChannelHandlerContext ctx) throws Exception {listener.onDisconnected(ctx);}
}

TcpServerStringInitializer.java

import com.loveprogrammer.base.factory.SpringContextHelper;
import com.loveprogrammer.base.network.listener.INetworkEventListener;
import com.loveprogrammer.base.network.support.NetworkListener;
import com.loveprogrammer.codec.MessageDecoder;
import com.loveprogrammer.codec.MessageEncoder;
import com.loveprogrammer.constants.ConstantValue;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.socket.SocketChannel;
import io.netty.handler.codec.DelimiterBasedFrameDecoder;
import io.netty.handler.codec.Delimiters;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;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 MessageEncoder());pipeline.addLast("encoder", new MessageDecoder(ConstantValue.MESSAGE_CODEC_MAX_FRAME_LENGTH,ConstantValue.MESSAGE_CODEC_LENGTH_FIELD_OFFSET, ConstantValue.MESSAGE_CODEC_LENGTH_FIELD_LENGTH,ConstantValue.MESSAGE_CODEC_LENGTH_ADJUSTMENT, ConstantValue.MESSAGE_CODEC_INITIAL_BYTES_TO_STRIP, false));TcpMessageStringHandler handler = (TcpMessageStringHandler) SpringContextHelper.getBean(TcpMessageStringHandler.class);pipeline.addLast(handler);}}

INetworkEventListener.java

public interface INetworkEventListener {/*** 连接建立** @param ctx ChannelHandlerContext*/void onConnected(ChannelHandlerContext ctx);/*** 连接断开* * @param ctx ChannelHandlerContext*/void onDisconnected(ChannelHandlerContext ctx);/*** 异常发生* * @param ctx ChannelHandlerContext* * @param throwable 异常*/void onExceptionCaught(ChannelHandlerContext ctx, Throwable throwable);void channelRead(ChannelHandlerContext ctx, StringMessage msg);}

NetworkListener.java

package com.loveprogrammer.base.network.support;import com.loveprogrammer.base.bean.session.Session;
import com.loveprogrammer.base.network.listener.INetworkEventListener;
import com.loveprogrammer.constants.CommonValue;
import com.loveprogrammer.pojo.StringMessage;
import io.netty.channel.ChannelHandlerContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;@Component
public class NetworkListener implements INetworkEventListener {protected static final Logger logger = LoggerFactory.getLogger(NetworkListener.class);@Overridepublic void onConnected(ChannelHandlerContext ctx) {logger.info("建立连接");SessionManager.getInstance().create(ctx.channel());}@Overridepublic void onDisconnected(ChannelHandlerContext ctx) {logger.info("建立断开");SessionManager.getInstance().close(ctx.channel());}@Overridepublic void onExceptionCaught(ChannelHandlerContext ctx, Throwable throwable) {logger.warn("异常发生", throwable);}@Overridepublic void channelRead(ChannelHandlerContext ctx, StringMessage msg) {logger.info("数据内容:data=" + msg.getBody());String result = "我是服务器,我收到了你的信息:" + msg.getBody();Session session = SessionManager.getInstance().getSessionByChannel(ctx.channel());result += ",sessionId = " + session.getId();StringMessage message = StringMessage.create(1,1);message.setStatusCode(CommonValue.MSG_STATUS_CODE_SUCCESS);message.setBody(result);SessionManager.getInstance().sendMessage(ctx.channel(),message);}
}

同样的client端也要跟着修改一下

SocketClient

public class SocketClient {private static final Logger logger = LoggerFactory.getLogger(SocketClient.class);private static final String IP = "127.0.0.1";private static final int PORT = 8088;private static EventLoopGroup group = new NioEventLoopGroup();protected static void run() throws InterruptedException {
//        Bootstrap bootstrap = new Bootstrap();
//        bootstrap.group(group);
//        bootstrap.channel(NioSocketChannel.class);
//        bootstrap.handler(new ChannelInitializer() {
//            protected void initChannel(Channel ch) throws Exception {
//                ChannelPipeline pipeline = ch.pipeline();
//                pipeline.addLast("framer",new DelimiterBasedFrameDecoder(8192, Delimiters.lineDelimiter()));
//                pipeline.addLast("decoder",new StringDecoder());
//                pipeline.addLast("encoder",new StringEncoder());
//                pipeline.addLast(new SocketClientHandler());
//            }
//        });Bootstrap bootstrap = new Bootstrap();bootstrap.group(group);bootstrap.channel(NioSocketChannel.class);bootstrap.handler(new ChannelInitializer() {@Overrideprotected void initChannel(Channel ch) {ChannelPipeline pipeline = ch.pipeline();pipeline.addLast("encoder", new MessageEncoder());pipeline.addLast("decoder", new MessageDecoder(ConstantValue.MESSAGE_CODEC_MAX_FRAME_LENGTH,ConstantValue.MESSAGE_CODEC_LENGTH_FIELD_LENGTH, ConstantValue.MESSAGE_CODEC_LENGTH_FIELD_OFFSET,ConstantValue.MESSAGE_CODEC_LENGTH_ADJUSTMENT, ConstantValue.MESSAGE_CODEC_INITIAL_BYTES_TO_STRIP,false));pipeline.addLast(new SocketClientHandler());}});// 连接服务器ChannelFuture channelFuture = bootstrap.connect(IP, PORT).sync();StringMessage msg = StringMessage.create(1,1);msg.setStatusCode(200);msg.setBody("你好,我是eric");// msg += "\r\n";channelFuture.channel().writeAndFlush(msg);logger.info("向服务器发送消息 {}",msg);channelFuture.channel().closeFuture().sync();}public static void main(String[] args) throws InterruptedException {logger.info("开始连接Socket服务器...");try {run();} catch (Exception e) {e.printStackTrace();} finally {group.shutdownGracefully();}}}

SocketClientHandler.java


import com.loveprogrammer.pojo.StringMessage;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;/*** @ClassName SocketClientHandler* @Description TODO* @Author admin* @Date 2024/1/29 17:41* @Version 1.0*/
public class SocketClientHandler extends SimpleChannelInboundHandler<StringMessage> {private static final Logger logger = LoggerFactory.getLogger(SocketClientHandler.class);@Overridepublic void exceptionCaught(ChannelHandlerContext arg0, Throwable arg1) {logger.info("异常发生", arg1);}@Overridepublic void channelRead(ChannelHandlerContext arg0, Object msg) throws Exception {super.channelRead(arg0, msg);}@Overrideprotected void channelRead0(ChannelHandlerContext context, StringMessage data) {logger.info("数据内容:data=" + data);try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}StringMessage msg = StringMessage.create(1,1);msg.setStatusCode(202);msg.setBody("你好,我是eric");// msg += "\r\n";context.channel().writeAndFlush(msg);logger.info("向服务器发送消息 {}",msg);}@Overridepublic void channelActive(ChannelHandlerContext ctx) throws Exception {logger.info("客户端连接建立");super.channelActive(ctx);}@Overridepublic void channelInactive(ChannelHandlerContext ctx) throws Exception {logger.info("客户端连接断开");super.channelInactive(ctx);}}

全部源码详见:

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

分支:step-06

相关文章:

从零开始手写mmo游戏从框架到爆炸(八)— byte数组传输

导航&#xff1a;从零开始手写mmo游戏从框架到爆炸&#xff08;零&#xff09;—— 导航-CSDN博客 Netty帧解码器 Netty中&#xff0c;提供了几个重要的可以直接使用的帧解码器。 LineBasedFrameDecoder 行分割帧解码器。适用场景&#xff1a;每个上层数据包&#xff0c;使…...

Elasticsearch:BM25 及 使用 Elasticsearch 和 LangChain 的自查询检索器

本工作簿演示了 Elasticsearch 的自查询检索器将非结构化查询转换为结构化查询的示例&#xff0c;我们将其用于 BM25 示例。 在这个例子中&#xff1a; 我们将摄取 LangChain 之外的电影样本数据集自定义 ElasticsearchStore 中的检索策略以仅使用 BM25使用自查询检索将问题转…...

uniapp的api用法大全

页面生命周期API uniApp中的页面生命周期API可以帮助开发者在页面的不同生命周期中执行相应的操作。常用的页面生命周期API包括&#xff1a;onLoad、onShow、onReady、onHide、onUnload等。其中&#xff0c;onLoad在页面加载时触发&#xff0c;onShow在页面显示时触发&#xf…...

笔记——asp.net core 中的 REST

REST&#xff08;reprentational state transfer&#xff0c;表层状态转移&#xff09; REST原则&#xff1a;提倡按照HTTP的语义使用HTTP。 如果一个系统符合REST原则&#xff0c;我们就说这个系统是Restful风格的。 在RPC风格的Web API系统中&#xff0c;我们把服务端的代码…...

排序算法---堆排序

原创不易&#xff0c;转载请注明出处。欢迎点赞收藏~ 堆排序&#xff08;Heap Sort&#xff09;是一种基于二叉堆数据结构的排序算法。它将待排序的元素构建成一个最大堆&#xff08;或最小堆&#xff09;&#xff0c;然后逐步将堆顶元素与堆的最后一个元素交换位置&#xff0c…...

Java字符串(包含字母和数字)通用排序

说明&#xff1a;本文章是之前查到的一篇安卓版的&#xff0c;具体原文路径忘记了。稍微改了一点&#xff0c;挺符合业务使用的&#xff01; 一、看代码 /*** 包含数字的字符串进行比较&#xff08;按照从小到大排序&#xff09;*/private static Integer compareString(Stri…...

【Spring】springmvc如何处理接受http请求

目录 ​编辑 1. 背景 2. web项目和非web项目 3. 环境准备 4. 分析链路 5. 总结 1. 背景 今天开了一篇文章“SpringMVC是如何将不同的Request路由到不同Controller中的&#xff1f;”&#xff1b;看完之后突然想到&#xff0c;在请求走到mvc 之前服务是怎么知道有请求进来…...

2024年安全员-B证证模拟考试题库及安全员-B证理论考试试题

题库来源&#xff1a;安全生产模拟考试一点通公众号小程序 2024年安全员-B证证模拟考试题库及安全员-B证理论考试试题是由安全生产模拟考试一点通提供&#xff0c;安全员-B证证模拟考试题库是根据安全员-B证最新版教材&#xff0c;安全员-B证大纲整理而成&#xff08;含2024年…...

redis过期淘汰策略、数据过期策略与持久化方式

redis的过期淘汰策略 redis过期淘汰策略有很多,默认是no-eviction 不删除任何数据,内存不足存入会直接报错,可以在redis配置文件中进行设置,其中有两个非常重要的概念,LRU与LFU LRU表示最近最少使用,LFU为最少频率使用 又按照volatile已设置过期时间的数据集和allkeys所有数…...

Oracle Vagrant Box 扩展根文件系统

需求 默认的Oracle Database 19c Vagrant Box的磁盘为34GB。 最近在做数据库升级实验&#xff0c;加之导入AWR dump数据&#xff0c;导致空间不够。 因此需要对磁盘进行扩容。 扩容方法1&#xff1a;预先扩容 此方法参考文档Vagrant, how to specify the disk size?。 指…...

TDengine用户权限管理

Background 官方文档关于用户管理没有很详细的介绍&#xff0c;只有零碎的几条&#xff0c;这里记录下方便后面使用。官方文档&#xff1a;https://docs.taosdata.com/taos-sql/show/#show-users 1、查看用户 show users;super 1&#xff0c;表示超级用户权限 0&#xff0c;表…...

推荐一款开源的跨平台划词翻译和OCR翻译软件:Pot

Pot简介 一款开源的跨平台划词翻译和OCR翻译软件 下载安装指南 根据你的机器型号下载对应版本&#xff0c;下载完成后双击安装即可。 使用教程 Pot具体功能如下&#xff1a; 划词翻译输入翻译外部调用鼠标选中需要翻译的文本&#xff0c;按下设置的划词翻译快捷键即可按下输…...

spring boot学习第十一篇:发邮件

1、pom.xml文件内容如下&#xff08;是我所有学习内容需要的&#xff0c;不再单独分出来&#xff0c;包不会冲突&#xff09;&#xff1a; <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache.org/POM/4.0.0"…...

Linux中ps/kill/execl的使用

ps命令&#xff1a; ps -aus或者ps -ajx或者 ps -ef可以查看有哪些进程。加上 | grep "xxx" 可以查看名为”xxx"的进程。 ps -aus | grep "xxx" kill命令&#xff1a; kill -9 pid 杀死某个进程 kill -l 查看系统有哪些信号 execl函数&#…...

【web前端开发】HTML及CSS简单页面布局练习

案例一 网页课程 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta http-equiv"X-UA-Compatible" content"IEedge"><meta name"viewport" content"widthdevice-wi…...

2.7日学习打卡----初学RabbitMQ(二)

2.7日学习打卡 JMS 由于MQ产品很多&#xff0c;操作方式各有不同&#xff0c;于是JAVA提供了一套规则 ——JMS&#xff0c;用于操作消息中间件。JMS即Java消息服务 &#xff08;JavaMessage Service&#xff09;应用程序接口&#xff0c;是一个Java平台中关于面 向消息中间件的…...

【工作学习 day04】 9. uniapp 页面和组件的生命周期

问题描述 uniapp常用的有&#xff1a;页面和组件&#xff0c;并且页面和组件各自有各自的生命周期函数&#xff0c;那么在页面/组件请求数据时&#xff0c;是用created呢&#xff0c;还是用onLoad呢&#xff1f; 先说结论: 组件使用组件的生命周期&#xff0c;页面使用页面的…...

Mysql-数据库优化-客户端连接参数

客户端参数 原文地址 # 连接池配置 # 初始化连接数 spring.datasource.druid.initial-size1 # 最小空闲连接数&#xff0c;一般设置和initial-size一致 spring.datasource.druid.min-idle1 # 最大活动连接数&#xff0c;一个数据库能够支撑最大的连接数是多少呢&#xff1f; …...

【十二】【C++】vector用法的探究

vector类创建对象 /*vector类创建对象*/ #if 1 #define _CRT_SECURE_NO_WARNINGS#include <iostream> using namespace std; #include <vector> #include <algorithm> #include <crtdbg.h>class Date {public:Date(int year 1900, int month 1, int …...

Docker 基本介绍

Docker 基本介绍 镜像 Docker镜像就是一个只读的模板。 例如&#xff1a;一个镜像可以包含一个完整的ubuntu操作系统环境&#xff0c;里面仅安装了Apache或用户需要的其它应用 程序。 镜像可以用来创建Docker容器。Docker提供了一个很简单的机制来创建镜像或者更新现有的镜…...

css实现圆环展示百分比,根据值动态展示所占比例

代码如下 <view class""><view class"circle-chart"><view v-if"!!num" class"pie-item" :style"{background: conic-gradient(var(--one-color) 0%,#E9E6F1 ${num}%),}"></view><view v-else …...

MongoDB学习和应用(高效的非关系型数据库)

一丶 MongoDB简介 对于社交类软件的功能&#xff0c;我们需要对它的功能特点进行分析&#xff1a; 数据量会随着用户数增大而增大读多写少价值较低非好友看不到其动态信息地理位置的查询… 针对以上特点进行分析各大存储工具&#xff1a; mysql&#xff1a;关系型数据库&am…...

Spring Boot+Neo4j知识图谱实战:3步搭建智能关系网络!

一、引言 在数据驱动的背景下&#xff0c;知识图谱凭借其高效的信息组织能力&#xff0c;正逐步成为各行业应用的关键技术。本文聚焦 Spring Boot与Neo4j图数据库的技术结合&#xff0c;探讨知识图谱开发的实现细节&#xff0c;帮助读者掌握该技术栈在实际项目中的落地方法。 …...

html-<abbr> 缩写或首字母缩略词

定义与作用 <abbr> 标签用于表示缩写或首字母缩略词&#xff0c;它可以帮助用户更好地理解缩写的含义&#xff0c;尤其是对于那些不熟悉该缩写的用户。 title 属性的内容提供了缩写的详细说明。当用户将鼠标悬停在缩写上时&#xff0c;会显示一个提示框。 示例&#x…...

C++ 设计模式 《小明的奶茶加料风波》

&#x1f468;‍&#x1f393; 模式名称&#xff1a;装饰器模式&#xff08;Decorator Pattern&#xff09; &#x1f466; 小明最近上线了校园奶茶配送功能&#xff0c;业务火爆&#xff0c;大家都在加料&#xff1a; 有的同学要加波霸 &#x1f7e4;&#xff0c;有的要加椰果…...

BLEU评分:机器翻译质量评估的黄金标准

BLEU评分&#xff1a;机器翻译质量评估的黄金标准 1. 引言 在自然语言处理(NLP)领域&#xff0c;衡量一个机器翻译模型的性能至关重要。BLEU (Bilingual Evaluation Understudy) 作为一种自动化评估指标&#xff0c;自2002年由IBM的Kishore Papineni等人提出以来&#xff0c;…...

C语言中提供的第三方库之哈希表实现

一. 简介 前面一篇文章简单学习了C语言中第三方库&#xff08;uthash库&#xff09;提供对哈希表的操作&#xff0c;文章如下&#xff1a; C语言中提供的第三方库uthash常用接口-CSDN博客 本文简单学习一下第三方库 uthash库对哈希表的操作。 二. uthash库哈希表操作示例 u…...

离线语音识别方案分析

随着人工智能技术的不断发展&#xff0c;语音识别技术也得到了广泛的应用&#xff0c;从智能家居到车载系统&#xff0c;语音识别正在改变我们与设备的交互方式。尤其是离线语音识别&#xff0c;由于其在没有网络连接的情况下仍然能提供稳定、准确的语音处理能力&#xff0c;广…...

c# 局部函数 定义、功能与示例

C# 局部函数&#xff1a;定义、功能与示例 1. 定义与功能 局部函数&#xff08;Local Function&#xff09;是嵌套在另一个方法内部的私有方法&#xff0c;仅在包含它的方法内可见。 • 作用&#xff1a;封装仅用于当前方法的逻辑&#xff0c;避免污染类作用域&#xff0c;提升…...

C++_哈希表

本篇文章是对C学习的哈希表部分的学习分享 相信一定会对你有所帮助~ 那咱们废话不多说&#xff0c;直接开始吧&#xff01; 一、基础概念 1. 哈希核心思想&#xff1a; 哈希函数的作用&#xff1a;通过此函数建立一个Key与存储位置之间的映射关系。理想目标&#xff1a;实现…...