Netty简单入门

软件要求

  • 最新版本Netty
  • JDK1.6以上

写一个Discard Server

在这个世界上最简单的协议不是 Hello world,而是DISCARD。该协议将一切接收到的数据全部舍弃,不做任何响应。因此,实现DISCARD协议,你唯一需要做的事情是忽略所有接收到的数据

  • 接下来,我们直接从handler的实现开始编写Discard服务,handler主要用于处理Netty产生的IO事件。
package discard.server;

import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;

/**
 * @ClassName DiscardHandler
 * @Description TODO
 * @Author xiuc_shi
 * @Date 2020/8/29 下午 1:17
 * @Version 1.0
 **/
public class DiscardHandler extends ChannelInboundHandlerAdapter {//(1)

    @Override public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {//(2)
        //舍弃接收到的消息
        //        ((ByteBuf)msg).release();//(3)

        ByteBuf in = (ByteBuf)msg;
        while (in.isReadable()) {
            System.out.println((char)in.readByte());
            System.out.flush();
        }
    }

    @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {//(4)
        super.exceptionCaught(ctx, cause);
    }
}

为了看到服务器是否真的接收到数据,我们直接将数据打印出来。

  1. DiscardServerHandler继承了ChannelInboundHandlerAdapter,该类是ChannelInboundHandler的一个实现。ChannelInboundHandler提供了很多事件处理方法供你重写。
  2. 我们此处重写了channelRead()事件处理方法。当有新消息从客户端发出,该方法被调用来接收来自客户端的消息。在这个例子中,接收消息的类型是ByteBuf
  3. 对于DISCARD协议的实现,处理器会忽略接收到的消息。ByteBuf是一个引用计数对象(Reference-counted Object),因此必须通过调用release()方法显式释放。通常,channelRead()事件处理方法如下实现:
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) {
    try {
        // Do something with msg
    } finally {
        ReferenceCountUtil.release(msg);
    }
}
  1. 当处理器在处理事件是发生异常或者出现IO错误时,exceptionCaught()事件处理方法会触发抛出Throwable。大多数情况下,应当记录日志并且关闭相关的channel。实现不尽相同,根据你的需求,例如有时还希望在关闭连接之前发送错误码响应消息。
  • 至此,我们已经实现了DISCARD服务器的第一步。接下来,写main方法用来启动DiscardServerHandler服务。
package discard.server;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
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;

/**
 * @ClassName Server
 * @Description TODO
 * @Author xiuc_shi
 * @Date 2020/8/29 下午 1:39
 * @Version 1.0
 **/
public class DiscardServer {
    private int port;

    public DiscardServer(int port) {
        this.port = port;
    }

    public void run() throws Exception{
        EventLoopGroup bossGroup = new NioEventLoopGroup();//(1)
        EventLoopGroup workerGroup = new NioEventLoopGroup();
        try {
            ServerBootstrap b = new ServerBootstrap();//(2)
            b.group(bossGroup, workerGroup).channel(NioServerSocketChannel.class)//(3)
                .childHandler(new ChannelInitializer() {//(4)
                    @Override protected void initChannel(SocketChannel socketChannel) throws Exception {
                        socketChannel.pipeline().addLast(new DiscardHandler());
                    }
                }).option(ChannelOption.SO_BACKLOG, 128)//(5)
                .childOption(ChannelOption.SO_KEEPALIVE, true);//(6)
            //绑定端口,接收进来的连接
            ChannelFuture f = b.bind(port).sync();//(7)
            //等待服务器的socket关闭
            //在本例中,这不会发生,但你可以优雅地关闭你的服务
            f.channel().closeFuture().sync();

        }finally {
            workerGroup.shutdownGracefully();
            bossGroup.shutdownGracefully();
        }
    }

    public static void main(String[] args) throws Exception {
        int port = 8080;
        if(args.length > 0){
            port = Integer.parseInt(args[0]);
        }
        new DiscardServer(port).run();
    }
}
  1. NioEventLoopGroup是一个用于处理IO操作的多线程事件循环。Netty为不同的传输类型实现了多样的EventLoopGroup。我们在本例中实现的服务器端应用,使用了两个NioEventLoopGroup。其一,通常称为boss,接受一个进来的连接;其二,通常称为worker,处理boss循环器已经接受并注册到worker中的连接。多少个线程,以及线程和Channel之间的映射取决于EventLoopGroup的实现,或者通过构造器配置。
  2. ServerBootstrap是一个用于配置服务器的帮助类。你可以使用channel直接配置服务器。然而,这是一个乏味的过程,在大多数情况下你不需要做。
  3. 在此处,我们指明使用NioServerSocketChannel类来实例化一个新的Channel来接受进来的连接。
  4. 此处指明的处理器总是由新接受的channel来评估。(The handler specified here will always be evaluated by a newly accepted Channel.)ChannelInitializer是一个特殊的处理器,它被用于帮助用户初始化一个新的channel。类似于你想通过添加一些处理器到channel的ChannelPipeline以完成初始化。
  5. 你还能够设置参数来定制化你的Channel实现。本例实现的是TCP/IP服务器,因此可以设置socket options例如tcpNoDelaykeepAlive
  6. option()是服务于NioServerSocketChannel,接受进来的连接。而childOption()则是服务于父ServerChannel接受的Channels。
  7. 最后,绑定端口然后开启服务。

测试结果
我们使用cmd,输入命令talnet localhost 8080发起请求,输入1,2,3,4,控制台依次打印出来。

Netty简单入门_第1张图片

你可能感兴趣的:(Netty简单入门)