第二章:第一个Netty程序

本章介绍java

获取Netty4最新版本bootstrap

设置运行环境来构建和运行netty程序服务器

建立一个基于Netty的服务器和客户端异步

拦截和处理异常socket

编写和运行Netty服务器和客户端ide

----学习Netty是如何拦截和处理异常,服务器和客户端的启动以及分离通道的处理程序----oop

1.NETTY介绍学习

一个Netty程序的工做图以下:this

1.客户端链接到服务器 .net

2.创建链接后,发送或接收数据 

3.服务器处理全部的客户端链接 

2. 1编写一个应答服务器

写一个Netty服务器主要由两部分组成:

配置服务器功能,如线程、端口

实现服务器处理程序,它包含业务逻辑,决定当有一个请求链接或接收数据时该作什么

2.1.1启动服务器

经过建立ServerBootstrap对象来启动服务器,而后配置这个对象的相关选项,如端口、线程模式、事件循环,而且添加逻辑处理程序用来处理业务逻辑。

package netty.example;  
import io.netty.bootstrap.ServerBootstrap;  
import io.netty.channel.Channel;  
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.nio.NioServerSocketChannel;  
public class EchoServer {  
    private final int port;  
    public EchoServer(int port) {  
        this.port = port;  
    }  
    public void start() throws Exception {  
        EventLoopGroup group = new NioEventLoopGroup();  
        try {  
            //create ServerBootstrap instance  
            ServerBootstrap b = new ServerBootstrap();  
            //Specifies NIO transport, local socket address  
            //Adds handler to channel pipeline  
            b.group(group).channel(NioServerSocketChannel.class).localAddress(port)  
                    .childHandler(new ChannelInitializer<Channel>() {  
                        @Override  
                        protected void initChannel(Channel ch) throws Exception {  
                            ch.pipeline().addLast(new EchoServerHandler());  
                        }  
                    });  
            //Binds server, waits for server to close, and releases resources  
            ChannelFuture f = b.bind().sync();  
            System.out.println(EchoServer.class.getName() + "started and listen on “" + f.channel().localAddress());  
            f.channel().closeFuture().sync();  
        } finally {  
            group.shutdownGracefully().sync();  
        }  
    }  
    public static void main(String[] args) throws Exception {  
        new EchoServer(65535).start();  
    }  
}

a.启动服务器应先建立一个ServerBootstrap对象,由于使用NIO,因此指定NioEventLoopGroup来接受和处理新链接,指定通道类型为NioServerSocketChannel,设置InetSocketAddress让服务器监听某个端口已等待客户端链接。

b. 接下来,调用childHandler用来指定链接后调用的ChannelHandler,这个方法传ChannelInitializer类型的参数,ChannelInitializer是个抽象类,因此须要实现initChannel方法,这个方法就是用来设置ChannelHandler。

c.最后绑定服务器 等待直到绑定完成,调用sync()方法会阻塞直到服务器完成绑定,而后服务器等待通道关闭,由于使用sync(),因此关闭操做也会被阻塞。如今你能够关闭EventLoopGroup和释放全部资源,包括建立的线程。

若是这个例子使用OIO方式传输,你须要指定OioServerSocketChannel

 本小节重点内容:

建立ServerBootstrap实例来引导绑定和启动服务器

建立NioEventLoopGroup对象来处理事件,如接受新链接、接收数据、写数据等等

指定InetSocketAddress,服务器监听此端口

设置childHandler执行全部的链接请求

都设置完毕了,最后调用ServerBootstrap.bind() 方法来绑定服务器

2.1.2 实现服务器业务逻辑

Netty使用futures和回调概念,它的设计容许你处理不一样的事件类型。

你的channelHandler必须继承ChannelInboundHandlerAdapter而且重写channelRead方法,这个方法在任什么时候候都会被调用来接收数据,在这个例子中接收的是字节。

下面是handler的实现,其实现的功能是将客户端发给服务器的数据返回给客户端:

package netty.example;  
import io.netty.buffer.Unpooled;  
import io.netty.channel.ChannelFutureListener;  
import io.netty.channel.ChannelHandlerContext;  
import io.netty.channel.ChannelInboundHandlerAdapter;  
public class EchoServerHandler extends ChannelInboundHandlerAdapter {  
    @Override  
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {  
        System.out.println("Server received: " + msg);  
        ctx.write(msg);  
    }  
    @Override  
    public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {  
        ctx.writeAndFlush(Unpooled.EMPTY_BUFFER).addListener(ChannelFutureListener.CLOSE);  
    }  
    @Override  
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {  
        cause.printStackTrace();  
        ctx.close();  
    }  
}

