Netty学习2.1——Netty的服务端

编写Echo服务器

    所有的Netty服务器都需要以下两部分:

        至少一个ChannelHandler_该组件实现了服务器对从客户端接收的数据的处理,即它的业务逻辑。

        引导——这是配置服务器的启动代码。至少,它会将服务器绑定到它要监听连接请求的端口上。

    1.ChannelHandler和业务逻辑

        ChannelHandler:是一个接口族的父接口,它的实现负责接收并响应事件通知。在Netty应用程序中,所有的数据处理逻辑都包含在这些核心抽象的实现中。

        因为Echo服务器会响应传入的消息,所以不它需要实现ChannelInboundHandler接口,用来定义响应入站事件的方法。

    channelRead()——对于每个传入的消息都要调用;

    channelReadComplete()——通知ChannelInboundHandler最后一次对channelRead()的调用时当前批量读取中的最后一条消息;

    exceptionCaught()——在读取操作期间,有异常抛出时会调用。

    该服务器的ChannelHandler实现是EchoServerHandler,如下:

    

//标识一个ChannelHandler可以被多个Channel安全地共享
@ChannelHandler.Sharable
public class EchoServerHandler extends ChannelInboundHandlerAdapter{

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg){
        ByteBuf in = (ByteBuf) msg;
        //将消息记录到控制台
        System.out.println("Server received:" + in.toString(CharsetUtil.UTF_8));
        //将接收到的消息写给发送者,而不冲刷出站消息
        ctx.write(in);
    }

    @Override
    public void channelReadComplete(ChannelHandlerContext ctx){
        //将未决消息(目前暂存于ChannelOutboundBuffer中的消息,在下一次调用flush()或者writeAndFlush()方法时将会尝试写出到套接字)冲刷到远程节点,并且关闭该Channel
        ctx.writeAndFlush(Unpooled.EMPTY_BUFFER).addListener(ChannelFutureListener.CLOSE);
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause){
        //打印异常栈跟踪
        cause.printStackTrace();
        //关闭该Channel
        ctx.close();
    }
}

    ChannelInboundHandlerAdapter有一个直观的API,并且它的每个方法都可以被重写以挂钩到事件生命周期的恰当点上。因为需要处理所有接收到的数据,所以重写了channelRead()方法。

    重写exceptionCaught()方法允许你对Throwable的任何子类型做出反应,在这里是记录异常并关闭连接。一个更加完善的应用程序也许会尝试从异常中恢复,但在这个场景下,只是通过简单地关闭连接来通知远程节点发生了错误。


    如果不捕获异常,会发生什么呢:

        每个Channel都拥有一个与之相关联的ChannelPipeline,其持有一个ChannelHandler的实例链。在默认情况下,ChannelHandler会把对它的方法的调用转发给链中的下一个ChannelHandler。因此,如果exceptionCaught()方法没有被该链的某处实现,那么所接收的异常将会被传递到ChannelPipeeline的尾端并被记录。为此,你的应用程序应该提供至少有一个实现了exceptionCaught()方法的ChannelHandler。 

    几个关键点:

        针对不同类型的事件来调用ChannelHandler;

        应用程序通过实现或者扩展ChannelHandler来挂钩带事件的生命周期,并且提供自定义的应用程序逻辑;

        在架构上,ChannelHandler有助于保持业务逻辑与网络处理代码的分离。这简化了开发过程,因为代码必须不断地演化以响应不断变化的需求。

    2.引导服务器

    引导服务器具体涉及以下内容:

    1.绑定到服务器将在其上监听并接受传入连接请求的接口;

    2.配置Channel,以将有关的入站消息通知给EchoServerHandler实例。

传输:
    在网络协议的标准多层视图中,传输层提供了端到端的或者主机到主机的通信服务。
    因特网通信是建立在TCP传输之上的。除了一些由Java NIO实现提供的服务器端性能增强之外,NIO传输的大多数时候指的就是TCP传输。

以下展示了EchoServerHandler类的完整代码。

public class EchoServer {
    private final int port;

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

    public static void main(String[] args) throws Exception{
        if(args.length != 1){
            System.out.println("Usage:" + EchoServer.class.getSimpleName() + " <port>");
            return;
        }
        //设置端口值(如果端口从参数的格式不正确,则抛出一个NumberFormatException)
        int port = Integer.parseInt(args[0]);
        //调用服务器的start()方法
        new EchoServer(port).start();
    }

    private void start() throws Exception {
        final EchoServerHandler serverHandler = new EchoServerHandler();
        //创建EventLoopGroup
        EventLoopGroup group = new NioEventLoopGroup();
        try{
            //创建ServerBootStrap
            ServerBootstrap b = new ServerBootstrap();
            b.group(group)
                    //指定所使用的NIO传输Channel
                    .channel(NioServerSocketChannel.class)
                    //使用指定的端口设置套接字地址
                    .localAddress(new InetSocketAddress(port))
                    //添加一个EchoServer到子Channel的ChannelPipeline
                    .childHandler(new ChannelInitializer<SocketChannel>() {
                @Override
                public void initChannel(SocketChannel channel) throws Exception{
                    //EchoServerHandler被标注为@Shareable,所以我们可以总是使用同样的实例
                    channel.pipeline().addLast(serverHandler);
                }
            });
            //异步地绑定服务器;调用sync()方法阻塞等待直到绑定完成
            ChannelFuture future = b.bind().sync();
            //获取Channel的CloseFuture,并且阻塞当前线程直到它完成
            future.channel().closeFuture().sync();
        } finally {
            //关闭EventLoopGroup,释放所有的资源
            group.shutdownGracefully().sync();
        }
    }

}

猜你喜欢

转载自blog.csdn.net/qq_22238629/article/details/80501184