java游戏服务器开发之使用工厂模式生成netty
在写netty的会发现很多东西是重复的,既然是重复的内容,我们其实可以把部分内容抽象出来写好,然后具体的内容就交到具体实现里面编写
来看下这次代码的更新情况,添加了这些内容
base
constant
ConstantValue 存放系统常量
exception
ServerErrException 服务启动错误
factory
ServerBootstrapFactory Bootstrap工厂类
ServerChannelFactory ServerChannel的工厂类
server
channel
tcp
str
TcpMessageStringHandler Channel对应的Handler处理器
TcpServerStringInitializer Channel的具体实现
pojo
ServerConfig 服务的配置内容
主要的内容是在ServerBootstrapFactory和ServerChannelFactory之中,其他的基本是为这2个类中出现的,但是需要用到的一些常量。
先看下ConstantValue,ServerErrException和ServerConfig这三个类,因为都是常量,不用太多说明,直接看代码。
ServerConfig
最后就是写具体的handler
后续代码添加,不好阅读的话,可以查看分支netty-factory
可以加qq群一起探讨Java游戏服务器开发的相关知识 676231524
在写netty的会发现很多东西是重复的,既然是重复的内容,我们其实可以把部分内容抽象出来写好,然后具体的内容就交到具体实现里面编写
来看下这次代码的更新情况,添加了这些内容
base
constant
ConstantValue 存放系统常量
exception
ServerErrException 服务启动错误
factory
ServerBootstrapFactory Bootstrap工厂类
ServerChannelFactory ServerChannel的工厂类
server
channel
tcp
str
TcpMessageStringHandler Channel对应的Handler处理器
TcpServerStringInitializer Channel的具体实现
pojo
ServerConfig 服务的配置内容
主要的内容是在ServerBootstrapFactory和ServerChannelFactory之中,其他的基本是为这2个类中出现的,但是需要用到的一些常量。
先看下ConstantValue,ServerErrException和ServerConfig这三个类,因为都是常量,不用太多说明,直接看代码。
ConstantValue
/** * Copyright (C), 2015-2018 * FileName: ConstantValue * Author: zhao * Date: 2018/6/12 11:01 * Description: 静态数据类 * History: * <author> <time> <version> <desc> * 作者姓名 修改时间 版本号 描述 */ package com.lizhaoblog.base.constant; /** * 〈一句话功能简述〉<br> * 〈静态数据类〉 * * @author zhao * @date 2018/6/12 * @since 1.0.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_CUSTOM = "CUSTOM"; public static final String PROTOCOL_TYPE_WEBSOCKET = "WEBSOCKET"; private ConstantValue() { } }
ServerErrException
/** * Copyright (C), 2015-2018 * FileName: ServerErrException * Author: zhao * Date: 2018/6/12 14:36 * Description: 服务启动错误 * History: * <author> <time> <version> <desc> * 作者姓名 修改时间 版本号 描述 */ package com.lizhaoblog.base.exception; /** * 〈一句话功能简述〉<br> * 〈服务启动错误〉 * * @author zhao * @date 2018/6/12 14:36 * @since 1.0.0 */ public class ServerErrException extends Exception { private String errMsg; public ServerErrException(String errMsg) { super(errMsg); this.errMsg = errMsg; } public ServerErrException(Throwable cause) { super(cause); } }
ServerConfig
/** * Copyright (C), 2015-2018 * FileName: ServerConfig * Author: zhao * Date: 2018/6/12 11:16 * Description: 服务的配置内容 * History: * <author> <time> <version> <desc> * 作者姓名 修改时间 版本号 描述 */ package com.lizhaoblog.server.pojo; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import io.netty.channel.EventLoopGroup; /** * 〈一句话功能简述〉<br> * 〈服务的配置内容〉 * * @author zhao * @date 2018/6/12 11:16 * @since 1.0.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这个类,是根据我们配置的CHANNEL_TYPE(NIO,OIO)来创建不同的ServerBootstrap。
/** * Copyright (C), 2015-2018 * FileName: ServerBootstrapFactory * Author: zhao * Date: 2018/6/12 10:56 * Description: Bootstrap工厂类 * History: * <author> <time> <version> <desc> * 作者姓名 修改时间 版本号 描述 */ package com.lizhaoblog.base.factory; import com.lizhaoblog.base.constant.ConstantValue; import com.lizhaoblog.base.exception.ServerErrException; import com.lizhaoblog.server.pojo.ServerConfig; 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; /** * 〈一句话功能简述〉<br> * 〈Bootstrap工厂类〉 * * @author zhao * @date 2018/6/12 * @since 1.0.0 */ public class ServerBootstrapFactory { private ServerBootstrapFactory() { } public static ServerBootstrap createServerBootstrap() throws ServerErrException { 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 ServerErrException( "Failed to create ServerBootstrap, " +ServerConfig.getInstance().getChannelType() + " not supported!"); } } }
然后看看ServerChannelFactory里面的逻辑,其实也就几句主逻辑,
获取配置的ip
通过ServerBootstrapFactory获取ServerBootstrap
设置ServerBootstrap的handler(通过getChildHandler()获取)
getChildHandler()通过设置的PROTOCOL_TYPE选择不同的handler
serverBootstrap绑定ip
*贯穿的其他代码是用来判断异常情况的
具体代码如下
/** * Copyright (C), 2015-2018 * FileName: ServerChannelFactory * Author: zhao * Date: 2018/6/12 14:29 * Description: ServerChannel的工厂类 * History: * <author> <time> <version> <desc> * 作者姓名 修改时间 版本号 描述 */ package com.lizhaoblog.base.factory; import com.lizhaoblog.base.constant.ConstantValue; import com.lizhaoblog.base.exception.ServerErrException; import com.lizhaoblog.server.channel.tcp.str.TcpServerStringInitializer; import com.lizhaoblog.server.pojo.ServerConfig; import org.slf4j.Logger; import org.slf4j.LoggerFactory; 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; /** * 〈一句话功能简述〉<br> * 〈ServerChannel的工厂类〉 * * @author zhao * @date 2018/6/12 14:29 * @since 1.0.0 */ public class ServerChannelFactory { private static final Logger logger = LoggerFactory.getLogger(ServerChannelFactory.class); public static Channel createAcceptorChannel() throws ServerErrException { Integer port = ServerConfig.getInstance().getPort(); final ServerBootstrap serverBootstrap = ServerBootstrapFactory.createServerBootstrap(); serverBootstrap.childHandler(getChildHandler()); // serverBootstrap.childHandler() 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 ServerErrException(errMsg); } // java.net.BindException: Address already in use: bind //接下来就是创建一个 } catch (Exception e) { logger.debug(port + "is bind"); throw new ServerErrException(e); } } private static ChannelInitializer<SocketChannel> getChildHandler() throws ServerErrException { 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_CUSTOM.equals(protocolType)) { } else { } String errMsg = "undefined protocol:" + protocolType + "!"; throw new ServerErrException(errMsg); } }
最后就是写具体的handler
TcpServerStringInitializer
/** * Copyright (C), 2015-2018 * FileName: TcpServerStringInitializer * Author: zhao * Date: 2018/6/12 15:00 * Description: TcpServerInitializer请求处理器 * History: * <author> <time> <version> <desc> * 作者姓名 修改时间 版本号 描述 */ package com.lizhaoblog.server.channel.tcp.str; import io.netty.channel.ChannelInitializer; import io.netty.channel.ChannelPipeline; import io.netty.channel.socket.SocketChannel; import io.netty.handler.codec.string.StringDecoder; import io.netty.handler.codec.string.StringEncoder; /** * 〈一句话功能简述〉<br> * 〈TcpServerInitializer请求处理器〉 * * @author zhao * @date 2018/6/12 15:00 * @since 1.0.0 */ public class TcpServerStringInitializer extends ChannelInitializer<SocketChannel> { @Override protected void initChannel(SocketChannel ch) { ChannelPipeline pipeline = ch.pipeline(); pipeline.addLast("decoder", new StringDecoder()); pipeline.addLast("encoder", new StringEncoder()); pipeline.addLast(new TcpMessageStringHandler()); } }
/** * Copyright (C), 2015-2018 * FileName: TcpMessageStringHandler * Author: zhao * Date: 2018/6/12 15:01 * Description: tcp消息处理 * History: * <author> <time> <version> <desc> * 作者姓名 修改时间 版本号 描述 */ package com.lizhaoblog.server.channel.tcp.str; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.SimpleChannelInboundHandler; /** * 〈一句话功能简述〉<br> * 〈tcp消息处理〉 * * @author zhao * @date 2018/6/12 15:01 * @since 1.0.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 = "小李,我是服务器,我收到你的信息了。"; 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); } }上面的代码在码云上 https://gitee.com/lizhaoandroid/JgServer
后续代码添加,不好阅读的话,可以查看分支netty-factory
可以加qq群一起探讨Java游戏服务器开发的相关知识 676231524
扫描二维码关注公众号,回复:
1661539 查看本文章