Netty使用多个ChannelHandler来达到对事件处理的分离,由于能够很容易的添加、更新、删除业务逻辑处理handler。Handler很简单,它的每一个方法均可以被重写,它的全部的方法中只有channelRead方法是必需要重写的。

2.1.3 捕获异常

 重写ChannelHandler的exceptionCaught方法能够捕获服务器的异常,好比客户端链接服务器后强制关闭,服务器会抛出"客户端主机强制关闭错误",经过重写exceptionCaught方法就能够处理异常,好比发生异常后关闭ChannelHandlerContext。

2.2 编写应答程序的客户端

服务器写好了,如今来写一个客户端链接服务器。应答程序的客户端包括如下几步:

链接服务器

写数据到服务器

等待接受服务器返回相同的数据

关闭链接

2.2.1 引导客户端

引导客户端启动和引导服务器很相似,客户端需同时指定host和port来告诉客户端链接哪一个服务器。看下面代码:

package netty.example;  
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;  
import io.netty.example.echo.EchoClientHandler;  
import java.net.InetSocketAddress;  
public class EchoClient {  
    private final String host;  
    private final int port;  
    public EchoClient(String host, int port) {  
        this.host = host;  
        this.port = port;  
    }  
    public void start() throws Exception {  
        EventLoopGroup group = new NioEventLoopGroup();  
        try {  
            Bootstrap b = new Bootstrap();  
            b.group(group).channel(NioSocketChannel.class).remoteAddress(new InetSocketAddress(host, port))  
                    .handler(new ChannelInitializer<SocketChannel>() {  
                        @Override  
                        protected void initChannel(SocketChannel ch) throws Exception {  
                            ch.pipeline().addLast(new EchoClientHandler());  
                        }  
                    });  
            ChannelFuture f = b.connect().sync();  
            f.channel().closeFuture().sync();  
        } finally {  
            group.shutdownGracefully().sync();  
        }  
    }  
    public static void main(String[] args) throws Exception {  
        new EchoClient("localhost", 20000).start();  
    }  
}

建立启动一个客户端包含下面几步:

建立Bootstrap对象用来引导启动客户端

建立EventLoopGroup对象并设置到Bootstrap中,EventLoopGroup能够理解为是一个线程池,这个线程池用来处理链接、接受数据、发送数据

建立InetSocketAddress并设置到Bootstrap中,InetSocketAddress是指定链接的服务器地址

添加一个ChannelHandler,客户端成功链接服务器后就会被执行

调用Bootstrap.connect()来链接服务器

最后关闭EventLoopGroup来释放资源

2.2.2 实现客户端的业务逻辑

和编写服务器的ChannelHandler同样,在这里将自定义一个继承SimpleChannelInboundHandler的ChannelHandler来处理业务

经过重写父类的三个方法来处理感兴趣的事件:

channelActive():客户端链接服务器后被调用

channelRead0():从服务器接收到数据后调用

exceptionCaught():发生异常时被调用

package netty.example;  
import io.netty.buffer.ByteBuf;  
import io.netty.buffer.ByteBufUtil;  
import io.netty.buffer.Unpooled;  
import io.netty.channel.ChannelHandlerContext;  
import io.netty.channel.SimpleChannelInboundHandler;  
import io.netty.util.CharsetUtil;  
public class EchoClientHandler extends SimpleChannelInboundHandler<ByteBuf> {  
    @Override  
    public void channelActive(ChannelHandlerContext ctx) throws Exception {  
        ctx.write(Unpooled.copiedBuffer("Netty rocks!",CharsetUtil.UTF_8));  
    }  
    @Override  
    protected void channelRead0(ChannelHandlerContext ctx, ByteBuf msg) throws Exception {  
        System.out.println("Client received: " + ByteBufUtil.hexDump(msg.readBytes(msg.readableBytes())));  
    }  
    @Override  
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {  
        cause.printStackTrace();  
        ctx.close();  
    }  
}

可能你会问为何在这里使用的是SimpleChannelInboundHandler而不使用ChannelInboundHandlerAdapter?

缘由是ChannelInboundHandlerAdapter在处理完消息后须要负责释放资源。在这里将调用ByteBuf.release()来释放资源。SimpleChannelInboundHandler会在完成channelRead0后释放消息,这是经过Netty处理全部消息的ChannelHandler实现了ReferenceCounted接口达到的。

为何在服务器中不使用SimpleChannelInboundHandler呢?

由于服务器要返回相同的消息给客户端,在服务器执行完成写操做以前不能释放调用读取到的消息,由于写操做是异步的,一旦写操做完成后,Netty中会自动释放消息。

2.3 编译和运行echo(应答)程序客户端和服务器

相关文章
相关标签/搜索