java游戏服务器开发之四--通讯框架netty

前言,

说明

 

引入netty的pom

		<!-- netty -->
		<dependency>
		  <groupId>io.netty</groupId>
		  <artifactId>netty-all</artifactId>
		  <version>4.1.6.Final</version>
		</dependency>

然后下面要写四个类,
SocketServer  ---服务器启动类
SocketServerHandler ---服务器消息处理
SocketClientHandlerTest ---测试使用的的客户端消息处理
SocketClientTest ---测试使用的客户端启动类



看这几个类,handle是启动类中的一个小部件,这个是2者的关系,具体代码如下

SocketServer 

/**
 * Copyright (C), 2015-2018
 * FileName: SocketServer
 * Author:   zhao
 * Date:     2018/6/10 21:26
 * Description: netty服务器
 * History:
 * <author>          <time>          <version>          <desc>
 * 作者姓名           修改时间           版本号              描述
 */
package com.lizhaoblog.net;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
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;

/**
 * 〈一句话功能简述〉<br>
 * 〈netty服务器〉
 *
 * @author zhao
 * @create 2018/6/10
 * @since 1.0.0
 */
public class SocketServer {
  private static final Logger logger = LoggerFactory.getLogger(SocketServer.class);
  private static final String IP = "127.0.0.1";
  private static final int PORT = 8088;

  /**
   * 分配用于处理业务的线程组数量
   */
  private static final int BIS_GROUP_SIZE = Runtime.getRuntime().availableProcessors() * 2;
  /**
   * 每个线程组中线程的数量
   */
  private static final int WORK_GROUP_SIZE = 4;

  private static EventLoopGroup bossGroup = new NioEventLoopGroup(BIS_GROUP_SIZE);
  private static EventLoopGroup workerGroup = new NioEventLoopGroup(WORK_GROUP_SIZE);

  public  void run() throws Exception {
    ServerBootstrap bootstrap = new ServerBootstrap();
    bootstrap.group(bossGroup, workerGroup);
    bootstrap.channel(NioServerSocketChannel.class);
    bootstrap.childHandler(new ChannelInitializer<SocketChannel>() {
      @Override
      protected void initChannel(SocketChannel ch) throws Exception {
        ChannelPipeline pipeline = ch.pipeline();
        // 以("\n")为结尾分割的 解码器
        pipeline.addLast("framer", new DelimiterBasedFrameDecoder(8192, Delimiters.lineDelimiter()));
        pipeline.addLast("decoder", new StringDecoder());
        pipeline.addLast("encoder", new StringEncoder());
        pipeline.addLast(new SocketServerHandler());
      }
    });
    bootstrap.bind(IP, PORT).sync();
    logger.info("Socket服务器已启动完成");
  }

  protected static void shutdown() {
    bossGroup.shutdownGracefully();
    workerGroup.shutdownGracefully();
  }

  public static void main(String[] args) throws Exception {
    logger.info("开始启动Socket服务器...");
    new SocketServer().run();
  }
}

SocketServerHandler

/**
 * Copyright (C), 2015-2018
 * FileName: SocketServerHandler
 * Author:   zhao
 * Date:     2018/6/10 21:27
 * Description: SocketServer的消息处理类
 * History:
 * <author>          <time>          <version>          <desc>
 * 作者姓名           修改时间           版本号              描述
 */
package com.lizhaoblog.net;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;

/**
 * 〈一句话功能简述〉<br>
 * 〈SocketServer的消息处理类〉
 *
 * @author zhao
 * @create 2018/6/10
 * @since 1.0.0
 */
public class SocketServerHandler extends SimpleChannelInboundHandler<String> {
  private static final Logger logger = LoggerFactory.getLogger(SocketServer.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 = "小李,我是服务器,我收到你的信息了。";

    //这行很重要,StringDecoder以这个作为消息分割,
    // 如果没有换行符的话,服务端就没办法接受到
    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);
  }
}

SocketClientHandlerTest

/**
 * Copyright (C), 2015-2018
 * FileName: SocketClientHandlerTest
 * Author:   zhao
 * Date:     2018/6/10 21:36
 * Description: SocketClient的测试类
 * History:
 * <author>          <time>          <version>          <desc>
 * 作者姓名           修改时间           版本号              描述
 */
package com.lizhaoblog.net;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;

/**
 * 〈一句话功能简述〉<br>
 * 〈SocketClient的测试类〉
 *
 * @author zhao
 * @create 2018/6/10
 * @since 1.0.0
 */
public class SocketClientHandlerTest extends SimpleChannelInboundHandler<String> {
  private static final Logger logger = LoggerFactory.getLogger(SocketClientTest.class);

  @Override
  public void exceptionCaught(ChannelHandlerContext arg0, Throwable arg1) {
    logger.info("异常发生", arg1);
  }

  @Override
  public void channelRead(ChannelHandlerContext arg0, Object msg) throws Exception {
    super.channelRead(arg0, msg);
  }

  @Override
  protected void channelRead0(ChannelHandlerContext arg0, String data) {
    logger.info("数据内容:data=" + data);
  }

