从零开始手写mmo游戏从框架到爆炸(二)— 核心组件抽离与工厂模式创建

        上一章我们已经完成了一个基本netty的通信,但是netty的启动很多代码都是重复的,所以我们使用工厂模式来生成不同的ServerBootstrap。

首先创建一个新的组件core组件,和common组件,主要用于netty通信和工具类,从server中分离出来没有本质的区别,就是希望可以把功能分散在不同的组件中,后续方便多人进行协同开发(如果有多人的话)。

eternity-server的pom文件中增加依赖:

    <dependencies><dependency><groupId>com.loveprogrammer</groupId><artifactId>eternity-core</artifactId><version>1.0-SNAPSHOT</version></dependency></dependencies>

eternity-core的pom文件中增加依赖:

    <dependencies><dependency><groupId>com.loveprogrammer</groupId><artifactId>eternity-common</artifactId><version>1.0-SNAPSHOT</version></dependency></dependencies>

公共变量

ConstantValue.java common:src/../constants

package com.loveprogrammer.constants;/*** @ClassName ConstantValue* @Description 静态数据类* @Author admin* @Date 2024/1/30 10:01* @Version 1.0*/
public class ConstantValue {public static final String CHANNEL_TYPE_NIO = "NIO";public static final String CHANNEL_TYPE_OIO = "OIO";public static final String PROTOCOL_TYPE_HTTP = "HTTP";public static final String PROTOCOL_TYPE_HTTPS = "HTTPS";public static final String PROTOCOL_TYPE_TCP = "TCP";public static final String PROTOCOL_TYPE_PROTOBUF = "PROTOBUF";public static final String PROTOCOL_TYPE_WEBSOCKET = "WEBSOCKET";public static final String MESSAGE_TYPE_STRING = "STRING";public static final String MESSAGE_TYPE_BYTE = "BYTE";public static final String PROJECT_CHARSET = "UTF-8";public static final int MESSAGE_CODEC_MAX_FRAME_LENGTH = 1024 * 1024;public static final int MESSAGE_CODEC_LENGTH_FIELD_LENGTH = 4;public static final int MESSAGE_CODEC_LENGTH_FIELD_OFFSET = 2;public static final int MESSAGE_CODEC_LENGTH_ADJUSTMENT = 0;public static final int MESSAGE_CODEC_INITIAL_BYTES_TO_STRIP = 0;/*** 登录和下线队列*/public static final int QUEUE_LOGIN_LOGOUT = 1;/*** 业务队列*/public static final int QUEUE_LOGIC = 2;private ConstantValue() {}}

ServerException.java common:src/../exception

public class ServerException extends Exception{private String errMsg;public ServerException(String errMsg) {super(errMsg);this.errMsg = errMsg;}public ServerException(Throwable cause) {super(cause);}
}

 下面是core中的新增代码

ServerConfig.java

/*** @ClassName ServerConfig* @Description 服务基本配置类* @Author admin* @Date 2024/2/4 15:12* @Version 1.0*/
public class ServerConfig {private static final Logger logger = LoggerFactory.getLogger(ServerConfig.class);private Integer port;private String channelType;private String protocolType;private static ServerConfig instance = null;private ServerConfig() {}public static ServerConfig getInstance() {if (instance == null) {instance = new ServerConfig();instance.init();instance.printServerInfo();}return instance;}private void init() {port = 8088;channelType = "NIO";protocolType = "TCP";}public void printServerInfo() {logger.info("**************Server INFO******************");logger.info("protocolType  : " + protocolType);logger.info("port          : " + port);logger.info("channelType   : " + channelType);logger.info("**************Server INFO******************");}public Integer getPort() {return port;}public void setPort(Integer port) {this.port = port;}public String getChannelType() {return channelType;}public void setChannelType(String channelType) {this.channelType = channelType;}public String getProtocolType() {return protocolType;}public void setProtocolType(String protocolType) {this.protocolType = protocolType;}
}

ServerBootstrapFactory.java

package com.loveprogrammer.base.factory;import com.loveprogrammer.base.bean.ServerConfig;
import com.loveprogrammer.constants.ConstantValue;
import com.loveprogrammer.exception.ServerException;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.oio.OioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.channel.socket.oio.OioServerSocketChannel;/*** @ClassName ServerBootstrapFactory* @Description Bootstrap工厂类* @Author admin* @Date 2024/2/4 15:13* @Version 1.0*/
public class ServerBootstrapFactory {private ServerBootstrapFactory() {}public static ServerBootstrap createServerBootstrap() throws ServerException {ServerBootstrap serverBootstrap = new ServerBootstrap();switch (ServerConfig.getInstance().getChannelType()) {case ConstantValue.CHANNEL_TYPE_NIO:EventLoopGroup bossGroup = new NioEventLoopGroup();EventLoopGroup workerGroup = new NioEventLoopGroup();serverBootstrap.group(bossGroup, workerGroup);serverBootstrap.channel(NioServerSocketChannel.class);return serverBootstrap;case ConstantValue.CHANNEL_TYPE_OIO:serverBootstrap.group(new OioEventLoopGroup());serverBootstrap.channel(OioServerSocketChannel.class);return serverBootstrap;default:throw new ServerException("Failed to create ServerBootstrap,  " +ServerConfig.getInstance().getChannelType() + " not supported!");}}
}

