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

目录

    • 引言
    • 技术选型
    • 正文
      • 创建基础架构
        • IDEA创建项目
        • 添加Netty监听端口
        • 编写客户端进行测试
    • 总结

引言

由于现在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'//springimplementation group: 'org.springframework', name: 'spring-context', version: '6.0.6'//logimplementation 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'//lombokcompileOnly 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) {// 初始化SpringAnnotationConfigApplicationContext springContext = new AnnotationConfigApplicationContext(GameBeanConfiguration.class);springContext.start();log.info("server start!");}
}

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

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

修改main方法如下:

    public static void main(String[] args) {// 初始化SpringAnnotationConfigApplicationContext 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> {/*** 读取数据*/@Overrideprotected 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) {// 初始化SpringAnnotationConfigApplicationContext 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>() {@Overrideprotected 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. 编写测试用客户端,与服务器进行数据交互。

下一节将进行注册登录的开发,内容将会比较多,感兴趣的点点关注或者留言评论。

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

如若内容造成侵权/违法违规/事实不符,请联系瞎胡闹网进行投诉反馈,一经查实,立即删除!

相关文章

鹅厂公开课:游戏服务器,了解一下?

讲师简介&#xff1a;张正&#xff0c;腾讯互娱北极光工作室群专家工程师&#xff0c;拥有12年游戏后台开发经验&#xff0c;主导和参与了《天涯明月刀》、《轩辕传奇》等自研大型MMORPG项目的后台开发&#xff0c;现担任《天涯明月刀》项目后台技术总监&#xff0c;北极光后台…

游戏服务器的那些事儿

游戏程序开发有两个大方向&#xff0c;包括前端和后端。其中&#xff0c;前端是指客户端方面&#xff0c;包括PC、手机和平板上面的可视化图形技术。后端则偏向于服务器&#xff0c;即用户不可见的部分。本文将通过游戏服务器的起源、功能特点、分类和发展历史&#xff0c;以及…

〔017〕Stable Diffusion 之 常用模型推荐 篇

✨ 目录 &#x1f388; 模型网站&#x1f388; 仿真系列&#x1f388; 国风系列&#x1f388; 卡通动漫系列&#x1f388; 3D系列&#x1f388; 一些好用的lora模型 &#x1f388; 模型网站 由于现在大模型超级多&#xff0c;导致每种画风的模型太多&#xff0c;那么如何选择最…

通达信形态匹配选股,不会编写指标公式也可以形态选股

编写指标公式匹配技术形态难度比较高&#xff0c;公式也很复杂&#xff0c;新手往往难以掌握&#xff0c;即使是老手也常常感到困难。之前编写了N字形态、W底&#xff08;双底&#xff09;、头肩底&#xff0c;虽然成功完成&#xff0c;但工作量巨大&#xff0c;公式理解起来也…

python函数学习

def add(num1,num2):resultnum1num2print(f"函数add输出的结果是{result}")return result resultadd(int(num1), int(num2)) print(f"调用def add(num1,num2):这个函数最终返回的结果是: {result}")# 函数返回值 ②无返回值&#xff08;也就是说是返回值类…

【python】python智能停车场数据分析(代码+数据集)【独一无二】

&#x1f449;博__主&#x1f448;&#xff1a;米码收割机 &#x1f449;技__能&#x1f448;&#xff1a;C/Python语言 &#x1f449;公众号&#x1f448;&#xff1a;测试开发自动化【获取源码商业合作】 &#x1f449;荣__誉&#x1f448;&#xff1a;阿里云博客专家博主、5…

Linux驱动之platform设备驱动

目录 前言 一、Linux驱动的分离与分层 二、开发环境 三、驱动程序编写 3.2 platform 驱动模块程序 3.3 测试app程序 四、运行测试 4.1 编译 4.2 运行测试 前言 前面几章编写的设备驱动都非常的简单&#xff0c;都是对 IO进行最简单的读写操作。像 I2C、SPI、 LCD 等这…

嵌入式系统启动文件及其流程的理解

简单概括总结嵌入式上电启动顺序 启动第1步&#xff1a;加载BIOS 当你打开计算机电源&#xff0c;计算机会首先加载BIOS信息&#xff0c;BIOS信息是如此的重要&#xff0c;以至于计算机必须在最开始就找到它。这是因为BIOS中包含了CPU的相关信息、设备启动顺序信息、硬盘信息、…

python使用win32库模拟拖拽文件发给指定窗口

最近要用python模拟人的操作给窗口发送拖拽文件的消息&#xff0c;网上搜了一大圈也没搜到现成可用的代码。幸好以前做过vc开发&#xff0c;熟悉点win32编程&#xff0c;于是装上vs和msdn&#xff0c;从消息WM_DROPFILES查起&#xff0c;慢慢得实现了这个功能。 WM_DROPFILES是…

TMS FNC UI Pack 3.1Crack,四个框架和五个操作系统的强大控件

TMS FNC UI Pack是TMS软件的产品&#xff0c;具有针对四个框架和五个操作系统的强大控件和丰富功能。 TMS FNC控件可以在以下框架中同时使用&#xff1a; TMS FNC控件可以在以下操作系统/浏览器中同时使用&#xff1a; TMS FNC控件可在以下IDE中同时使用&#xff1a; TMS FNC U…

TMS FNC组件crack,TMS FNC跨平台的图形组件

TMS FNC组件crack,TMS FNC跨平台的图形组件 TMS FNC Chart 为公司、统计、财政和科学信息开发的完全跨平台的图形组件。 TMS FNC 组件可以在这些框架上同时使用。 TMS FNC 组件可以在这些操作系统/浏览器上同时使用。 TMS FNC 控件可以在这些 IDE 上同时使用。 TMS FNC图表应用…

开源代码扫描工具 Socket新增对 Go 生态系统的支持

导读继日前宣布完成 2000 万美元的 A 轮融资后&#xff0c;开源代码扫描工具 Socket 紧接着宣布新增了对 Go 语言的支持&#xff1b;此前其仅支持 JavaScript 和 Python 语言。 “在过去的几个月中&#xff0c;我们观察到针对 Golang 的供应链攻击有所增加。意识到这种迫在眉睫…

Python数据分析的bs4用法

在爬虫的世界里&#xff0c;数据解析占用很重要的位置 数据解析原理&#xff1a; 标签定位提取标签、标签属性中存储的数据值 bs4数据解析原理&#xff1a; 1.实例化一个BeautifulSoup对象&#xff0c;并且将页面原码数据加载到该对象中2.通过调用BeautifulSoup对象中相关的…

BS架构通信原理

BS架构通信原理 1.关于域名 https://www.baidu.com/&#xff08;网址&#xff09; www.baidu.com(是一个域名) 在浏览器地址栏上输入域名&#xff0c;回车后&#xff0c;域名解析器会将域名解析出来一个具体的IP地址和端口号等。 该地址也可以通过DOS窗口来显示&#xff08;…

Python爬虫:bs4解析

Python爬虫&#xff1a;bs4解析 html语法什么是bs4bs4安装从bs4中查找数据的方法bs4的基本使用实例&#xff1a;使用bs4爬取优美图库图片思路代码 html语法 <标签 属性“值” 属性“值”>被标记内容 </标签>什么是bs4 bs4全称&#xff1a;beautifulsoup4&#xf…

合泰BS8116A-3触摸芯片开发踩坑指南

一、硬件说明 引脚图&#xff1a; 接线&#xff1a; 说明&#xff1a;由于用到了唤醒检测&#xff0c;所以KEY16引脚用作IRQ中断唤醒功能&#xff0c;未使用引脚拉低。 二、IIC配置说明 1、最大波特率&#xff1a; 实际单片机配置最好不要设置波特率100Khz容易出错&#xff…

BS4基本用法

1.找米下锅&#xff0c;安装bs4库 pip install BeautifulSoup 2.此物何用&#xff0c;BS4作用 Beautiful Soup库是解析、遍历、维护“html标签树”的功能库。在爬虫中用于解析数据。 3.bs4标签元素&#xff0c;解析其组成&#xff0c;方可庖丁解牛&#xff0c;游刃有余 by…

XPath和bs4

XPath XPath 是一门在 XML 文档中查找信息的语言。XPath 用于在 XML 文档中通过元素和属性进行导航 使用方法&#xff1a; 使用前要把response.text通过etree.HTML()转换为对应的格式&#xff0c;再通过 变量名.xpath(xpath)截取内容 responserequests.get(url,headershead…

Python爬虫 BeautifulSoup(bs4)-- bs4介绍、安装bs4、bs4基础语法

1. BeautifulSoup简介 BeautifulSoup简称&#xff1a; bs4 。什么是BeatifulSoup&#xff1f; BeautifulSoup&#xff0c;和lxml一样&#xff0c;是一个html的解析器&#xff0c;主要功能也是解析和提取数据 。优缺点&#xff1f; 缺点&#xff1a;效率没有lxml的效率高优点&a…

Python爬虫 之数据解析之bs4

数据解析之bs4 一、bs4进行数据解析二、bs4库和lxml库的安装三、BeautifulSoup对象四、项目实例 一、bs4进行数据解析 1、数据解析的原理 ① 标签定位。 ② 提取标签、标签属性中存储的数据值。 2、bs4数据解析的原理 ① 实例化一个BeautifulSoup对象&#xff0c;并且将网页源…