Netty构建游戏服务器(三)

一,基本方法

上节实现了netty的基本连接,这节加入spring来管理netty,由spring来开启netty服务。

在netty服务器中,我们建立了三个类:HelloServer(程序主入口) , HelloServerInitializer(传输通道初始化),HelloServerHandler(业务控制器)

这三个类中HelloServer中new了一个HelloServerInitializer,在HelloServerInitializer最后又new了一个HelloServerHandler。其中需要new的地方,就是spring要管理的地方。

二,实现源码

1,相关准备

导入需要的JAR包和相关依赖,主要是spring,log4j,netty等,后面的项目源码中包含了所有jar包;

另外你可以使用maven来构建项目,添加各种jar包。

2,主程序TestSrping.java

主程序很简单,就是加载spring配置文件就可以了。

1package com.wayhb; 2 3import org.apache.log4j.Logger; 4import org.springframework.context.ApplicationContext; 5import org.springframework.context.support.ClassPathXmlApplicationContext; 6 7public class TestSpring { 8 9 private static Logger log = Logger.getLogger(TestSpring.class); 10 11 public static void main(String[] args) { 12 // TODO Auto-generated method stub 13 //加载spirng配置文件 14 ApplicationContext context= new ClassPathXmlApplicationContext("server.xml"); 15 16 17 } 18 19}

3,spring的XML配置单server.xml和LOG4J配置文件

server.xml

1<beans xmlns="http://www.springframework.org/schema/beans" 2 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop" 3 xmlns:tx="http://www.springframework.org/schema/tx" xmlns:context="http://www.springframework.org/schema/context" 4 xmlns:lang="http://www.springframework.org/schema/lang" xmlns:util="http://www.springframework.org/schema/util" 5 xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd http://www.springframework.org/schema/lang http://www.springframework.org/schema/lang/spring-lang-3.0.xsd http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-3.0.xsd" 6 default-lazy-init="false" default-autowire="byName"> 7 8<!-- <bean id="user" class="com.wayhb.User"> 9<property name="userid" value="20"></property> 10<property name="username" value="wayhb"></property> 11</bean>--> 12<!--开启注解方式,扫描com.wayhb,com.netty两个包路径--> 13<context:annotation-config/> 14<context:component-scan base-package="com.wayhb,com.netty" /> 15 16<!-- 传统方法配置BEAN 17<bean id="helloServer" class="com.netty.HelloServer" init-method="serverStart"> 18<property name="helloServerInitializer" ref="helloServerInitializer"></property> 19</bean> 20 21<bean id="helloServerInitializer" class="com.netty.HelloServerInitializer"> 22<property name="helloServerHandler" ref="helloServerHandler"></property> 23</bean> 24 25<bean id="helloServerHandler" class="com.netty.HelloServerHandler" scope="prototype"></bean> 26 --> 27 28</beans>

log4j.properties

1 ### 设置### 2#log4j.rootLogger = debug,stdout,D,E 3log4j.rootLogger = debug,stdout 4### 输出信息到控制抬 ### 5log4j.appender.stdout = org.apache.log4j.ConsoleAppender 6log4j.appender.stdout.Target = System.out 7log4j.appender.stdout.layout = org.apache.log4j.PatternLayout 8log4j.appender.stdout.layout.ConversionPattern = [%-5p] %d{yyyy-MM-dd HH:mm:ss,SSS} method:%l%n%m%n 9 10### 输出DEBUG 级别以上的日志到=E://logs/error.log ### 11#log4j.appender.D = org.apache.log4j.DailyRollingFileAppender 12#log4j.appender.D.File = C://logs/log.log 13#log4j.appender.D.Append = true 14#log4j.appender.D.Threshold = DEBUG 15#log4j.appender.D.layout = org.apache.log4j.PatternLayout 16#log4j.appender.D.layout.ConversionPattern = %-d{yyyy-MM-dd HH:mm:ss} [ %t:%r ] - [ %p ] %m%n 17 18### 输出ERROR 级别以上的日志到=E://logs/error.log ### 19#log4j.appender.E = org.apache.log4j.DailyRollingFileAppender 20#log4j.appender.E.File =C://logs/error.log 21#log4j.appender.E.Append = true 22#log4j.appender.E.Threshold = ERROR 23#log4j.appender.E.layout = org.apache.log4j.PatternLayout 24#log4j.appender.E.layout.ConversionPattern = %-d{yyyy-MM-dd HH:mm:ss} [ %t:%r ] - [ %p ] %m%n

其中输出功能是关闭的,需要的话去掉#就可以了

4,HelloServer

1package com.netty; 2 3import io.netty.bootstrap.ServerBootstrap; 4import io.netty.channel.ChannelFuture; 5import io.netty.channel.EventLoopGroup; 6import io.netty.channel.nio.NioEventLoopGroup; 7import io.netty.channel.socket.nio.NioServerSocketChannel; 8 9import javax.annotation.PostConstruct; 10 11import org.apache.log4j.Logger; 12import org.springframework.beans.factory.annotation.Autowired; 13import org.springframework.stereotype.Service; 14//注解方式注入bean,名字是helloServer 15@Service("helloServer") 16public class HelloServer { 17 private static Logger log = Logger.getLogger(HelloServer.class); 18 /** 19 * 服务端监听的端口地址 20 */ 21 private static final int portNumber = 7878; 22 23 //自动装备变量,spring会根据名字或者类型来装备这个变量,注解方式不需要set get方法了 24 @Autowired 25 private HelloServerInitializer helloServerInitializer; 26 27 //程序初始方法入口注解,提示spring这个程序先执行这里 28 @PostConstruct 29 public void serverStart() throws InterruptedException{ 30 EventLoopGroup bossGroup = new NioEventLoopGroup(); 31 EventLoopGroup workerGroup = new NioEventLoopGroup(); 32 try { 33 ServerBootstrap b = new ServerBootstrap(); 34 b.group(bossGroup, workerGroup); 35 b.channel(NioServerSocketChannel.class); 36 b.childHandler(helloServerInitializer); 37 38 // 服务器绑定端口监听 39 ChannelFuture f = b.bind(portNumber).sync(); 40 // 监听服务器关闭监听 41 f.channel().closeFuture().sync(); 42 43 log.info("###########################################"); 44 // 可以简写为 45 /* b.bind(portNumber).sync().channel().closeFuture().sync(); */ 46 } finally { 47 bossGroup.shutdownGracefully(); 48 workerGroup.shutdownGracefully(); 49 } 50 } 51 52}

注解方式更加简便,配置内容少了很多

5,HelloServerInitializer

1package com.netty; 2 3import org.springframework.beans.factory.annotation.Autowired; 4import org.springframework.context.annotation.Scope; 5import org.springframework.stereotype.Service; 6 7import io.netty.channel.ChannelInitializer; 8import io.netty.channel.ChannelPipeline; 9import io.netty.channel.socket.SocketChannel; 10import io.netty.handler.codec.DelimiterBasedFrameDecoder; 11import io.netty.handler.codec.Delimiters; 12import io.netty.handler.codec.string.StringDecoder; 13import io.netty.handler.codec.string.StringEncoder; 14 15@Service("helloServerInitializer") 16public class HelloServerInitializer extends ChannelInitializer<SocketChannel> { 17 18 @Autowired 19 private HelloServerHandler helloServerHandler; 20 21 @Override 22 protected void initChannel(SocketChannel ch) throws Exception { 23 ChannelPipeline pipeline = ch.pipeline(); 24 25 // 以("\n")为结尾分割的 解码器 26 pipeline.addLast("framer", new DelimiterBasedFrameDecoder(8192, Delimiters.lineDelimiter())); 27 28 // 字符串解码 和 编码 29 pipeline.addLast("decoder", new StringDecoder()); 30 pipeline.addLast("encoder", new StringEncoder()); 31 32 // 自己的逻辑Handler 33 pipeline.addLast("handler", helloServerHandler); 34 } 35}

6,HelloServerHandler

1package com.netty; 2 3import java.net.InetAddress; 4 5import org.springframework.context.annotation.Scope; 6import org.springframework.stereotype.Service; 7 8import io.netty.channel.Channel; 9import io.netty.channel.ChannelHandlerContext; 10import io.netty.channel.SimpleChannelInboundHandler; 11import io.netty.channel.group.ChannelGroup; 12import io.netty.channel.group.DefaultChannelGroup; 13import io.netty.util.concurrent.GlobalEventExecutor; 14import io.netty.channel.ChannelHandler.Sharable; 15 16@Service("helloServerHandler") 17@Scope("prototype") 18//特别注意这个注解@Sharable,默认的4版本不能自动导入匹配的包,需要手动加入 19//地址是import io.netty.channel.ChannelHandler.Sharable; 20@Sharable 21public class HelloServerHandler extends SimpleChannelInboundHandler<String> { 22 23 24 public static ChannelGroup channels = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE); 25 26 @Override 27 public void handlerAdded(ChannelHandlerContext ctx) throws Exception { // (2) 28 Channel incoming = ctx.channel(); 29 for (Channel channel : channels) { 30 channel.writeAndFlush("[SERVER] - " + incoming.remoteAddress() + " 加入\n"); 31 } 32 channels.add(ctx.channel()); 33 } 34 35 @Override 36 public void handlerRemoved(ChannelHandlerContext ctx) throws Exception { // (3) 37 Channel incoming = ctx.channel(); 38 for (Channel channel : channels) { 39 channel.writeAndFlush("[SERVER] - " + incoming.remoteAddress() + " 离开\n"); 40 } 41 channels.remove(ctx.channel()); 42 } 43 44 @Override 45 protected void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception { 46 // 收到消息直接打印输出 47 System.out.println(ctx.channel().remoteAddress() + " Say : " + msg); 48 49 // 返回客户端消息 - 我已经接收到了你的消息 50 ctx.writeAndFlush("Received your message !\n"); 51 } 52 53 /* 54 * 55 * 覆盖 channelActive 方法 在channel被启用的时候触发 (在建立连接的时候) 56 * 57 * channelActive 和 channelInActive 在后面的内容中讲述,这里先不做详细的描述 58 * */ 59 @Override 60 public void channelActive(ChannelHandlerContext ctx) throws Exception { 61 62 System.out.println("RamoteAddress : " + ctx.channel().remoteAddress() + " active !"); 63 64 ctx.writeAndFlush( "Welcome to " + InetAddress.getLocalHost().getHostName() + " service!\n"); 65 66 super.channelActive(ctx); 67 } 68}

特别注意这个注解@Sharable,默认的4版本不能自动导入匹配的包,需要手动加入
地址是import io.netty.channel.ChannelHandler.Sharable;

三,运行测试

1,打开TestSpring.java,右键运行,正常情况下服务器会开启;

2,打开上节写的项目,打开HelloClient.java,右键运行;

3,HelloClient.java上再运行一次,出现两个客户端,如果都连接成功,说明项目没有问题。

注意事项:netty4需要@Sharable才可以开启多个handler,所以需要多个连接的handler类上要加入注解@Sharable,该注解自动导入有问题,手动加入包import io.netty.channel.ChannelHandler.Sharable;

四,完整项目下载

工具:eclipse Kepler Service Release 2

链接: http://pan.baidu.com/s/1i4Vcj5r 密码: chzg

点赞
收藏

评论区

加载中...

相关推荐

MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1

文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s

Oracle 分组与拼接字符串同时使用

SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(

MySQL部分从库上面因为大量的临时表tmp_table造成慢查询

背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_

皕杰报表之UUID

​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为

手写Java HashMap源码

HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22

2020年前端实用代码段,为你的工作保驾护航

有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )