从零开始搭建游戏服务器 第一节 创建一个简单的服务器架构

这篇具有很好参考价值的文章主要介绍了从零开始搭建游戏服务器 第一节 创建一个简单的服务器架构。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

引言

由于现在java web太卷了,所以各位同行可以考虑换一个赛道,做游戏还是很开心的。

本篇教程给新人用于学习游戏服务器的基本知识,给新人们一些学习方向,有什么错误的地方欢迎各位同行进行讨论。

技术选型

本篇教程预计使用Java+Redis+Mongo

正文

本着先完成再完美的原则,从最简单的echo服务器开始。

从零开始搭建游戏服务器 第一节 创建一个简单的服务器架构

Echo服务器就是,客户端发什么数据,服务端就原样返回回去。

创建基础架构

IDEA创建项目

从零开始搭建游戏服务器 第一节 创建一个简单的服务器架构

我这边用Gradle进行依赖管理,使用的版本为 gradle8.0.2, openjdk19.

修改build.gradle导入几个基础开发包。

dependencies {
    //网络
    implementation group: 'io.netty', name: 'netty-all', version: '4.1.90.Final'
    //spring
    implementation group: 'org.springframework', name: 'spring-context', version: '6.0.6'
    //log
    implementation group: 'org.slf4j', name: 'slf4j-api', version: '1.7.36'
    implementation group: 'ch.qos.logback', name: 'logback-core', version: '1.2.11'
    implementation group: 'ch.qos.logback', name: 'logback-access', version: '1.2.11'
    implementation group: 'ch.qos.logback', name: 'logback-classic', version: '1.2.11'
    //lombok
    compileOnly group: 'org.projectlombok', name: 'lombok', version: '1.18.24'
}

创建Bean配置类

@Configuration
@ComponentScan(basePackages = {"com.wfgame"})
public class GameBeanConfiguration {
}

创建主类

@Component
@Slf4j
public class GameMain {
    public static void main(String[] args) {
        // 初始化Spring
        AnnotationConfigApplicationContext springContext = new AnnotationConfigApplicationContext(GameBeanConfiguration.class);
        springContext.start();
        log.info("server start!");
    }
}

运行一下,正常输出server start!

我们会发现,程序执行后马上停止了,对于游戏服务器来说,我们需要保持运行状态,等待玩家接入进行游戏。所以我们main中增加一个循环,不停读取控制台输入,当读取到控制台输入stop时,我们再进行停服。

