从零开始手写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.hqwc.cn/news/453479.html

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

相关文章

react将选中本文自动滑动到容器可视区域内

// 自动滚动到可视区域内useEffect(() > {const target ref;const wrapper wrapperRef?.current;if (target && wrapperRef) {const rect target.getBoundingClientRect();const wrapperRect wrapper.getBoundingClientRect();const isVisible rect.bottom &l…

自学Java的第十九天

一&#xff0c;每日收获 1.排序 2.冒泡排序法 3.查找 4.多维数组-二维数组 二&#xff0c;新名词与小技巧 三&#xff0c;今天学习中所遇到的困难 一&#xff0c;每日收获 1.排序 ① 排序的介绍 排序是将多个数据&#xff0c;依指定的顺序进行排列的过程。 ② 排序的…

85.如何设计高并发系统(缓存、限流、熔断、降级)

文章目录 一&#xff1a;简介二&#xff1a;什么是高并发1、高并发的定义2、高并发的特点3、高并发场景和应用4、高并发的影响 三&#xff1a;高并发应对策略四&#xff1a;缓存1、浏览器缓存2、客户端缓存3、CDN缓存4、反向代理缓存5、本地缓存6、分布式缓存7、缓存问题8、缓存…

云上未来:探索云计算的技术变革与应用趋势

一、云计算的起源和演进 1.1 早期计算模型 在探讨云计算的起源和演进之前&#xff0c;理解早期的计算模型对于构建全面的视角至关重要。早期计算模型的发展奠定了云计算的基础&#xff0c;为其演进提供了技术和理念的支撑。 1.1.1 集中式计算模型 在计算技术的早期阶段&…

软件测试学习笔记-使用jmeter进行接口测试

使用jmeter之前首先需要安装jdk&#xff0c;配置其环境变量&#xff0c;然后再安装jmeter 软件测试学习笔记-使用jmeter进行接口测试 1. 断言1. 响应断言2. JSON断言 2. 参数化1. 用户定义的变量2. CSV参数化 3. 接口关联4. 连接数据库 1. 断言 对某个接口其中一些信息进行判断…

FastAdmin西陆房产系统(xiluHouse)全开源

应用介绍 一款基于FastAdminThinkPHPUniapp开发的西陆房产管理系统&#xff0c;支持小程序、H5、APP&#xff1b;包含房客、房东(高级授权)、经纪人(高级授权)三种身份。核心功能有&#xff1a;新盘销售、房屋租赁、地图找房、房源代理(高级授权)、在线签约(高级授权)、电子合同…

Vue学习笔记之组件基础

1、组件的定义 一般将 Vue 组件定义在一个单独的 .vue 文件中&#xff0c;称做单文件组件&#xff1b;当然也可以将组件直接定义在js文件中&#xff0c;如下js代码&#xff0c;定义一个组件BlogPost&#xff0c;通过props定义对外暴露属性title&#xff0c;父组件传递title&am…

在线JSON转SQL工具

在线JSON转SQL - BTool在线工具软件&#xff0c;为开发者提供方便。在线JSON转SQL工具可以将JSON文件中的数据或者JSON对象转换为SQL插入语句&#xff0c;方便用户将数据导入到数据库中。用户可以通过简单的界面上传JSON文件&#xff0c;或者文本框输入&#xff0c;点击JSON转S…

嵌入式中经典面试题分析

1.关键字static的作用是什么&#xff1f;为什么static变量只初始化一次&#xff1f; 1&#xff09;修饰局部变量&#xff1a;使得变量变成静态变量&#xff0c;存储在静态区&#xff0c;存储在静态区的数据周期和程序相同&#xff0c; 在main函数开始前初始化&#xff0c;在退…

牛客网-------------------------长方体

解题思路&#xff1a; 设棱长为x&#xff0c;y&#xff0c;z&#xff1b;假设已知面积为a&#xff0c;b&#xff0c;c 那么&#xff0c;xya&#xff1b;yzb&#xff1b;xzc&#xff1b; 一式除二式得x/za/b x(a/b)*z 联立xzc 代入得&#xff08;a/b)z^2c z^2c*b/a z根号下&…

sql相关子查询

1.什么是相关子查询 相关子查询是一个嵌套在外部查询中的查询&#xff0c;它使用了外部查询的某些值。每当外部查询处理一行数据时&#xff0c;相关子查询就会针对那行数据执行一次&#xff0c;因此它的结果可以依赖于外部查询中正在处理的行。 2.为什么要使用相关子…

【axios报错异常】: Uncaught ReferenceError: axios is not defined

问题描述: 当前代码在vivo手机和小米手机运行是正常的,点击分享按钮调出相关弹框,发送接口进行分享,但是现在oppo手机出现了问题: 点击分享按钮没有反应. 问题解析: 安卓同事经过查询后,发现打印了错误: 但是不清楚这个问题是安卓端造成的还是前端造成的,大家都不清楚. 问题…