从零开始手写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);

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable throwable) {
        logger.debug("异常发生", throwable);
    }

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        super.channelRead(ctx, msg);
    }

    @Override
    protected void channelRead0(ChannelHandlerContext ctx, String msg) {
        logger.info("数据内容:data=" + msg);
        String result = "我是服务器,我收到了你的信息:" + msg;
        result += "\r\n";
        ctx.writeAndFlush(result);
    }

    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        logger.info("建立连接");
        super.channelActive(ctx);
    }

    @Override
    public 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> {

    @Override
    protected 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

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

[Android] 240204批量生成联系人,短信,通话记录的APK

平常在做测试的时候&#xff0c;需要批量生成很多测试数据&#xff1b; 陌生人 联系人名字的生成支持随机生成&#xff0c;也可以自定义生成&#xff0c;自定义生成陌生人的数据&#xff0c;联系人的名字是否带索引&#xff1b; 通话记录 随机生成通话记录&#xff0c;在生…

在vscode上传项目到gitee

一、在Gitee上新建一个仓库 Tip&#xff1a;若已经创建过了&#xff0c;直接跳到第二部分看VsCode如何上传代码到Gitee 创建仓库比较简单&#xff0c;下面两张图就是整个过程&#xff0c;这里不在赘述&#xff0c;具体如下&#xff1a; 二、VsCode连接Gitee上创建的仓…

AtCoder Regular Contest 171(A~B)

A - No Attacking N*N棋盘上&#xff0c;放A个rook棋和B个pawn棋。 条件1&#xff1a;假设&#xff08;i&#xff0c;j&#xff09;上有一个rook&#xff0c;那么这 i 行和这 j 列&#xff0c;都不能再有其他棋子。 条件2&#xff1a;假设&#xff08;i&#xff0c;j&#x…

Cadence——输出文件部分

本文章基于凡亿教育基础入门66讲 &#xff08;一&#xff09;MARK点&#xff0c;工艺边&#xff0c;阻抗说明相关准备文件 &#xff08;1&#xff09;MARK点 a,点击设置&#xff0c;用户偏好设置 b,指定MARK焊盘路径和封装路径 c,点击放置&#xff0c;手动 d,点击高级设置,将…

什么是TCP三次握手、四次挥手?

&#x1f3ac;作者简介&#xff1a;大家好&#xff0c;我是小徐&#x1f947;☁️博客首页&#xff1a;CSDN主页小徐的博客&#x1f304;每日一句&#xff1a;好学而不勤非真好学者 &#x1f4dc; 欢迎大家关注&#xff01; ❤️ 1、三次握手 你(客户端)给一个朋友(服务器)打电…

文心一言 VS 讯飞星火 VS chatgpt (194)-- 算法导论14.3 2题

二、用go语言&#xff0c;改写 INTERVAL-SEARCH 的代码&#xff0c;使得当所有区间都是开区间时&#xff0c;它也能正确地工作。 文心一言&#xff0c;代码正常运行&#xff1a; 在Go语言中&#xff0c;处理开区间&#xff08;open intervals&#xff09;时&#xff0c;我们需…

034 多维数组

二维数组理解图例 示例 int[][][] nums new int[2][2][2]; Random random new Random(); for (int[][] num : nums) {for (int[] ints : num) {for (int i 0; i < ints.length; i) {// 生成100以内的随机数ints[i] random.nextInt(100);}} } for (int[][] num : nums)…

Bard 最新更新:全球开放访问Gemini Pro并生成图片

深入解析最新的技术突破、实际应用案例和未来的趋势。与全球数同行一同&#xff0c;从行业内部的深度分析和实用指南中受益。不要错过这个机会&#xff0c;成为AI领域的领跑者。点击订阅&#xff0c;与未来同行&#xff01; 订阅&#xff1a;https://rengongzhineng.io/ 。 今…

[嵌入式AI从0开始到入土]5_炼丹炉的搭建(基于wsl2_Ubuntu22.04)

[嵌入式AI从0开始到入土]嵌入式AI系列教程 注&#xff1a;等我摸完鱼再把链接补上 可以关注我的B站号工具人呵呵的个人空间&#xff0c;后期会考虑出视频教程&#xff0c;务必催更&#xff0c;以防我变身鸽王。 第一章 昇腾Altas 200 DK上手 第二章 下载昇腾案例并运行 第三章…

【5G SA流程】5G SA下终端完整注册流程介绍

博主未授权任何人或组织机构转载博主任何原创文章,感谢各位对原创的支持! 博主链接 本人就职于国际知名终端厂商,负责modem芯片研发。 在5G早期负责终端数据业务层、核心网相关的开发工作,目前牵头6G算力网络技术标准研究。 博客内容主要围绕: 5G/6G协议讲解 …

JVM工作原理与实战(三十五):性能调优

专栏导航 JVM工作原理与实战 RabbitMQ入门指南 从零开始了解大数据 目录 专栏导航 前言 一、性能调优 1.性能调优方法 二、性能调优案例 案例1&#xff1a;解决CPU占用率高问题的方案 案例2&#xff1a;接口响应时间长问题 案例3&#xff1a;定位底层性能问题 案例4&a…

echarts使用之地图(五)

1 基本使用 百度地图 API : 使用百度地图的 api , 它能够在线联网展示地图 , 百度地图需要申请 ak 矢量地图 : 可以离线展示地图 , 需要开发者准备矢量地图数据。本文使用该方式。 json格式的数据如下&#xff1a; 格式参照&#xff1a;GeoJSON <!DOCTYPE html&…

车载充电器(OBC)氮化镓(GaN)驱动(高压高功率)设计(第四篇)

上图来自于网络 1、GaN FET概念 GaN FET&#xff0c;全称为Gallium Nitride Field-Effect Transistor&#xff08;氮化镓场效应晶体管&#xff09;&#xff0c;是一种采用氮化镓&#xff08;Gallium Nitride, GaN&#xff09;材料制作的新型功率半导体器件。相较于传统的硅基…

枚举(Java)

一、概念 枚举是一种特殊的类。 格式&#xff1a; 修饰符 enum 枚举类名{ 对象名称1&#xff0c;对象名称2&#xff0c;....; 其他成员... } 二、枚举类的特点 1.枚举类的第一行只能罗列一些名称&#xff0c;并且这些名称都是常量&#xff0c;每个常量记住一个枚举类对象…

【Java程序设计】【C00247】基于Springboot的农机电招平台(有论文)

基于Springboot的农机电招平台&#xff08;有论文&#xff09; 项目简介项目获取开发环境项目技术运行截图 项目简介 这是一个基于Springboot的农机电招平台 本系统分为系统功能模块、管理员功能模块、农机机主功能模块以及使用者功能模块。 系统功能模块&#xff1a;农机电招…

ubuntu离线安装k8s

目录 一、前期准备 二、安装前配置 三、安装docker 四、安装cri-dockerd 五、部署k8s master节点 六、整合kubectl与cri-dockerd 七、网络等插件安装 八、常见问题及解决方法 一、前期准备 ①ubuntu系统 本地已安装ubuntu系统&#xff0c;lsb_release -a命令查看版本信…

【数据结构】排序---C语言版

七大排序算法 一、对于排序的分类&#xff1a;二、插入排序1、直接插入排序&#xff08;1&#xff09;基本思想&#xff1a;&#xff08;2&#xff09;直接插入排序&#xff1a;&#xff08;3&#xff09;代码实现&#xff1a;&#xff08;4&#xff09;总结&#xff1a; 2、希…

2024机械工程师面试题

1.常用的机械画图软件有哪些 SolidWorks、Pro/e、CATIA、UG、Creo、CAD、inventor。CAXA电子图板. 2.第一视角是___&#xff0c;第三视角是___&#xff1b; 只要区别是&#xff1a;物体所处的位置不同。一般中国都使用第一视角的。 3.气缸属于_____执行元件&#xff0c;电磁…

AIGC技术讲解以及应用的落地

简介 近期&#xff0c;火爆的“AI绘画”、图片转AI图&#xff0c;智能聊天软件ChatGPT&#xff0c;引起了人们广泛关注。人工智能潜力再次被证明&#xff0c;而这三个概念均来自同一个领域&#xff1a;AIGC。AIGC到底是什么&#xff1f;为什么如此引人关注&#xff1f;AIGC能产…

关系型数据库的介绍与历史(History of DataBase)

昨晚和大家聊到 数据库&#xff08;DataBase 简称DB&#xff09;简单概述 &#xff0c;今天简单和大家聊聊 关系型数据库&#xff08;关系数据库&#xff09; 的历史&#xff0c;它是以关系模型&#xff08;Relational Model&#xff09;来构建的数据存储系统。 关系数据库有个…