Netty/NIO实践

来源:互联网 发布:高中免费教学视频软件 编辑:程序博客网 时间:2024/06/09 18:18
Netty的异步事件驱动模型主要涉及到下面几个核心的概念:
  Channel:表示一个与socket关联的通道
  ChannelPipeline: 管道,一个Channel拥有一个ChannelPipeline,ChannelPipeline维护着一个处理链(严格的说是两 个:upstream、downstream),
处理链是由很多处理句柄ChannelHandler所构成,每个ChannelHandler处理完以 后会传递给链中的下一个处理句柄继续处理。
  ChannelHandler:处理句柄,用户可以定义自己的处理句柄来处理每个请求,或发出请求前进行预处理,典型的有编码/解码器:decoder、encoder。
  ChannelEvent:事件,是整个模型的处理对象,当产生或触发(fire)一个事件时,该事件会沿着ChannelPipeline处理链依次被处理。
  ChannelFuture: 异步结果,这个是异步事件处理的关键,当一个事件被处理时,可以直接以ChannelFuture的形式直接返回,不用在当前操作中被阻塞。

可以通过 ChannelFuture得到最终的执行结果,具体的做法是在ChannelFuture添加监听器listener,当操作最终被执行完 后,listener会被触发,

我们可以在listener的回调函数中预定义我们的业务代码。


Netty应用实例:


1. netty版本

<dependency>            <groupId>io.netty</groupId>            <artifactId>netty-all</artifactId>            <version>5.0.0.Alpha2</version>        </dependency>

2. 服务器端:

