天天看點

Netty 簡易實戰,都能看懂!

1.使用Netty能夠做什麼

開發異步、非阻塞的TCP網絡應用程式;

開發異步、非阻塞的UDP網絡應用程式;

開發異步檔案傳輸應用程式;

開發異步HTTP服務端和用戶端應用程式;

提供對多種編解碼架構的內建,包括谷歌的Protobuf、Jboss marshalling、Java序列化、壓縮編解碼、XML解碼、字元串編解碼等,這些編解碼架構可以被使用者直接使用;

提供形式多樣的編解碼基礎類庫,可以非常友善的實作私有協定棧編解碼架構的二次定制和開發;

基于職責鍊模式的Pipeline-Handler機制,使用者可以非常友善的對網絡事件進行攔截和定制;

所有的IO操作都是異步的,使用者可以通過Future-Listener機制主動Get結果或者由IO線程操作完成之後主動Notify結果,使用者的業務線程不需要同步等待;

IP黑白名單控制;

列印消息碼流;

流量控制和整形;

性能統計;

基于鍊路空閑事件檢測的心跳檢測

2. Netty常用類講解

在這裡我們就一些我們常用到的類做大緻的講解,然後再寫入門程式的時候大緻知道每一行都講了什麼。

EventLoop,EventLoopGroup

EventLoop目的是為Channel處理IO操作,一個EventLoop可以為多個Channel服務,EventLoopGroup會包含多個EventLoop。

BootStrap,ServerBootstrap

一個Netty應用通常由一個Bootstrap開始,它主要作用是配置整個Netty程式,串聯起各個元件。

ChannelInitializer

當一個連結建立時,我們需要知道怎麼來接收或者發送資料,當然,我們有各種各樣的Handler實作來處理它,那麼ChannelInitializer便是用來配置這些Handler,它會提供一個ChannelPipeline,并把Handler加入到ChannelPipeline。

Handler

為了支援各種協定和處理資料的方式,便誕生了Handler元件。Handler主要用來處理各種事件,這裡的事件很廣泛,比如可以是連接配接、資料接收、異常、資料轉換等。

ChannelInboundHandler

一個最常用的Handler。這個Handler的作用就是處理接收到資料時的事件,也就是說,我們的業務邏輯一般就是寫在這個Handler裡面的,ChannelInboundHandler就是用來處理我們的核心業務邏輯。

Future

在Netty中所有的IO操作都是異步的,是以,你不能立刻得知消息是否被正确處理,但是我們可以過一會等它執行完成或者直接注冊一個監聽,具體的實作就是通過Future和ChannelFutures,他們可以注冊一個監聽,當操作執行成功或失敗時監聽會自動觸發。總之,所有的操作都會傳回一個ChannelFuture。

3. 第一個Helloworld

上面我們已經對常用類進行說明,下面我們就使用這些類來建構我們的第一個入門程式,本示例我使用的是maven來建構工程,如果你使用的是普通的項目則跳過第一步。

首先引入maven jar包:

<dependency>
      <groupId>io.netty</groupId>
      <artifactId>netty-all</artifactId>
      <version>4.1.5.Final</version>
    </dependency>      

下面我們來寫用戶端:

public class HelloWorldClient {
    private  int port;
    private  String address;

    public HelloWorldClient(int port,String address) {
        this.port = port;
        this.address = address;
    }

    public void start(){
        EventLoopGroup group = new NioEventLoopGroup();

        Bootstrap bootstrap = new Bootstrap();
        bootstrap.group(group)
                .channel(NioSocketChannel.class)
                .handler(new ClientChannelInitializer());

        try {
            Channel channel = bootstrap.connect(address,port).sync().channel();
            BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
            for(;;){
                String msg = reader.readLine();
                if(msg == null){
                    continue;
                }             
                channel.writeAndFlush(msg + "\r\n");
            }         
        } catch (Exception e) {
            e.printStackTrace();
        }finally {
            group.shutdownGracefully();
        }

    }

    public static void main(String[] args) {
        HelloWorldClient client = new HelloWorldClient(7788,"127.0.0.1");
        client.start();
    }
}      

ChannelInitializer用來配置處理資料的handler:

public class ClientChannelInitializer extends  ChannelInitializer<SocketChannel> {

    protected void initChannel(SocketChannel socketChannel) throws Exception {
        ChannelPipeline pipeline = socketChannel.pipeline();

        /*
         * 這個地方的 必須和服務端對應上。否則無法正常解碼和編碼
         *
         * 解碼和編碼 我将會在下一節為大家詳細的講解。暫時不做詳細的描述
         *
         * /        
        pipeline.addLast("decoder", new StringDecoder());
        pipeline.addLast("encoder", new StringEncoder());

        // 我們自己的handler
        pipeline.addLast("handler", new HelloWorldClientHandler());
    }
}      
Netty 簡易實戰,都能看懂!
public class HelloWordServer {
    private int port;

    public HelloWordServer(int port) {
        this.port = port;
    }

    public void start(){
        EventLoopGroup bossGroup = new NioEventLoopGroup();
        EventLoopGroup workGroup = new NioEventLoopGroup();

        ServerBootstrap server = new ServerBootstrap().group(bossGroup,workGroup)
                                    .channel(NioServerSocketChannel.class)
                                    .childHandler(new ServerChannelInitializer());

        try {
            ChannelFuture future = server.bind(port).sync();
            future.channel().closeFuture().sync();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }finally {
            bossGroup.shutdownGracefully();
            workGroup.shutdownGracefully();
        }
    }

    public static void main(String[] args) {
        HelloWordServer server = new HelloWordServer(7788);
        server.start();
    }
}      

服務端的ChannelInitializer:

public class ServerChannelInitializer extends ChannelInitializer<SocketChannel> {
    @Override
    protected void initChannel(SocketChannel socketChannel) throws Exception {
        ChannelPipeline pipeline = socketChannel.pipeline();      

        // 字元串解碼 和 編碼
        pipeline.addLast("decoder", new StringDecoder());
        pipeline.addLast("encoder", new StringEncoder());

        // 自己的邏輯Handler
        pipeline.addLast("handler", new HelloWordServerHandler());
    }
}      

伺服器端的handler:

public class HelloWordServerHandler extends ChannelInboundHandlerAdapter {

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        System.out.println(ctx.channel().remoteAddress()+"===>server: "+msg.toString());
        ctx.write("received your msg");
        ctx.flush();
    }

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

上面伺服器端和用戶端的代碼都已經寫完,下面我們先啟動服務端,然後啟動用戶端,程式中我是在用戶端讓手動輸入,輸入結束之後回車,伺服器端即可接受資料。

用戶端:

Netty 簡易實戰,都能看懂!

服務端:

Netty 簡易實戰,都能看懂!