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这三个类,因为都是常量,不用太多说明,直接看代码。
ConstantValue
/**
* Copyright (C), 2015-2018
* FileName: ConstantValue
* Author: zhao
* Date: 2018/6/12 11:01
* Description: 静态数据类
* History:
*
* 作者姓名 修改时间 版本号 描述
*/
package com.lizhaoblog.base.constant;
/**
* 〈一句话功能简述〉
* 〈静态数据类〉
*
* @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:
*
* 作者姓名 修改时间 版本号 描述
*/
package com.lizhaoblog.base.exception;
/**
* 〈一句话功能简述〉
* 〈服务启动错误〉
*
* @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:
*
* 作者姓名 修改时间 版本号 描述
*/
package com.lizhaoblog.server.pojo;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import io.netty.channel.EventLoopGroup;
/**
* 〈一句话功能简述〉
* 〈服务的配置内容〉
*
* @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:
*
* 作者姓名 修改时间 版本号 描述
*/
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;
/**
* 〈一句话功能简述〉
* 〈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:
*
* 作者姓名 修改时间 版本号 描述
*/
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;
/**
* 〈一句话功能简述〉
* 〈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 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:
*
* 作者姓名 修改时间 版本号 描述
*/
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;
/**
* 〈一句话功能简述〉
* 〈TcpServerInitializer请求处理器〉
*
* @author zhao
* @date 2018/6/12 15:00
* @since 1.0.0
*/
public class TcpServerStringInitializer extends ChannelInitializer {
@Override
protected void initChannel(SocketChannel ch) {
ChannelPipeline pipeline = ch.pipeline();
pipeline.addLast("decoder", new StringDecoder());
pipeline.addLast("encoder", new StringEncoder());
pipeline.addLast(new TcpMessageStringHandler());
}
}
TcpMessageStringHandler
/**
* Copyright (C), 2015-2018
* FileName: TcpMessageStringHandler
* Author: zhao
* Date: 2018/6/12 15:01
* Description: tcp消息处理
* History:
*
* 作者姓名 修改时间 版本号 描述
*/
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;
/**
* 〈一句话功能简述〉
* 〈tcp消息处理〉
*
* @author zhao
* @date 2018/6/12 15:01
* @since 1.0.0
*/
public class TcpMessageStringHandler extends SimpleChannelInboundHandler {
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游戏服务器开发的相关知识 676231564