Java Netty 学习(七) - 第一个Netty程序

介绍

Netty是由JBOSS提供的一个java开源框架。Netty提供异步的、事件驱动的网络应用程序框架和工具,它简化了程序员的工作,用以快速开发高性能、高可靠性的网络服务器和客户端程序。
本系列文章讲慢慢一起走进学习Netty
本篇文章就以一个Hello Word程序开始。

例子

首先,Netty作为网络编程的框架,自然离不开Socket,同时,也包括Server端以及Client端。他们利用Socket进行信息传输。
Socket可以理解为应用层与TCP/IP协议族通信的中间软件抽象层。
关于Socket可看文章:Socket
下面看demo

Client端

首先是一个启动的类:

public class Client {
    private Integer port;
    private String address;
    public Client(Integer port, String address){
        this.port = port;
        this.address = address;
    }
    /**
     * 用于连接服务器
     */
    public void start() throws Exception{
        EventLoopGroup group = new NioEventLoopGroup();  
        Bootstrap b = new Bootstrap();
        b.group(group)
                .channel(NioSocketChannel.class)
                .handler(new ChannelInitializer() {
            @Override
            protected void initChannel(SocketChannel ch) throws Exception {
                ch.pipeline().addLast(new HelloClientHandler());
            }
        });
        System.out.println("begin to connect...");
        ChannelFuture future = b.connect(this.address, this.port);
        future.channel().closeFuture().sync();
    }
    public static void main(String[] args) throws Exception {
        Client client = new Client(8989, "127.0.0.1");
        client.start();
    }
}

再看看里面的HelloClientHandler

public class HelloClientHandler extends SimpleChannelInboundHandler {
    @Override
    protected void channelRead0(ChannelHandlerContext ctx, ByteBuf msg) throws Exception {
        System.out.println("Client received Message from server: " + msg.toString(CharsetUtil.UTF_8));  //读取数据就输出
    }
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        ctx.writeAndFlush(Unpooled.copiedBuffer("Netty rocks!",CharsetUtil.UTF_8));   //活跃时候输出
    }
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        cause.printStackTrace();
        ctx.close();   //遇到错误就关闭
    }
}

对于上面的client端的代码,可以看到下面几个概念:

  • EventLoopGroup:可以理解为线程池,里面产生EventLoop来执行channel所有产生的事件
  • Bootstrap:启动器,讲Netty各个组件串起来使之运行
  • Pipline:可以理解为管道,或者流水线,里面可以装载多个ChannelHandler并让他们执行
  • ChannelHandler:例如HelloClientHandler ,具体的channel所处理的工作

整个过程可以理解为这样一个过程,分发快递过程,快递车把快递送到目的地,此时有一个或者多个快递员(EventLoop)进行快递分发,分发到给不同的区域标签(Pipline),此时,由于快递是已经定义好的,再有快递员发给不同的快递手上(执行ChannelHandler所定一个的规则)
EventLoop 的作用是一个死循环,而这个循环中做3件事情:

  • 有条件的等待 NIO 事件
  • 处理 NIO 事件
  • 处理消息队列中的任务

下面看看服务端代码

Server端

首先也是一个启动类:

public class Server {
    private Integer port;
    public Server(Integer port){
        this.port = port;
    }
    public void startServer() throws Exception {
        EventLoopGroup group = new NioEventLoopGroup();
        try {
            ServerBootstrap serverBootstrap = new ServerBootstrap();
            serverBootstrap.group(group)
                    .channel(NioServerSocketChannel.class)
                    .localAddress(new InetSocketAddress(port))
                    .childHandler(new ChannelInitializer() {
                        @Override
                        protected void initChannel(SocketChannel ch) throws Exception {
                            System.out.println("new channel");
                            ch.pipeline().addLast(new HelloServerHandler());
                        }
                    });
            System.out.println("begin to run server");
            ChannelFuture future = serverBootstrap.bind().sync();
            future.channel().closeFuture().sync();
        }finally {
            group.shutdownGracefully().sync();
        }
    }
    public static void main(String[] args) throws Exception {
        Server server = new Server(8989);
        server.startServer();
    }
}

再看HelloServerHandler,和前面的HelloClientHandler逻辑差不多:

public class HelloServerHandler extends ChannelInboundHandlerAdapter {
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        ByteBuf in = (ByteBuf) msg;
        System.out.println("Server received from client: " + in.toString(CharsetUtil.UTF_8));
        ctx.write(in);     //发送出去,所以client端可以读
    }
    @Override
    public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
        ctx.writeAndFlush(Unpooled.EMPTY_BUFFER)
                .addListener(ChannelFutureListener.CLOSE);
    }
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        System.out.println("server active");
    }
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        System.out.println(cause);
        ctx.close();
    }
}

就看启动代码来看,服务端代码和客户端代码还是很相似的

  • Bootstrap变为ServerBootstrap
  • NioSocketChannel变为NioServerSocketChannel
  • 而在Handler处理上,client端的handler变为了childHandler

整个例子的流程如下:

  1. 服务端启动,监听127.0.0.1:8989端口
  2. 客户端连接端口,服务端监听到,输出new channel并把HelloServerHandler加入到pipline
  3. 此时,客户端连接上后,触发了channelActive事件,并把Netty rocks!发了出去
  4. 此时,服务端接受到了数据Netty rocks!,触发了HelloServerHandler中事件,所以打印了Server received: Netty rocks!,并且发送了出去
  5. 此时,由于客户端接受消息,触发了channelRead0 所以最后打印了Client received: Netty rocks!

整个例子就完成了。

资料:

  1. Netty In Action

你可能感兴趣的:(Java-Netty,Netty)