netty的学习

本人看了很久官网,这里写点总结

前言

问题

现如今我们使用通用的应用程序或者类库来实现系统之间地互相访问,比如我们经常使用一个HTTP客户端来从web服务器上获取信息,或者通过web service来执行一个远程的调用。

然而,有时候一个通用的协议和他的实现并没有覆盖一些场景。比如我们无法使用一个通用的HTTP服务器来处理大文件、电子邮件、近实时消息比如财务信息和多人游戏数据。我们需要一个合适的协议来处理一些特殊的场景。例如你可以实现一个优化的Ajax的聊天应用、媒体流传输或者是大文件传输的HTTP服务器,你甚至可以自己设计和实现一个新的协议来准确地实现你的需求。

另外不可避免的事情是你不得不处理这些私有协议来确保和原有系统的互通。这个例子将会展示如何快速实现一个不影响应用程序稳定性和性能的协议。

解决方案

Netty是一个提供异步事件驱动的网络应用框架,用以快速开发高性能、高可靠性的网络服务器和客户端程序。

换句话说,Netty是一个NIO框架,使用它可以简单快速地开发网络应用程序,比如客户端和服务端的协议。Netty大大简化了网络程序的开发过程比如TCP和UDP的 Socket的开发。

“快速和简单”并不意味着应用程序会有难维护和性能低的问题,Netty是一个精心设计的框架,它从许多协议的实现中吸收了很多的经验比如FTP、SMTP、HTTP、许多二进制和基于文本的传统协议,Netty在不降低开发效率、性能、稳定性、灵活性情况下,成功地找到了解决方案。

有一些用户可能已经发现其他的一些网络框架也声称自己有同样的优势,所以你可能会问是Netty和它们的不同之处。答案就是Netty的哲学设计理念。Netty从第一天开始就为用户提供了用户体验最好的API以及实现设计。正是因为Netty的设计理念,才让我们得以轻松地阅读本指南并使用Netty。

这是官网介绍。

netty主要用于通讯,既然是通讯,就少不了客户端和服务端,并且信息的通讯一般都是以字节发送,先简单的写个服务端的例子,使用telnet命令连接服务端测试服务是否能工作。

1.客户端启动类

package com.zapp;

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

/**
 * @author zwd
 * @date 2018/1/8 15:58
 */
public class DiscardServer {
    private int port;

    public DiscardServer(int port) {
        this.port = port;
    }
    public void run() throws Exception{
        EventLoopGroup bossGroup=new NioEventLoopGroup();
        EventLoopGroup workerGroup=new NioEventLoopGroup();
        try{
            ServerBootstrap b=new ServerBootstrap();
            b.group(bossGroup,workerGroup)
                    .channel(NioServerSocketChannel.class)
                    .childHandler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel socketChannel) {
                            socketChannel.pipeline().addLast(new DiscardServerHandler());
                        }
                    }).option(ChannelOption.SO_BACKLOG,128)
                        .childOption(ChannelOption.SO_KEEPALIVE,true);
            ChannelFuture f=b.bind(port).sync();
            f.channel().closeFuture().sync();
        }finally {
            workerGroup.shutdownGracefully();
            bossGroup.shutdownGracefully();
        }
    }
    public static void main(String[] args) throws Exception {
        int port;
        if (args.length > 0) {
            port = Integer.parseInt(args[0]);
        } else {
            port = 8080;
        }
        new DiscardServer(port).run();
    }
}

2.server处理类

package com.zapp;

import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerAdapter;
import io.netty.channel.ChannelHandlerContext;
import io.netty.util.ReferenceCountUtil;

/**
 * @author zwd
 * @date 2018/1/8 15:41
 */
public class DiscardServerHandler extends ChannelHandlerAdapter{

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) { // (2)
//        // Discard the received data silently.
//        ByteBuf in=(ByteBuf) msg; // (3)
//        try{
//            while (in.isReadable()){
//                System.out.println((char) in.readByte());
//                System.out.flush();
//            }
//        }finally {
//            ReferenceCountUtil.release(msg);
//        }
        ctx.write(msg);
        ctx.flush();
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { // (4)
        // Close the connection when an exception is raised.
        cause.printStackTrace();
        ctx.close();
    }

}

其实代码很简单,就是开个端口监听是否有信息传入,当有信息传递过来时,执行channelRead方法。


现在开始添加一些辅助类,不知道你有没有发现我们传递的数据使用bytebuf接受的,这样和我们平常传递的对象使用起来还是有点不舒服的,所有netty使用了一个辅助类帮我们把对象进行拆箱、装箱。

下面我想实现的功能逻辑是:后台返回给我一个对象,client读取这个对象

这里就不粘贴以bytebuf来传递数据的代码了,直接给封装的代码:

1.服务端的启动

package com.zapp.time;

import com.zapp.DiscardServer;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;

/**
 * @author zwd
 * @date 2018/1/9 08:56
 */
public class TimeServer {
    private int port;

    public TimeServer(int port) {
        this.port = port;
    }
    public void run() throws Exception{
        EventLoopGroup bossGroup=new NioEventLoopGroup();
        EventLoopGroup workerGroup=new NioEventLoopGroup();
        try{
            ServerBootstrap b=new ServerBootstrap();
            b.group(bossGroup,workerGroup)
                    .channel(NioServerSocketChannel.class)
                    .childHandler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel socketChannel) {
                            socketChannel.pipeline().addLast(new TimeEncoder(),new TimeServerHandler());
                        }
                    }).option(ChannelOption.SO_BACKLOG,128)
                    .childOption(ChannelOption.SO_KEEPALIVE,true);
            ChannelFuture f=b.bind(port).sync();
            f.channel().closeFuture().sync();
        }finally {
            workerGroup.shutdownGracefully();
            bossGroup.shutdownGracefully();
        }
    }
    public static void main(String[] args) throws Exception {
        int port;
        if (args.length > 0) {
            port = Integer.parseInt(args[0]);
        } else {
            port = 8080;
        }
        System.out.println(port);
        new TimeServer(port).run();
    }
}