ServerChannelFactory.java

package com.loveprogrammer.base.factory;import com.loveprogrammer.base.bean.ServerConfig;
import com.loveprogrammer.base.network.channel.tcp.str.TcpServerStringInitializer;
import com.loveprogrammer.constants.ConstantValue;
import com.loveprogrammer.exception.ServerException;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.socket.SocketChannel;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;/*** @ClassName ServerChannelFactory* @Description channel工厂类* @Author admin* @Date 2024/2/4 15:13* @Version 1.0*/
public class ServerChannelFactory {private static final Logger logger = LoggerFactory.getLogger(ServerChannelFactory.class);public static Channel createAcceptorChannel() throws ServerException {Integer port = ServerConfig.getInstance().getPort();final ServerBootstrap serverBootstrap = ServerBootstrapFactory.createServerBootstrap();serverBootstrap.childHandler(getChildHandler());logger.info("创建Server...");try {ChannelFuture channelFuture = serverBootstrap.bind(port).sync();channelFuture.awaitUninterruptibly();if(channelFuture.isSuccess()) {return channelFuture.channel();}else{String errMsg = "Failed to open socket! Cannot bind to port: " + port + "!";logger.error(errMsg);throw new ServerException(errMsg);}} catch (Exception e) {logger.debug(port + "is bind");throw new ServerException(e);}}private static ChannelInitializer<SocketChannel> getChildHandler() throws ServerException {String protocolType = ServerConfig.getInstance().getProtocolType();if (ConstantValue.PROTOCOL_TYPE_HTTP.equals(protocolType) || ConstantValue.PROTOCOL_TYPE_HTTPS.equals(protocolType)) {} else if (ConstantValue.PROTOCOL_TYPE_TCP.equals(protocolType)) {return new TcpServerStringInitializer();} else if (ConstantValue.PROTOCOL_TYPE_WEBSOCKET.equals(protocolType)) {} else if (ConstantValue.PROTOCOL_TYPE_PROTOBUF.equals(protocolType)) {} else {}String errMsg = "undefined protocol:" + protocolType + "!";throw new ServerException(errMsg);}}

TcpMessageStringHandler.java

package com.loveprogrammer.base.network.channel.tcp.str;import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;/*** @ClassName TcpMessageStringHandler* @Description tcp消息处理类* @Author admin* @Date 2024/2/4 15:16* @Version 1.0*/
public class TcpMessageStringHandler extends SimpleChannelInboundHandler<String> {private static final Logger logger = LoggerFactory.getLogger(TcpMessageStringHandler.class);@Overridepublic void exceptionCaught(ChannelHandlerContext ctx, Throwable throwable) {logger.debug("异常发生", throwable);}@Overridepublic void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {super.channelRead(ctx, msg);}@Overrideprotected void channelRead0(ChannelHandlerContext ctx, String msg) {logger.info("数据内容:data=" + msg);String result = "我是服务器,我收到了你的信息:" + msg;result += "\r\n";ctx.writeAndFlush(result);}@Overridepublic void channelActive(ChannelHandlerContext ctx) throws Exception {logger.info("建立连接");super.channelActive(ctx);}@Overridepublic void channelInactive(ChannelHandlerContext ctx) throws Exception {logger.info("连接断开");super.channelInactive(ctx);}
}

 TcpServerStringInitializer.java

package com.loveprogrammer.base.network.channel.tcp.str;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;/*** @ClassName TcpServerStringInitializer* @Description TODO* @Author admin* @Date 2024/2/4 15:15* @Version 1.0*/
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());pipeline.addLast(new TcpMessageStringHandler());}}

  修改启动类EternityServerMain :