  @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);
  }
}

SocketClientTest

/**
 * Copyright (C), 2015-2018
 * FileName: SocketServerTest
 * Author:   zhao
 * Date:     2018/6/10 21:35
 * Description: SocketServer的测试类
 * History:
 * <author>          <time>          <version>          <desc>
 * 作者姓名           修改时间           版本号              描述
 */
package com.lizhaoblog.net;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import io.netty.bootstrap.Bootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioSocketChannel;
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;

/**
 * 〈一句话功能简述〉<br>
 * 〈SocketServer的测试类〉
 *
 * @author zhao
 * @create 2018/6/10
 * @since 1.0.0
 */
public class SocketClientTest {
  private static final Logger logger = LoggerFactory.getLogger(SocketClientTest.class);
  private static final String IP = "127.0.0.1";
  private static final int PORT = 8088;

  private static EventLoopGroup group = new NioEventLoopGroup();

  @SuppressWarnings("rawtypes")
  protected static void run() throws Exception {
    Bootstrap bootstrap = new Bootstrap();
    bootstrap.group(group);
    bootstrap.channel(NioSocketChannel.class);
    bootstrap.handler(new ChannelInitializer() {
      @Override
      protected void initChannel(Channel ch) throws Exception {
        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 SocketClientHandlerTest());
      }
    });

    // 连接服务端
    ChannelFuture channelFuture = bootstrap.connect(IP, PORT).sync();

    String msg = "小王,我是客户端";
    //这行很重要,StringDecoder以这个作为消息分割,
    // 如果没有换行符的话,服务端就没办法接受到
    msg += "\r\n";
    channelFuture.channel().writeAndFlush(msg);
    logger.info("向Socket服务器发送数据:" + msg);

    channelFuture.channel().closeFuture().sync();

  }

  public static void main(String[] args) {
    logger.info("开始连接Socket服务器...");
    try {
      run();
    } catch (Exception e) {
      e.printStackTrace();
    } finally {
      group.shutdownGracefully();
    }
  }
}

测试方法:先启动Main函数中的main或者SocketServer中的main函数,2者一样,然后启动SocketClientTest中的函数,能做到收发就ok了,像这样

服务器
			2018-06-10 21:48:45.763 INFO  com.lizhaoblog.net.SocketServer - Socket服务器已启动完成
			2018-06-10 21:48:51.277 DEBUG io.netty.buffer.AbstractByteBuf - -Dio.netty.buffer.bytebuf.checkAccessible: true
			2018-06-10 21:48:51.281 DEBUG io.netty.util.ResourceLeakDetectorFactory - Loaded default ResourceLeakDetector: io.netty.util.ResourceLeakDetector@4a38e446
			2018-06-10 21:48:51.291 INFO  com.lizhaoblog.net.SocketServer - 建立连接
			2018-06-10 21:48:51.303 DEBUG io.netty.util.Recycler - -Dio.netty.recycler.maxCapacityPerThread: 32768
			2018-06-10 21:48:51.303 DEBUG io.netty.util.Recycler - -Dio.netty.recycler.maxSharedCapacityFactor: 2
			2018-06-10 21:48:51.303 DEBUG io.netty.util.Recycler - -Dio.netty.recycler.linkCapacity: 16
			2018-06-10 21:48:51.303 DEBUG io.netty.util.Recycler - -Dio.netty.recycler.ratio: 8
			2018-06-10 21:48:51.329 INFO  com.lizhaoblog.net.SocketServer - 数据内容:data=小王,我是客户端
		客户端
			2018-06-10 21:48:51.246 INFO  com.lizhaoblog.net.SocketClientTest - 客户端连接建立
			2018-06-10 21:48:51.251 DEBUG io.netty.util.Recycler - -Dio.netty.recycler.maxCapacityPerThread: 32768
			2018-06-10 21:48:51.251 DEBUG io.netty.util.Recycler - -Dio.netty.recycler.maxSharedCapacityFactor: 2
			2018-06-10 21:48:51.251 DEBUG io.netty.util.Recycler - -Dio.netty.recycler.linkCapacity: 16
			2018-06-10 21:48:51.251 DEBUG io.netty.util.Recycler - -Dio.netty.recycler.ratio: 8
			2018-06-10 21:48:51.255 INFO  com.lizhaoblog.net.SocketClientTest - 向Socket服务器发送数据:小王,我是客户端

			2018-06-10 21:48:51.340 INFO  com.lizhaoblog.net.SocketClientTest - 数据内容:data=小李,我是服务器,我收到你的信息了。
			2018-06-10 21:49:02.674 INFO  com.lizhaoblog.net.SocketClientTest - 异常发生


接下来就是改造下简单的实例了,spring框架在java体系中确实是至关重要的一个东西,所以下一篇应该是将netty和spring结合起来。


上面的代码在码云上 https://gitee.com/lizhaoandroid/JgServer
可以加qq群一起探讨Java游戏服务器开发的相关知识 676231564

 

 

 

展开阅读全文

没有更多推荐了,返回首页