上一章我们已经完成了一个基本netty的通信,但是netty的启动很多代码都是重复的,所以我们使用工厂模式来生成不同的ServerBootstrap。
首先创建一个新的组件core组件,和common组件,主要用于netty通信和工具类,从server中分离出来没有本质的区别,就是希望可以把功能分散在不同的组件中,后续方便多人进行协同开发(如果有多人的话)。
eternity-server的pom文件中增加依赖:
<dependencies>
<dependency>
<groupId>com.loveprogrammer</groupId>
<artifactId>eternity-core</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
</dependencies>
eternity-core的pom文件中增加依赖:
<dependencies>
<dependency>
<groupId>com.loveprogrammer</groupId>
<artifactId>eternity-common</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
</dependencies>
公共变量
ConstantValue.java common:src/../constants
package com.loveprogrammer.constants;
/**
* @ClassName ConstantValue
* @Description 静态数据类
* @Author admin
* @Date 2024/1/30 10:01
* @Version 1.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_PROTOBUF = "PROTOBUF";
public static final String PROTOCOL_TYPE_WEBSOCKET = "WEBSOCKET";
public static final String MESSAGE_TYPE_STRING = "STRING";
public static final String MESSAGE_TYPE_BYTE = "BYTE";
public static final String PROJECT_CHARSET = "UTF-8";
public static final int MESSAGE_CODEC_MAX_FRAME_LENGTH = 1024 * 1024;
public static final int MESSAGE_CODEC_LENGTH_FIELD_LENGTH = 4;
public static final int MESSAGE_CODEC_LENGTH_FIELD_OFFSET = 2;
public static final int MESSAGE_CODEC_LENGTH_ADJUSTMENT = 0;
public static final int MESSAGE_CODEC_INITIAL_BYTES_TO_STRIP = 0;
/**
* 登录和下线队列
*/
public static final int QUEUE_LOGIN_LOGOUT = 1;
/**
* 业务队列
*/
public static final int QUEUE_LOGIC = 2;
private ConstantValue() {
}
}
ServerException.java common:src/../exception
public class ServerException extends Exception{
private String errMsg;
public ServerException(String errMsg) {
super(errMsg);
this.errMsg = errMsg;
}
public ServerException(Throwable cause) {
super(cause);
}
}
下面是core中的新增代码
ServerConfig.java
/**
* @ClassName ServerConfig
* @Description 服务基本配置类
* @Author admin
* @Date 2024/2/4 15:12
* @Version 1.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.java
package com.loveprogrammer.base.factory;
import com.loveprogrammer.base.bean.ServerConfig;
import com.loveprogrammer.constants.ConstantValue;
import com.loveprogrammer.exception.ServerException;
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;
/**
* @ClassName ServerBootstrapFactory
* @Description Bootstrap工厂类
* @Author admin
* @Date 2024/2/4 15:13
* @Version 1.0
*/
public class ServerBootstrapFactory {
private ServerBootstrapFactory() {
}
public static ServerBootstrap createServerBootstrap() throws ServerException {
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 ServerException(
"Failed to create ServerBootstrap, " +ServerConfig.getInstance().getChannelType() + " not supported!");
}
}
}
ServerChannelFactory.java
package com.loveprogrammer.base.factory;
import com.loveprogrammer.base.bean.ServerConfig;
import com.loveprogrammer.base.network.channel.tcp.str.TcpServerStringInitializer;
import com.loveprogrammer.constants.ConstantValue;
import com.loveprogrammer.exception.ServerException;
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;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* @ClassName ServerChannelFactory
* @Description channel工厂类
* @Author admin
* @Date 2024/2/4 15:13
* @Version 1.0
*/
public class ServerChannelFactory {
private static final Logger logger = LoggerFactory.getLogger(ServerChannelFactory.class);
public static Channel createAcceptorChannel() throws ServerException {
Integer port = ServerConfig.getInstance().getPort();
final ServerBootstrap serverBootstrap = ServerBootstrapFactory.createServerBootstrap();
serverBootstrap.childHandler(getChildHandler());
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 ServerException(errMsg);
}
} catch (Exception e) {
logger.debug(port + "is bind");
throw new ServerException(e);
}
}
private static ChannelInitializer<SocketChannel> getChildHandler() throws ServerException {
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_PROTOBUF.equals(protocolType)) {
} else {
}
String errMsg = "undefined protocol:" + protocolType + "!";
throw new ServerException(errMsg);
}
}
TcpMessageStringHandler.java
package com.loveprogrammer.base.network.channel.tcp.str;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* @ClassName TcpMessageStringHandler
* @Description tcp消息处理类
* @Author admin
* @Date 2024/2/4 15:16
* @Version 1.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 = "我是服务器,我收到了你的信息:" + msg;
result += "\r\n";
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);
}
}
TcpServerStringInitializer.java
package com.loveprogrammer.base.network.channel.tcp.str;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.socket.SocketChannel;
import io.netty.handler.codec.DelimiterBasedFrameDecoder;
import io.netty.handler.codec.Delimiters;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;
/**
* @ClassName TcpServerStringInitializer
* @Description TODO
* @Author admin
* @Date 2024/2/4 15:15
* @Version 1.0
*/
public class TcpServerStringInitializer extends ChannelInitializer<SocketChannel> {
@Override
protected void initChannel(SocketChannel ch) {
ChannelPipeline pipeline = ch.pipeline();
pipeline.addLast("framer",new DelimiterBasedFrameDecoder(8192, Delimiters.lineDelimiter()));
pipeline.addLast("decoder", new StringDecoder());
pipeline.addLast("encoder", new StringEncoder());
pipeline.addLast(new TcpMessageStringHandler());
}
}
修改启动类EternityServerMain :
package com.loveprogrammer;
import com.loveprogrammer.base.factory.ServerBootstrapFactory;
import com.loveprogrammer.base.factory.ServerChannelFactory;
import com.loveprogrammer.exception.ServerException;
import com.loveprogrammer.netty.simple.SocketServer;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.Channel;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Hello world!
*
*/
public class EternityServerMain
{
// 为了保证使用时,不需要每次都去创建logger 对象,我们声明静态常量
public static final Logger LOGGER = LoggerFactory.getLogger(EternityServerMain.class);
public static void main( String[] args )
{
LOGGER.info( "Hello World!" );
// 最基本的启动方法
// try {
// LOGGER.info("开始启动Socket服务器...");
// new SocketServer().run();
// } catch (Exception e) {
// LOGGER.error( "服务器启动失败",e);
// }
// 工厂模式启动方法
try {
Channel channel = ServerChannelFactory.createAcceptorChannel();
channel.closeFuture().sync();
} catch (Exception e) {
LOGGER.error( "服务器启动失败",e);
}
}
}
全部源码详见:
gitee : eternity-online: 多人在线mmo游戏 - Gitee.com
分支:step-02
上一章:
从零开始手写mmo游戏从框架到爆炸(一)— 开发环境-CSDN博客
下一章:从零开始手写mmo游戏从框架到爆炸(三)— 服务启动接口与网络事件监听器-CSDN博客
参考:
java游戏服务器开发: https://blog.csdn.net/cmqwan/category_7690685.html