package com.loveprogrammer;import com.loveprogrammer.base.factory.ServerBootstrapFactory;
import com.loveprogrammer.base.factory.ServerChannelFactory;
import com.loveprogrammer.exception.ServerException;
import com.loveprogrammer.netty.simple.SocketServer;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.Channel;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;/*** Hello world!**/
public class EternityServerMain
{// 为了保证使用时,不需要每次都去创建logger 对象,我们声明静态常量public static final Logger LOGGER = LoggerFactory.getLogger(EternityServerMain.class);public static void main( String[] args ){LOGGER.info( "Hello World!" );// 最基本的启动方法
//        try {
//            LOGGER.info("开始启动Socket服务器...");
//            new SocketServer().run();
//        } catch (Exception e) {
//            LOGGER.error( "服务器启动失败",e);
//        }// 工厂模式启动方法try {Channel channel = ServerChannelFactory.createAcceptorChannel();channel.closeFuture().sync();} catch (Exception e) {LOGGER.error( "服务器启动失败",e);}}
}

 全部源码详见:

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

分支:step-02

上一章:

从零开始手写mmo游戏从框架到爆炸(一)— 开发环境-CSDN博客

下一章:从零开始手写mmo游戏从框架到爆炸(三)— 服务启动接口与网络事件监听器-CSDN博客

参考:

java游戏服务器开发: https://blog.csdn.net/cmqwan/category_7690685.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/669929.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

JavaScript基础速成

由于学web时只学了后端&#xff0c;现在到了前后端联调的场景发现看不懂前端代码&#xff0c;于是开始恶补 看了下基础内容发现html和css比较好看懂&#xff0c;但JavaScript比较迷&#xff0c;大概知道组件id绑定事件 下面选取看菜鸟教程补充的JS知识 JS的作用 JS是在html…

PDF文件格式(一):新版格式交叉引用表

PDF交叉引用表是PDF的重要组成部分&#xff0c;本文介绍的是新交叉引用表&#xff0c;这种引用表的格式是PDF的obj格式&#xff0c;内容是被压缩存放在obj下的stream中&#xff0c;因此比常规的引用表格式复杂。下面就开始介绍这种交叉引用表的格式和解析的方法&#xff1a; 1…

文心一言4.0API接入指南

概述 文心一言是百度打造出来的人工智能大语言模型&#xff0c;具备跨模态、跨语言的深度语义理解与生成能力&#xff0c;文心一言有五大能力&#xff0c;文学创作、商业文案创作、数理逻辑推算、中文理解、多模态生成&#xff0c;其在搜索问答、内容创作生成、智能办公等众多…

LeetCode、790. 多米诺和托米诺平铺【中等,二维DP,可转一维】

文章目录 前言LeetCode、790. 多米诺和托米诺平铺【中等&#xff0c;二维DP&#xff0c;可转一维】题目与分类思路二维解法二维转一维 资料获取 前言 博主介绍&#xff1a;✌目前全网粉丝2W&#xff0c;csdn博客专家、Java领域优质创作者&#xff0c;博客之星、阿里云平台优质…

飞天使-k8s知识点12-kubernetes散装知识点1-架构有状态资源对象分类

文章目录 k8s架构图有状态和无状态服务 资源和对象对象规约和状态 资源的对象-资源的分类元数据型与集群型资源命名空间 k8s架构图 有状态和无状态服务 区分有状态和无状态服务有利于维护yaml文件 因为配置不同资源和对象 命令行yaml来定义对象对象规约和状态 规约 spec 描述…

嵌入式软件设计方式与方法

1、嵌入式软件与设计模式 思从深而行从简 软件开发&#xff0c;难的不是编写软件&#xff0c;而是编写功能正常的软件。软件工程化才能保证软件质量和项目进度&#xff0c;而设计模式使代码开发真正工程化&#xff0c;设计模式是软件工程的基石。 所谓设计模式就是对常见问题的…

idea(2023.3.3 ) spring boot热部署,修改热部署延迟时间

1、添加依赖 <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-devtools</artifactId><optional>true</optional> </dependency>载入依赖 2、设置编辑器 设置两个选项 设置热部署更新延迟时…

功率电阻器应用 (electroschematics.com)

功率电阻器的应用非常广泛&#xff0c;因此无法轻易地将其制成表格。这里讨论的是一些实用的、有用的应用程序&#xff0c;你会发现它们很有趣。首先是一些典型的管状功率电阻器的图像。 一些常见的功率电阻器应用 电阻加热器 电流检测 – 分流应用 缓冲器应用 泄放电阻 浪…

MC34063异常发热分析

问题描述&#xff1a; 工程现场反馈若干电源转换模块损坏&#xff0c;没有输出。拿到问题模块后&#xff0c;查看有一个MC34063周围的PCB有比较明显的高温痕迹&#xff0c;配套的电感也有明显的高温过热痕迹。 问题调查&#xff1a; MC34063的电路非常经典&#xff08;虽然自…

RabbitMQ 安装

