Netty系列:二、第一个Netty程序

不正经的开头

本文首先会介绍Netty的基本API,然后在实现一个小应用,那我们开始吧,是不是有点小激动呢 [滑稽]

本文主要从以下几个方面:

  • ChannelHandler的作用和它的方法
  • 服务端编写
    • Handler
    • BootStrap
  • 客户端的编写
    • Handler
    • BootStrap
  • 运行结果

1.ChannelHandler的API

之前我们也介绍过了ChannelHandler,此处详细介绍它的方法以及用途。它是一个接口,它的实现负责接收并响应事件。
我们比较常用的是ChannelInboundHandlerAdapter它是一个实现了ChannelHandler接口的类,一般也够我们用了,当然可以自己写一个类继承它,扩充自己的功能。

一些常用的方法:

  • channelRead()对于每个传入的消息都要调用
  • channelReadComplete()通知ChannelInboundHandler最后一次对channelRead()的调用时是当前批次中的最后一条消息
  • exceptionCaught()读取操作期间,抛出异常时调用

再说一次,ChannelHandler负责接收并响应事件,即来了一个事件,它执行对应的方法。哎妈呀,我太机智了~

2.服务端编写

有了上面的基础就可以开始了

一个小插曲:Netty服务器结构
  • 至少一个ChannelHandler:该组件实现了服务器接收的数据的处理,即它的业务逻辑
  • 引导:这是配置服务器的启动代码。将服务器绑定到他要监听链接请求的端口上,配置Channel,将有关的消息通知给ChannelHanler

Server端代码:

public class ServerDemo {
    private int prot;
    public ServerDemo(int prot) {
        this.prot = prot;
    }
    public static void main(String[] args) throws InterruptedException {
        new ServerDemo(9111).satart();
    }

    public void satart() throws InterruptedException {
        final ServerHandller serverHandller = new ServerHandller();
        //创建ServerBootstrap实例
        ServerBootstrap boot = new ServerBootstrap();
        //创建一个EventLoopGroup
        EventLoopGroup group =  new NioEventLoopGroup();
        //指定实例来接受和处理新的连接。
        boot.group(group)
                //指定使用NIO Channel传输
                .channel(NioServerSocketChannel.class)
                .localAddress(new InetSocketAddress(prot))
                //当一个新的连接被接受时,一个新的子Channel会被创建,
                // ChannelInitializer会把serverHandller的实例加入到该Channel的ChannelPipeLine
                .childHandler(new ChannelInitializer<SocketChannel>() {
                    protected void initChannel(SocketChannel socketChannel) throws Exception {
                        socketChannel.pipeline().addLast(serverHandller);
                    }
                });
        try {
            //异步绑定到服务器,调用sync方法阻塞当前线程,直到绑定完成
            ChannelFuture future = boot.bind().sync();
            future.channel().closeFuture().sync();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }finally {
            group.shutdownGracefully().sync();
        }
    }
}

ServerHandler代码:

public class ServerHandller extends ChannelInboundHandlerAdapter{
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object o) {
        ByteBuf in = (ByteBuf)o;
        System.out.println("Server Receive:"+in.toString(CharsetUtil.UTF_8));
    }
    @Override
    public void channelReadComplete(ChannelHandlerContext ctx) {
        ctx.writeAndFlush((Unpooled.copiedBuffer("Thanks ~", CharsetUtil.UTF_8)));
        /*ctx.writeAndFlush(Unpooled.EMPTY_BUFFER)
                .addListener(ChannelFutureListener.CLOSE);*/
    }
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
        cause.printStackTrace();
        ctx.close();
    }
}
总结一下
  • ServerHandler实现了业务逻辑
  • 创建ServerBootStrap实例,引导以及绑定服务器
  • 创建EventLoopGroup实例,进行事件的处理,如连接、读取、写入
  • 用一个ServerHandler实例初始化每一个新的Channel

3.客户端

编写客户端也包括业务逻辑和引导,和服务器中一样。

3.1.ChannelHandler实现客户端的业务逻辑

介绍一个抽象类SimpleChannelInboundHandler,有点和ChannelInboundHandlerAdapter类似,它可以用来处理客户的业务逻辑。直接上代码

public class ClientHandler extends SimpleChannelInboundHandler<ByteBuf> {
    @Override
    //当被通知Channel是活跃的时候,发送一条消息
    public void channelActive(ChannelHandlerContext ctx) {
        ctx.writeAndFlush(Unpooled.copiedBuffer("Netty is No.1", CharsetUtil.UTF_8));
    }
    @Override
    //接收消息时会调用该方法
    protected void channelRead0(ChannelHandlerContext ctx, ByteBuf msg) throws Exception {
        System.out.println("Client Recived:"+msg.toString(CharsetUtil.UTF_8));
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        cause.printStackTrace();
        ctx.close();
    }
}
3.3.客户端
public class ClientDemo {
   private final String host = "127.0.0.1";
   private final int port = 9999;
   public void start() throws Exception {
       EventLoopGroup group = new NioEventLoopGroup();
       //创建Bootstrap
       Bootstrap bootstrap = new Bootstrap();
       try {
           //指定EventLoopGroup处理客户端事件
           bootstrap.group(group)
                   //适用于Nio传输的Channel类型
                   .channel(NioSocketChannel.class)
                   .remoteAddress(new InetSocketAddress(host, port))
                   //在创建Chann时,向ChannelPipeline添加一个ClientHandler实例
                   .handler(new ChannelInitializer<SocketChannel>() {
                       protected void initChannel(SocketChannel ch) throws Exception {
                           ch.pipeline().addLast(new ClientHandler());
                       }
                   });

           //连接到服务器,阻塞直到连接完成
           ChannelFuture future = bootstrap.connect().sync();
           //阻塞到Channel关闭
           future.channel().closeFuture().sync();
       }catch (Exception e) {
           e.printStackTrace();
       }finally {
           //关闭线程池并释放资源
           group.shutdownGracefully().sync();
       }
   }

   public void main(String args[]) throws Exception {
       new ClientDemo().start();
   }

}

总结一下

  • 实例化了NioEventLoopGroup实例,被用来处理创建新的连接以及数据的出入
  • 实例化InetSocketAddress用来连接到服务器
  • 当有连接建立,ClientHandler的实例会被加到ChannelPipeline
  • 正式连接到服务器Bootstrap.connect()

运行结果
Server端输出:Server Receive:Netty is No.1
Client端输出:Client Recived:Thanks ~

下一篇:http://blog.csdn.net/theludlows/article/details/79463635

猜你喜欢

转载自blog.csdn.net/TheLudlows/article/details/79449562