开网上授课的网站应该怎么做,网络文化经营许可证申请,环境文化建设方案网站,域名注册商排名目录引言技术选型正文创建基础架构IDEA创建项目添加Netty监听端口编写客户端进行测试总结引言
由于现在java web太卷了#xff0c;所以各位同行可以考虑换一个赛道#xff0c;做游戏还是很开心的。
本篇教程给新人用于学习游戏服务器的基本知识#xff0c;给新人们一些学习…
目录引言技术选型正文创建基础架构IDEA创建项目添加Netty监听端口编写客户端进行测试总结引言
由于现在java web太卷了所以各位同行可以考虑换一个赛道做游戏还是很开心的。
本篇教程给新人用于学习游戏服务器的基本知识给新人们一些学习方向有什么错误的地方欢迎各位同行进行讨论。
技术选型
本篇教程预计使用JavaRedisMongo
正文
本着先完成再完美的原则从最简单的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.36implementation group: ch.qos.logback, name: logback-core, version: 1.2.11implementation group: ch.qos.logback, name: logback-access, version: 1.2.11implementation 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 SimpleChannelInboundHandlerObject {/*** 读取数据*/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 ChannelInitializerSocketChannel() {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();}}}
}测试一下我们先运行服务器再运行客户端。
在客户端控制台下输入测试信息。 可以成功进行信息交互
总结
本节一共做了这么几件事
项目的初步创建通过build.gradle进行依赖包的管理。Netty服务器的启动并且不断监听控制台输入客户端上行数据的读取。编写测试用客户端与服务器进行数据交互。
下一节将进行注册登录的开发内容将会比较多感兴趣的点点关注或者留言评论。