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()));
    }
}


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