java游戏服务器开发之使用工厂模式生成netty

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:
 * <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());
  }

}


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 查看本文章


猜你喜欢

转载自blog.csdn.net/cmqwan/article/details/80676833