Netty-3 Netty入门案例及核心API

完整代码请查看:https://gitee.com/firewolf/java-io/tree/master/java-io/netty-01-helloworld

Java IO(BIO)、伪异步IO、NIO、NIO2(AIO)、Netty这篇文章里面,我们简单的讲解了Netty的一些优点,下面我们使用Netty完成一个简单的群聊功能(多个客户端之间可以进行聊天),同时对Netty里面的一些核心API进行介绍。

一、使用Netty开发群聊

(一)引入netty的jar包

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

注意:Netty最新版本为5.0.0.Alpha2,但是已经被废弃了,所以这里依然使用的是Netty4。

(二)Netty群聊服务端

package com.firewolf.java.io.netty.chat;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
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 java.util.HashMap;
import java.util.Map;

/**
 * 作者:刘兴 时间:2019/5/15
 **/
public class NettyChatServer {

  public static Map<String, Channel> channels = new HashMap<>();

  public NettyChatServer(int port) {

    //服务端NIO线程组,用于网络事件处理,实际上就是Reactor线程组
    EventLoopGroup bossGroup = new NioEventLoopGroup(); // 用于接受客户端的连接
    EventLoopGroup workGroup = new NioEventLoopGroup(); // 用户SocketChannel进行网络读写请求
    try {
      ServerBootstrap bootstrap = new ServerBootstrap(); //用于启动NIO的辅助启动类
      bootstrap.group(bossGroup, workGroup)
          .channel(NioServerSocketChannel.class) //这个类似于NIO中的NioServerSocketChannel,IO中的ServerSocket,用于绑定端口
          .option(ChannelOption.SO_BACKLOG, 1024) //这是TCP参数,这里是设置了backlog的大小
          //设置网络IO事件处理器信息,如处理器类、编码、解码等等。
          .childHandler(new ChannelInitializer<SocketChannel>() {
            @Override
            protected void initChannel(SocketChannel socketChannel) throws Exception {
              socketChannel.pipeline().addLast(new MessageServerHandler());
            }
          });
      //绑定端口,同时进行阻塞直到服务关闭
      ChannelFuture future = bootstrap.bind(port).sync();
      System.out.println("启动服务端监听端口:" + port);
      //关闭通道
      future.channel().closeFuture().sync();

    } catch (InterruptedException e) {
      e.printStackTrace();
    } finally {
      //优雅退出,释放线程资源
      bossGroup.shutdownGracefully();
      workGroup.shutdownGracefully();
    }

  }


  /**
   * 事件消息处理器,需要继承ChannelInboundHandlerAdapter
   */
  class MessageServerHandler extends ChannelInboundHandlerAdapter {

    //当有客户端连接的时候,会触发
    @Override
    public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
      //保存所有的连接Channel
      channels.put(ctx.channel().id().asLongText(), ctx.channel());
    }

    //读取到数据之后会触发这个方法
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
      ByteBuf buf = (ByteBuf) msg;
      byte[] bytes = new byte[buf.readableBytes()];
      buf.readBytes(bytes);
      String message = new String(bytes); //把读取到的字节信息转换成字符串
      System.out.println("有客户端消息:" + message);
      buf.release(); //获取到消息后释放ByteBuf
      //转发消息
      dispathMessage(message, ctx.channel());
    }
  }

  private void dispathMessage(String message, Channel channel) {
    String otherResponse = "有人说:" + message;
    String meResponse = "我说:" + message;
    channels.entrySet().forEach(entry -> {
      String response = entry.getKey().equals(channel.id().asLongText()) ? meResponse : otherResponse;
      ByteBuf writeBuf = Unpooled.buffer(response.getBytes().length);
      writeBuf.writeBytes(response.getBytes());
      entry.getValue().writeAndFlush(writeBuf);
    });
  }

  public static void main(String[] args) {
    new NettyChatServer(9999);
  }
}

(三)Netty群聊客户端

package com.firewolf.java.io.netty.chat;

import io.netty.bootstrap.Bootstrap;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import java.net.InetSocketAddress;
import java.util.Scanner;

/**
 * 作者:刘兴 时间:2019/5/15
 **/
public class NettyChatClient {