修改main方法如下:

    public static void main(String[] args) {
        // 初始化Spring
        AnnotationConfigApplicationContext springContext = new AnnotationConfigApplicationContext(GameBeanConfiguration.class);
        springContext.start();

        log.info("server start!");

        //region 处理控制台输入,每秒检查一遍 stopFlag,为true就跳出循环,执行关闭操作
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        // 设置循环使服务器不立刻停止
        while (true) {
            if (stopFlag) {
                log.info("receive stop flag, server will stop!");
                break;
            }
            // 每次循环停止一秒,避免循环频率过高
            try {
                Thread.sleep(1000L);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            //处理控制台指令
            try {
                if (br.ready()) {
                    String cmd = br.readLine().trim();
                    if (cmd.equals("stop")) {//正常关服
                        stopFlag = true;
                        log.info("Receive stop flag, time to stop.");
                    } else {
                        log.info("Unsupported cmd:{}", cmd);
                    }
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

        //停掉虚拟机
        System.exit(0);
    }

这样我们就获得了一个可以控制停服的服务器。当我们控制台输入stop时,程序结束运行。

添加Netty监听端口

要与客户端进行TCP连接,需要建立socket通道,然后通过socket通道进行数据交互。

传统BIO一个线程一个连接,有新的连接进来时就要创建一个线程,并持续读取数据流,当这个连接发送任何请求时,会对性能造成严重浪费。

NIO一个线程通过多路复用器可以监听多个连接,通过轮询判断连接是否有数据请求。

Netty对java原生NIO进行了封装,简化了代码,便于我们的使用。

Netty的包我们之前已经导入过了,直接拿来用即可。

首先我们创建一个Netty自定义消息处理类。

@Sharable
public class NettyMessageHandler extends SimpleChannelInboundHandler<Object> {
    /**
     * 读取数据
     */
    @Override
    protected void channelRead0(ChannelHandlerContext ctx, Object msg) throws Exception {
        this.doRead(ctx, msg);
    }

    private void doRead(ChannelHandlerContext ctx, Object msg) {
        System.out.println("received msg = : " + msg);
        // 马上将原数据返回
        ctx.writeAndFlush(msg);
    }
}

然后编写Netty服务器启动代码,我们修改GameMain类的代码

@Component
@Slf4j
public class GameMain {

    // 停服标志
    private static boolean stopFlag = false;

    public static void main(String[] args) {
        // 初始化Spring
        AnnotationConfigApplicationContext springContext = new AnnotationConfigApplicationContext(GameBeanConfiguration.class);
        springContext.start();

        // 启动Netty服务器
        try {
            startNetty();
            log.info("Netty server start!");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        log.info("server start!");

        //region 处理控制台输入,每秒检查一遍 stopFlag,为true就跳出循环,执行关闭操作
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        // 设置循环使服务器不立刻停止
        while (true) {
            if (stopFlag) {
                log.info("receive stop flag, server will stop!");
                break;
            }
            // 每次循环停止一秒,避免循环频率过高
            try {
                Thread.sleep(1000L);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            //处理控制台指令
            try {
                if (br.ready()) {
                    String cmd = br.readLine().trim();
                    if (cmd.equals("stop")) {//正常关服
                        stopFlag = true;
                        log.info("Receive stop flag, time to stop.");
                    } else {
                        log.info("Unsupported cmd:{}", cmd);
                    }
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

        //停掉虚拟机
        System.exit(0);
    }

    /**
     * 启动netty服务器
     */
    private static void startNetty() throws InterruptedException {
        int port = 2333;
        log.info("Netty4SocketServer start---Normal, port = " + port);

        final NioEventLoopGroup bossGroup = new NioEventLoopGroup(2);
        final NioEventLoopGroup workerGroup = new NioEventLoopGroup();

        ServerBootstrap bootstrap = new ServerBootstrap();
        bootstrap.group(bossGroup, workerGroup);
        bootstrap.channel(NioServerSocketChannel.class);
        bootstrap.option(ChannelOption.SO_REUSEADDR, true);//允许重用端口
        bootstrap.option(ChannelOption.SO_BACKLOG, 512);//允许多少个新请求进入等待
        bootstrap.option(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT);//是否使用内存池
        bootstrap.childOption(ChannelOption.SO_KEEPALIVE, true);    // 保持连接活动
        bootstrap.childOption(ChannelOption.TCP_NODELAY, false);    // 禁止Nagle算法等待更多数据合并发送,提高信息及时性
        bootstrap.childOption(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT);//是否使用内存池

        final NettyMessageHandler handler = new NettyMessageHandler();
        bootstrap.childHandler(new ChannelInitializer<SocketChannel>() {
            @Override
            protected void initChannel(SocketChannel ch) throws Exception {
                ChannelPipeline cp = ch.pipeline();
                cp.addLast(new StringDecoder());
                cp.addLast(new StringEncoder());
                cp.addLast("handler", handler);
            }
        });
        // 绑定并监听端口
        bootstrap.bind(port).sync();//线程同步阻塞等待服务器绑定到指定端口

        // 优雅停机
        Runtime.getRuntime().addShutdownHook(new Thread(() -> {
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }));

        log.info("Netty4SocketServer ok,bind at :" + port);
    }

我们先创建了一个startNetty()方法,用于启动Netty服务器,同时绑定了端口2333

我们要注意一下initChannel这块代码,我们注册了String编码解码器,他们是用换行符作为一个消息的结束标志,因此我们等下通过客户端发送消息过来需要在行尾添加换行符。同时将我们自定义的消息处理类也注册进pipeline中,当客户端发送消息过来,先通过StringDecoder进行解码,然后流入自定义处理类中进行下一步处理。

至此服务端Netty接入完毕,我们下面编写一个客户端进行测试。

编写客户端进行测试

我们增加了ClientMain类,用socket与服务器进行连接,读取控制台输入上行到服务器,同时接受服务器下行的消息。

public class ClientMain {

    private static Socket socket = null;
    private static BufferedReader br = null;
    private static BufferedWriter writer = null;
    private static BufferedReader receivedBufferedReader = null;
    public static void main(String[] args) {
        // 新增连接到服务器
        startSocket();
    }

    /**
     * 启动socket连接
     */
    private static void startSocket() {
        try {
            socket = new Socket("127.0.0.1", 2333);
            br = new BufferedReader(new InputStreamReader(System.in));
            writer = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
            receivedBufferedReader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
            new Thread(() -> {
                try {
                    while (true) {
                        Thread.sleep(1000L);
                        String s = receivedBufferedReader.readLine();
                        if (s!=null && !s.equals("")) {
                            System.out.println("receive: " + s);
                        }
                    }
                } catch (IOException | InterruptedException e) {
                    e.printStackTrace();
                }
            }).start();
            while (true) {
                Thread.sleep(1000L);
                if (br.ready()) {
                    writer.write(br.readLine().trim() + "\n");
                    writer.flush();
                }
            }
        } catch (IOException | InterruptedException e) {
            e.printStackTrace();
        } finally {
            try {
                if (receivedBufferedReader != null) {
                    receivedBufferedReader.close();
                }
                if (writer != null) {
                    writer.close();
                }
                if (br != null) {
                    br.close();
                }
                if (socket != null) {
                    socket.close();
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
}

测试一下,我们先运行服务器,再运行客户端。

在客户端控制台下输入测试信息。

从零开始搭建游戏服务器 第一节 创建一个简单的服务器架构

可以成功进行信息交互

总结

本节一共做了这么几件事:

  1. 项目的初步创建,通过build.gradle进行依赖包的管理。
  2. Netty服务器的启动,并且不断监听控制台输入,客户端上行数据的读取。
  3. 编写测试用客户端,与服务器进行数据交互。

下一节将进行注册登录的开发,内容将会比较多,感兴趣的点点关注或者留言评论。文章来源地址https://www.toymoban.com/news/detail-497691.html

到了这里,关于从零开始搭建游戏服务器 第一节 创建一个简单的服务器架构的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包赞助服务器费用

相关文章

  • 从零开始搭建高效的文件服务器:FastDFS与Nginx完美结合,内网穿透实现公网访问

    从零开始搭建高效的文件服务器:FastDFS与Nginx完美结合,内网穿透实现公网访问

    目录 前言 1. 本地搭建FastDFS文件系统 1.1 环境安装 1.2 安装libfastcommon 1.3 安装FastDFS 1.4 配置Tracker 1.5 配置Storage 1.6 测试上传下载 1.7 与Nginx整合 1.8 安装Nginx 1.9 配置Nginx 2. 局域网测试访问FastDFS 3. 安装cpolar内网穿透 4. 配置公网访问地址 5. 固定公网地址 5.1 保留二级子域名 5.2 配置

    2024年02月03日
    浏览(12)
  • 从零开始用Nodejs搭建一个MQTT服务器,并且用stm32通过esp8266进行消息订阅和发布

    从零开始用Nodejs搭建一个MQTT服务器,并且用stm32通过esp8266进行消息订阅和发布

    最近在做一个物联网项目,需要用到服务器进行数据的存储和数据的请求和发送,之前我用过onenet平台上的http服务,虽然能通过get和post请求进行数据的提交和发送,但是平台上的数据发生改变却不能主动推送给esp8266,与我此次的项目不符合,所以pass。然后我了解了下mqtt协

    2024年02月04日
    浏览(12)
  • 服务器从零开始配环境

    服务器从零开始配环境

    宝塔,xftp,xshell 阿里云的话就是在实例那里点远程连接,输入: yum install -y wget wget -O install.sh http://download.bt.cn/install/install_6.0.sh sh install.sh 中途输入y确定安装 安装成功就是这样的页面: 在软件商店下好常用的软件 服务器买的核数小,每次在宝塔安装mysql都会崩,得自己手

    2024年01月22日
    浏览(13)
  • 华为云云服务器评测 | 从零开始:云耀云服务器L实例的全面使用解析指南

    华为云云服务器评测 | 从零开始:云耀云服务器L实例的全面使用解析指南

    本文收录在专栏:#云计算入门与实践 - 华为云 专栏中,本系列博文还在更新中 相关华为云云耀云服务器L实例评测文章列表如下: 华为云云耀云服务器L实例评测 | 从零开始:云耀云服务器L实例的全面使用解析指南 华为云云耀云服务器L实例评测|轻量级应用服务器对决:基

    2024年02月10日
    浏览(33)
  • 从零开始写一个RTSP服务器(一)RTSP协议讲解

    从零开始写一个RTSP服务器(一)RTSP协议讲解

    为什么要写这个系列? 因为我自己在学习rtsp协议想自己从零写一个rtsp服务器的时候,由于rtsp比较复杂,所以觉得这个过程非常的困难,网上许多相关文章或模棱两可,或是复制粘贴。所以想写这样一个系列,来帮助想要学习rtsp协议或者想要从零写一个rtsp服务器的初学者

    2024年04月17日
    浏览(7)
  • 从零开始写一个RTSP服务器(二)RTSP协议的实现

    此系列只追求精简,旨在学习RTSP协议的实现过程,不追求复杂完美,所以这里要实现的RTSP服务器为了简单,实现上同一时间只能有一个客户端,下面开始介绍实现过程 在写一个RTSP服务器之前,我们必须知道一个RTSP服务器最简单的包含两部分,一部分是RTSP的交互,一部分是

    2024年04月17日
    浏览(9)
  • 从零开始实现一个C++高性能服务器框架----环境变量模块

    此项目是根据sylar框架实现,是从零开始重写sylar,也是对sylar丰富与完善 项目地址:https://gitee.com/lzhiqiang1999/server-framework 项目介绍 :实现了一个基于协程的服务器框架,支持多线程、多协程协同调度;支持以异步处理的方式提高服务器性能;封装了网络相关的模块,包括

    2024年02月02日
    浏览(10)
  • 从零开始实现一个C++高性能服务器框架----Hook模块

    此项目是根据sylar框架实现,是从零开始重写sylar,也是对sylar丰富与完善 项目地址:https://gitee.com/lzhiqiang1999/server-framework 项目介绍 :实现了一个基于协程的服务器框架,支持多线程、多协程协同调度;支持以异步处理的方式提高服务器性能;封装了网络相关的模块,包括

    2023年04月09日
    浏览(7)
  • 从零开始实现一个C++高性能服务器框架----Socket模块

    此项目是根据sylar框架实现,是从零开始重写sylar,也是对sylar丰富与完善 项目地址:https://gitee.com/lzhiqiang1999/server-framework 项目介绍 :实现了一个基于协程的服务器框架,支持多线程、多协程协同调度;支持以异步处理的方式提高服务器性能;封装了网络相关的模块,包括

    2023年04月08日
    浏览(11)
  • C++ Webserver从零开始:基础知识(三)——Linux服务器程序框架

    C++ Webserver从零开始:基础知识(三)——Linux服务器程序框架

    目录 前言 一.服务器编程基础框架 C/S模型 主要框架 二.I/O模型 阻塞I/O 非阻塞I/O 异步I/O 三.两种高效的事件处理模式 Reactor Proactor 四.模拟Proactor模式 五.半同步/半异步的并发模式 六.有限状态机 七.其他提高服务器性能的方法 池 数据复制 上下文切换和锁         这一章是

    2024年02月22日
    浏览(8)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包