package com.xx.xx.web.nettypro.server;import io.netty.bootstrap.ServerBootstrap;import io.netty.channel.ChannelFuture;import io.netty.channel.ChannelHandler;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.NioServerSocketChannel;import javax.annotation.PostConstruct;/** * Created by winy. */@ChannelHandler.Sharablepublic class EpayServer {    // 服务绑定端口    private int port;    public EpayServer(int port) {        this.port = port;    }    public static void main(String[] args) throws InterruptedException {        new EpayServer(5555).start();    }    /**     * 此处用的是main启动,在项目中时,start方法上可以用spring注解:@PostConstruct,然后把该类EpayServer交给spring管理即可     * 该注解的作用是spring装载bean的时候,在加载完EpayServer构造函数后执行     * @throws InterruptedException     */    public void start() throws InterruptedException {        EventLoopGroup eventLoopGroup = new NioEventLoopGroup();        try {            // 服务端业务处理            final EpayServerHandler epayServerHandler = new EpayServerHandler();            ServerBootstrap serverBootstrap = new ServerBootstrap();            // 配置            serverBootstrap.group(eventLoopGroup)                    .channel(NioServerSocketChannel.class)                    .childHandler(new ChannelInitializer<SocketChannel>() {                        @Override                        protected void initChannel(SocketChannel socketChannel) throws Exception {                            socketChannel.pipeline().addLast(epayServerHandler);                        }                    });            // 异步            ChannelFuture channelFuture = serverBootstrap.bind(port).sync();            channelFuture.channel().closeFuture().sync();        } catch (InterruptedException e) {            e.printStackTrace();        } finally {            eventLoopGroup.shutdownGracefully().sync();        }    }}


3. 服务器端handler

package com.xx.xx.web.nettypro.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.ChannelHandlerAdapter;import io.netty.channel.ChannelHandlerContext;import io.netty.util.CharsetUtil;/** * Netty5: 取消了进站、出站的划分,统一为继承ChannelHandlerAdapter,原来的ChannelInboundHandlerAdapter,ChannelOutboundHandlerAdapter被废弃 * Created by winy */@ChannelHandler.Sharablepublic class EpayServerHandler extends ChannelHandlerAdapter {        /**     * 服务端接收到消息后执行     * @param ctx     * @param msg     * @throws Exception     */    @Override    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {        ByteBuf byteBuf = (ByteBuf)msg;        System.out.println("服务器接收到的信息====" + byteBuf.toString(CharsetUtil.UTF_8));                // 客户端请求服务器的请求信息  一般包含报文头+报文体        // 报文头为固定长度        // 报文体协定用&拼接        String msgServer = "服务器发送的消息:1";        byte[] bytes = msgServer.getBytes();        ByteBuf firstMessage = Unpooled.buffer(bytes.length);        firstMessage.writeBytes(bytes);        ctx.writeAndFlush(firstMessage);    }        /**     * 客户端读完(客户端channelRead方法执行完后)数据后执行     * @param ctx     * @throws Exception     */    @Override    public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {        ctx.writeAndFlush(Unpooled.EMPTY_BUFFER).addListener(ChannelFutureListener.CLOSE);    }        /**     * 异常的场合调用     * @param ctx     * @param cause     * @throws Exception     */    @Override    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {        cause.printStackTrace();        ctx.close();    }}


4. 客户端

package com.xx.xx.web.nettypro.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;/** * Created by winy. */public class EpayClient {    private String host;    private int port;    public EpayClient(String host,int port) {        this.host = host;        this.port = port;    }    public static void main(String[] args) {        new EpayClient("服务器端ip",5555).start();    }    public void start() {        EventLoopGroup eventLoopGroup = new NioEventLoopGroup();        try {            Bootstrap bootstrap = new Bootstrap();            bootstrap.group(eventLoopGroup)                    .channel(NioSocketChannel.class)                    .handler(new ChannelInitializer<SocketChannel>() {                        @Override                        protected void initChannel(SocketChannel socketChannel) throws Exception {                            socketChannel.pipeline().addLast(new EpayClientHandler());                        }                    });            // 此处为异步调用  若需要同步,可使用await()            ChannelFuture channelFuture = bootstrap.connect(host,port).sync();            channelFuture.channel().closeFuture().sync();        } catch (InterruptedException e) {            e.printStackTrace();        } finally {            try {                eventLoopGroup.shutdownGracefully().sync();            } catch (InterruptedException e) {                e.printStackTrace();            }        }    }}


5、客户端handler

package com.xx.xx.web.nettypro.client;import io.netty.buffer.ByteBuf;import io.netty.buffer.Unpooled;import io.netty.channel.ChannelHandlerAdapter;import io.netty.channel.ChannelHandlerContext;/** * Created by winy */public class EpayClientHandler extends ChannelHandlerAdapter {        /**     * 连接服务器成功后执行发送信息     * 向服务器发送报文     * @param ctx     * @throws Exception     */    @Override    public void channelActive(ChannelHandlerContext ctx) throws Exception {        // 客户端请求服务器的请求信息  一般包含报文头+报文体        // 报文头为固定长度        // 报文体协定用&拼接        String msgClient = "客户端信息1";        byte[] bytes = msgClient.getBytes();        ByteBuf firstMessage = Unpooled.buffer(bytes.length);        firstMessage.writeBytes(bytes);        ctx.writeAndFlush(firstMessage);    }    /**     * 服务端返回消息后执行     * @param ctx     * @param msg     * @throws Exception     */    @Override    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {        //服务端返回消息后        ByteBuf buf = (ByteBuf) msg;        byte[] req = new byte[buf.readableBytes()];        buf.readBytes(req);        String body = new String(req, "UTF-8");        System.out.println("client1 接收到的信息=" + body);    }    /**     * 异常的场合调用     * @param ctx     * @param cause     * @throws Exception     */    @Override    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {        cause.printStackTrace();        ctx.close();    }}



注:

   1. 本实例测试过多个客户端的情况,当有多个客户端连接的场合,server和server handler 需要使用注解:@ChannelHandler.Sharable

   2. 经测试此处客户端连接为短连接,而不是传输层TCP/IP协议的长连接,即一次连接完成就会断开!



文中如有不对之处,望大家指正!




0 0