  public NettyChatClient(String host, int port) {
    NioEventLoopGroup group = new NioEventLoopGroup();
    try {
      Bootstrap bootstrap = new Bootstrap();
      bootstrap.group(group)
          .channel(NioSocketChannel.class)
          .option(ChannelOption.TCP_NODELAY, true)
          .handler(new ChannelInitializer<SocketChannel>() {
            @Override
            protected void initChannel(SocketChannel socketChannel) throws Exception {
              socketChannel.pipeline().addLast(new MessageClientHandler());
            }
          });

      ChannelFuture f = bootstrap.connect(new InetSocketAddress(host, port)).sync();
      System.out.println("连接服务器成功-----");
      f.channel().closeFuture().sync();//关闭通道
    } catch (InterruptedException e) {
      e.printStackTrace();
    } finally {
      group.shutdownGracefully();
    }
  }


  class MessageClientHandler extends ChannelInboundHandlerAdapter {

    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
      //通道准备就绪,可以写入数据
      //启动线程监听用户输入
      new Thread(new Runnable() {
        @Override
        public void run() {
          Scanner scanner = new Scanner(System.in);
          while (true) {
            String message = scanner.nextLine();
            ByteBuf buf = Unpooled.buffer(message.getBytes().length);
            buf.writeBytes(message.getBytes());
            ctx.writeAndFlush(buf);
          }
        }
      }).start();

    }

    //读取数据
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
      ByteBuf buf = (ByteBuf) msg;
      byte[] bytes = new byte[buf.readableBytes()];
      buf.readBytes(bytes);
      String message = new String(bytes);
      System.out.println(message);
    }
  }


  public static void main(String[] args) {
    new NettyChatClient("127.0.0.1", 9999);
  }
}

接下来,我们启动Server,然后启动多个Client,就可以进行聊天了。
我们可以看到,Netty相对NIO来说,开发更加简洁。

二、Netty核心API

  • ServerBootStrap:服务端启动辅助类,用于设置服务器启动的一些参数
  • EventLoopGroup:Netty的Reactor线程池,实际上是一个EventLoop数组。EventLoop的责是处理所有注册到本线程多路复用器Selector上的Channel,
  • NioServerSocketChannel:相当于NIO中的ServerSocketChannel
  • ChannelPipeline:负责处理网络事件的职责连,管理和执行ChannelHandler,典型网络事件如下:
    1. 链路注册;
    2. 链路激活;
    3. 链路断开;
    4. 接受到请求消息;
    5. 请求消息接受并处理完毕;
    6. 发送应答消息;
    7. 链路发生异常;
    8. 发生用户自定义事件;
  • ChannelHandler:Netty提供给用户的用于定制和扩展的关键接口。可以用于定制:消息编解码、心跳、安全认证、TSL/SSL认证、流量监控、流量整形等,Netty提供了很多定义好的ChannelHandler,比较实用的有:
    1. MessageToByteEncoder:系统编码框架,用来把POJO转换成ByteBuf
    2. ByteToMessageDecoder:系统解码框架,用来把ByteBuf转换成POJO
    3. ByteToMessageCodec:系统编解码框架
    4. MessageToMessageDecoder:二次解码器,把一种类型的POJO转换成另外一种类型的POJO
    5. LengthFieldBasedFrameDecoder:基于长度的半包解码器
    6. DelimiterBasedFrameDecoder:自定义分隔符解码器
    7. LineBasedFrameDecoder:回车换行符分割包解码器
    8. FixedLengthFrameDecoder:固定长度包解码器
    9. LengthFieldPrepender:会在包的前面添加固定长度
    10. StringDecoder:把包转换成字符串
    11. LoggingHandler:打印码流日志
    12. SslHandler:SSL安全认证
    13. IdleStateHandler:链路空闲检测
    14. Base64Decoder:Base64解码器
    15. Base64Encoder:Base64编码器
  • ChannelInboundHandlerAdapter:提供链接事件回调接口,我们通常主要处理这一部分内容
  • HttpObjectAggregator:把HttpMessage和HttpContent整合成FullHttpRequest,POST请求的时候,需要使用这个处理器,否则的话,接收不到参数(因为POST请求的参数是携带在请求体里面的)
  • HttpRequestDecoder:Http请求编码器
  • HttpResponseEncoder:Http响应编码器

猜你喜欢

转载自blog.csdn.net/mail_liuxing/article/details/90603164