2.服务端的处理类

package com.zapp.time;

import com.zapp.time.entity.UnixTime;
import io.netty.buffer.ByteBuf;
import io.netty.channel.*;

/**
 * @author zwd
 * @date 2018/1/8 19:51
 */
public class TimeServerHandler extends ChannelInboundHandlerAdapter{
    /**
     * 启动端口监听服务,当有client来访问这个端口时,执行此方法,此方法不接受client传递过来的
     * 消息,只负责传递消息给client并立即关闭请求
     * @param ctx
     */
    @Override
   public void channelActive(final ChannelHandlerContext ctx) { // (1)
//        final ByteBuf time = ctx.alloc().buffer(4); //
//        time.writeInt((int) (System.currentTimeMillis() / 1000L + 2208988800L));
//        System.out.println(time);
//        final ChannelFuture f = ctx.writeAndFlush(time); // (3)
//        f.addListener(new ChannelFutureListener() {
//            @Override
//            public void operationComplete(ChannelFuture future) {
//                assert f == future;
//                ctx.close();
//            }
//        }); // (4)
    /**
     *对象的使用
     *
     */
        System.out.println("aaaa");
        UnixTime unixTime=new UnixTime();
        System.out.println(unixTime);
        final ChannelFuture f=ctx.writeAndFlush(new UnixTime());
    f.addListener(ChannelFutureListener.CLOSE);
    }

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

3.服务端的辅助类(用于将服务端返回的对象处理成bytebuf对象)

package com.zapp.time;

import com.zapp.time.entity.UnixTime;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerAdapter;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelPromise;
import io.netty.handler.codec.ByteToMessageDecoder;

/**
 * @author zwd
 * @date 2018/1/9 10:44
 */
public class TimeEncoder extends ChannelHandlerAdapter{

    @Override
    public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
        System.out.println("1111");
        UnixTime m= (UnixTime) msg;
        ByteBuf encoded=ctx.alloc().buffer(4);
        encoded.writeLong(m.value());
        ctx.write(encoded,promise);
    }
}


4.客户端的启动类


package com.zapp.time;

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


/**
 * @author zwd
 * @date 2018/1/8 19:52
 */
public class TimeClient {
    public static void main(String[] args) throws Exception {
        String host = "localhost";
        int port = Integer.parseInt("8080");
        EventLoopGroup workerGroup = new NioEventLoopGroup();

        try {
            Bootstrap b = new Bootstrap(); // (1)
            b.group(workerGroup); // (2)
            b.channel(NioSocketChannel.class); // (3)
            b.option(ChannelOption.SO_KEEPALIVE, true); // (4)
            b.handler(new ChannelInitializer<SocketChannel>() {
                @Override
                public void initChannel(SocketChannel ch) throws Exception {
                    ch.pipeline().addLast(new TimeDecoder(),new TimeClientHandler());
                }
            });

            // Start the client.
            ChannelFuture f = b.connect(host, port).sync(); // (5)

            // Wait until the connection is closed.
            f.channel().closeFuture().sync();
        } finally {
            workerGroup.shutdownGracefully();
        }
    }
}

5.客户端的处理类

package com.zapp.time;

import com.zapp.time.entity.UnixTime;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerAdapter;
import io.netty.channel.ChannelHandlerContext;

import java.util.Date;

/**
 * @author zwd
 * @date 2018/1/8 19:56
 */
public class TimeClientHandler extends ChannelHandlerAdapter {

    private ByteBuf buf;

    @Override
    public void handlerAdded(ChannelHandlerContext ctx) {
        buf = ctx.alloc().buffer(4); // (1)
    }

    @Override
    public void handlerRemoved(ChannelHandlerContext ctx) {
        buf.release(); // (1)
        buf = null;
    }

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) {

        System.out.println("wwww");
        UnixTime m = (UnixTime) msg;
        System.out.println(m);
        ctx.close();
//        buf.writeBytes(m); // (2)
//        try{
//            long currentTime=(buf.readUnsignedInt()-2208988800L)*1000L;
//            System.out.println(new Date(currentTime));
//            ctx.close();
//        }finally {
//            m.release();
//        }
//
//        if (buf.readableBytes() >= 4) { // (3)
//            long currentTimeMillis = (buf.readUnsignedInt() - 2208988800L) * 1000L;
//            System.out.println(new Date(currentTimeMillis));
//            ctx.close();
//        }
    }

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


6.客户端的辅助类(用于将bytebuf转化为对象)
package com.zapp.time;

import com.zapp.time.entity.UnixTime;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.ByteToMessageDecoder;
import io.netty.handler.codec.ReplayingDecoder;

import java.util.List;

/**
 * @author zwd
 * @date 2018/1/9 09:49
 */
public class TimeDecoder extends ReplayingDecoder{
    @Override
    protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
        System.out.println("zzzz");
        if (in.readableBytes()<4) {
           return;
      }
        System.out.println(in.readInt());
        out.add(new UnixTime((int) in.readUnsignedInt()));
    }
}


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章