下载erlang语言&#xff1a; erlang语言 下载RabbitMQ rabbitmq 安装erlang 1.以管理员身份安装erlang 2.弹出框选择next 3.选择安装路径&#xff0c;亦可以安装在默认路径 4.接下来一路点击下一步&#xff0c;无需任何修改&#xff0c;直到 install安装为止&#xff…

微服务限流(漏桶算法、令牌桶算法)

在微服务架构中&#xff0c;限流是一种重要的技术手段&#xff0c;用于控制服务接收的流量&#xff0c;以保护系统免受突发流量冲击。漏桶算法和令牌桶算法是两种常见的限流算法。同时&#xff0c;负载均衡策略和自定义负载均衡也是确保服务稳定性和高效性的关键措施。下面将深…

力扣:131. 分割回文串

回溯解法思路&#xff1a; 1.先声明一个集合来接受全部的回文子串组合&#xff0c;在声明一个集合来接收单个回文子串的组合。 2.写一个回溯函数&#xff0c;里面有终止条件和遍历全部组合的for循环来进行遍历全部的组合&#xff0c;终止条件为开始索引等于字符串的长度时&am…

政安晨:机器学习快速入门(二){基于Python与Pandas} {建立您的第一个机器学习模型}

现在咱们要一起创建您的第一个机器学习模型啦&#xff01; 选择建模数据 你的数据集包含太多变量&#xff0c;让你无法理解&#xff0c;甚至无法很好地打印出来。你如何将这大量的数据减少到你能理解的程度&#xff1f; 我们将从直觉上选择几个变量。后续课程将向你展示自动优…

【教学类-46-05】吉祥字门贴5.0(华光彩云_CNKI 文本框 空心字涂色 ,繁简都可以,建议简体)

作品展示 背景需求&#xff1a; 1、制作了空心字的第1款 华光通心圆_CNKI &#xff0c;发现它不能识别某些简体字&#xff0c;但可以识别他们的繁体字&#xff08;繁体为准&#xff09; 【教学类-46-01】吉祥字门贴1.0&#xff08;华光通心圆_CNKI 文本框 空心字涂色&#xf…

掌握Linux du命令:高效查看文件和目录大小

今天我们在生产环境中的服务器上收到了有关/var磁盘目录使用率较高的警报。为了解决这一问题&#xff0c;我们进行了/var目录下一些大文件的清理和转移操作。在查找那些占用磁盘空间较多的文件时&#xff0c;我们频繁使用了du命令。在Linux系统中&#xff0c;du命令是一款功能强…

Webpack插件浅析

常用的webpack插件功能介绍&#xff1a; 1.HotModuleReplacementPlugin 模块热更新插件。Hot-Module-Replacement的热更新是依赖于webpack-dev-server&#xff0c;有时是在打包文件改变时更新打包文件或者重新加载刷新整个页面&#xff0c;HMR是只更新修改的部分。 HotModul…

SpringBoot集成axis发布WebService服务

文章目录 1、使用maven-web项目生成server-config.wsdd文件1.1、新建maven-web项目1.1.1、新建项目1.1.2、添加依赖 1.2、编写服务接口和实现类1.2.1、OrderService接口1.2.2、OrderServiceImpl实现类 1.3、配置deploy.wsdd文件deploy.wsdd文件 1.4、配置tomcat1.4.1、配置tomc…

C++ Webserver从零开始:基础知识(八)——多线程编程

线程概述 现代Linux系统主要使用的线程库是NPTL&#xff0c;在Linux中可以通过 getconf GNU_LIBPTHREAD_VERSION 获取Linux线程库版本。谈到线程&#xff0c;就不得不提及线程模型 线程模型 本小节概念较多&#xff0c;采用问答式进行讲解&#xff0c;以帮助读者理解 什么…

Matlab:利用1D-CNN(一维卷积神经网络),分析高光谱曲线数据或时序数据

1DCNN 简介&#xff1a; 1D-CNN&#xff08;一维卷积神经网络&#xff09;是一种特殊类型的卷积神经网络&#xff0c;设计用于处理一维序列数据。这种网络结构通常由多个卷积层和池化层交替组成&#xff0c;最后使用全连接层将提取的特征映射到输出。 以下是1D-CNN的主要组成…

详细分析Redis中数值乱码的根本原因以及解决方式

目录 前言1. 问题所示2. 原理分析3. 拓展 前言 对于这方面的相关知识推荐阅读&#xff1a; Redis框架从入门到学精&#xff08;全&#xff09;Java关于RedisTemplate的使用分析 附代码java框架 零基础从入门到精通的学习路线 附开源项目面经等&#xff08;超全&#xff09; …