Netty学习笔记(一)

1,678 阅读3分钟

Netty简介


Netty是一款用于创建高性能网络应用程序的IO框架,它对Java的原生IO进行封装,提供了更加简洁易用的API,支持阻塞和非阻塞,并且推崇将应用逻辑代码和网络连接代码解耦。

Netty核心组件

  • Channel
  • 回调
  • Future
  • 事件和ChannelHandler

Channel

通道,NIO中的构造,类似于BIO中的流。BIO中面向流,NIO中面向Channel。Channel表示一个连接,是出站和入站数据的载体,可以对该连接进行读写。

回调

就是一个方法,一个指向 已经被提供给另一个方法 的方法的引用。使得后者可以在适当的时机调用前者。在Netty中,使用回调来处理事件。当一个回调被触发,相关事件会被一个实现了ChannelHandler接口的对象的方法处理。

Future

Netty中实现操作完成时通知应用程序的另一方式。JDK中的Future需要手动查看结果,因此可能会造成轮询或者阻塞,Netty中给了ChannelFuture实现,可以自动通知,用于异步通知。

ChannelFuture中提供了额外方法,使得一个ChannelFuture可以注册一个或多个ChannelFutureListener实例。监听器的回调方法operationComplete()将会在对用的操作完成时被调用。监听器可以判断操作是否成功,若失败可以检查抛出的Throwable。

每个Netty的出站操作都会返回一个ChannelFuture,也就是说不会阻塞,线程可以做别的事情。

事件和ChannelHandler

每个事件可以被分发给对应的ChannelHandler实现类处理。

编写EchoServer

EchoServerHandler的编写

package server;

import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.util.CharsetUtil;


/**
 * @ClassName EchoServerHandler
 * @Description TODO
 * @Author LYcreate
 * @Date 2019/2/22 21:41
 */
@ChannelHandler.Sharable
public class EchoServerHandler extends ChannelInboundHandlerAdapter {
    /**
     * 对于每个传入的消息都调用
     * @author LYcreate
     * @date 2019/10/14 21:22
     * @param ctx
     * @param msg
     * @return void
     */
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        ByteBuf in=(ByteBuf) msg;
        System.out.println("Server received:"+in.toString(CharsetUtil.UTF_8));
        //将收到的消息写给发送者,但不冲刷消息。该操作不会阻塞,直接返回结果,因此此时写数据可能还未完成。
        ctx.write(in);
    }

    /**
     * 通知ChannelHandler最后一次对channelRead()调用是当前批量读取中的最后一条消息
     * @author LYcreate
     * @date 2019/10/14 21:23
     * @param ctx
     * @return void
     */
    @Override
    public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
        //将未决消息冲刷给远程节点,并且关闭该Channel
        ctx.writeAndFlush(Unpooled.EMPTY_BUFFER).addListener(ChannelFutureListener.CLOSE);
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        cause.printStackTrace();
        ctx.close();
    }
}

EchoServer引动类的编写

package server;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.ServerSocketChannel;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;

import java.net.InetSocketAddress;

/**
 * @ClassName EchoServer
 * @Description TODO
 * @Author LYcreate
 * @Date 2019/2/22 21:54
 */
public class EchoServer {
    private final int port=8080;
    public EchoServer(){

    }

    public static void main(String[] args) throws Exception{
        new EchoServer().start();
    }
    public void start() throws Exception{
        final EchoServerHandler serverHandler=new EchoServerHandler();
        //NIO传输使用NioEventLoopGroup
        EventLoopGroup group=new NioEventLoopGroup();
        try{
            ServerBootstrap b=new ServerBootstrap();
            b.group(group)
                    .channel(NioServerSocketChannel.class)
                    .localAddress(new InetSocketAddress(port))
                    //ChannelInitializer会将ChannelHandler添加到Channel对应的ChannelPipeline中
                    .childHandler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel socketChannel) throws Exception {
                            socketChannel.pipeline().addLast(serverHandler);
                        }
                    });
            //绑定服务器,通过sync()方法同步返回
            ChannelFuture f=b.bind().sync();
            //阻塞等待关闭
            f.channel().closeFuture().sync();
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            group.shutdownGracefully().sync();
        }
    }
}

编写EchoClient

编写EchoClientHandler

package client;

import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.util.CharsetUtil;

/**
 * @ClassName EchoClientHandler
 * @Description TODO
 * @Author LYcreate
 * @Date 2019/2/22 22:15
 */
@ChannelHandler.Sharable
public class EchoClientHandler extends SimpleChannelInboundHandler<ByteBuf> {

    /**
     * 服务器连接后被调用
     * @author LYcreate
     * @date 2019/10/14 22:08
     * @param ctx
     * @return void
     */
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        ctx.writeAndFlush(Unpooled.copiedBuffer("true",CharsetUtil.UTF_8));
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        cause.printStackTrace();
        ctx.close();
    }

    /**
     * 从服务器收到消息后调用
     * @author LYcreate
     * @date 2019/10/14 22:09
     * @param ctx
     * @param byteBuf 
     * @return void
     */
    @Override
    protected void channelRead0(ChannelHandlerContext ctx, ByteBuf byteBuf) throws Exception {
        System.out.println("Client received:" + byteBuf.toString(CharsetUtil.UTF_8));
    }
}

客户端引导类

package client;

import io.netty.bootstrap.Bootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;

/**
 * @ClassName EchoClient
 * @Description TODO
 * @Author LYcreate
 * @Date 2019/2/22 22:21
 */
public class EchoClient {
    private final String host="http://localhost";
    private final int port=8080;
    public EchoClient(){};

    public static void main(String[] args) {
        new EchoClient().start();
    }
    public void start(){
        EventLoopGroup group=new NioEventLoopGroup();
        try{
            Bootstrap bootstrap=new Bootstrap();
            bootstrap.group(group)
                    .channel(NioSocketChannel.class)
                    .remoteAddress(host,port)
                    .handler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel socketChannel) throws Exception {
                            socketChannel.pipeline().addLast(new EchoClientHandler());
                        }
                    });
            ChannelFuture future=bootstrap.connect().sync();
            future.channel().closeFuture().sync();
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            group.shutdownGracefully();
        }
    }
}