淺入淺出Netty(二) Netty

這一遍先簡單的講一些netty是如何實現nio的代碼的,後面會詳細講述netty原理

一、Netty是什麼

用一句簡單的話來說就是:Netty 封裝了 JDK 的 NIO,讓你用得更爽,你不用再寫一大堆複雜的代碼了。

用官方正式的話來說就是:Netty 是一個異步事件驅動的網絡應用框架,用於快速開發可維護的高性能服務器和客戶端。

二、爲什麼要使用Netty而不使用NIO

  • 使用 JDK 自帶的NIO需要了解太多的概念,編程複雜,一不小心 bug 橫飛
  • Netty 底層 IO 模型隨意切換,而這一切只需要做微小的改動,改改參數,Netty可以直接從 NIO 模型變身爲 IO 模型
  • Netty 自帶的拆包解包,異常檢測等機制讓你從NIO的繁重細節中脫離出來,讓你只需要關心業務邏輯
  • Netty 解決了 JDK 的很多包括空輪詢在內的 Bug
  • Netty 底層對線程,selector 做了很多細小的優化,精心設計的 reactor 線程模型做到非常高效的併發處理
  • 自帶各種協議棧讓你處理任何一種通用協議都幾乎不用親自動手
  • Netty 社區活躍,遇到問題隨時郵件列表或者 issue
  • Netty 已經歷各大 RPC框架,消息中間件,分佈式通信中間件線上的廣泛驗證,健壯性無比強大

三、用Netty實現之前的功能

首先添加pom依賴

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

然後是代碼部分

public class Client {

    public static void main(String[] args) {
        EventLoopGroup nioEventLoopGroup = new NioEventLoopGroup();
        Bootstrap bootstrap = new Bootstrap();//客戶端引導類
        //EventLoopGroup可以理解爲是一個線程池,這個線程池用來處理連接、接受數據、發送數據
        bootstrap.group(nioEventLoopGroup)//多線程處理
                .channel(NioSocketChannel.class)//制定通道類型爲NioSocketChannel
                .handler(new ChannelInitializer<SocketChannel>() {//業務處理類
                    @Override
                    protected void initChannel(SocketChannel ch) throws Exception {
                        ch.pipeline().addLast(new ClientHandler());//註冊handler
                    }
                });
        // 4.建立連接
        bootstrap.connect("127.0.0.1", 8000).addListener(future -> {
            if (future.isSuccess()) {
                System.out.println("連接成功!");
            } else {
                System.err.println("連接失敗!");
            }
        });

    }
}

public class ClientHandler extends SimpleChannelInboundHandler<ByteBuf> {

    //客戶端連接服務器後被調用
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        System.out.println("客戶端連接服務器,開始發送數據......");
        byte[] req = "QUERY TIME ORDER".getBytes();//請求消息
        ByteBuf firstMessage = Unpooled.buffer(req.length);//發送類
        firstMessage.writeBytes(req);//發送
        ctx.writeAndFlush(firstMessage);//flush
    }

    //從服務器收到數據後調用
    @Override
    protected void channelRead0(ChannelHandlerContext ctx, ByteBuf buf) throws Exception {
        System.out.println("client 讀取server數據..");
        //服務器返回消息後
        byte[] req = new byte[buf.readableBytes()];//創建一個存儲信息的byte數組
        buf.readBytes(req);//將buffer中的數據讀到byte數組中
        String body = new String(req, "UTF-8");//將byte數組轉換爲String(並轉碼)
        System.out.println("服務端數據爲:" + body);//打印服務端反饋的信息
    }

    //發生異常時調用
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        System.out.println("client exceptionCaught...");
        //釋放資源
        ctx.close();
    }
}

public class Server {

    public static void main(String[] args) {
        // bossGroup表示監聽端口,accept 新連接的線程組,workerGroup表示處理每一條連接的數據讀寫的線程組
        NioEventLoopGroup bossGroup = new NioEventLoopGroup();
        NioEventLoopGroup workerGroup = new NioEventLoopGroup();
        //server端引導類,來引導綁定和啓動服務器;
        ServerBootstrap serverBootstrap = new ServerBootstrap();
        //裝配ServerBootstrap
        serverBootstrap.group(bossGroup, workerGroup)//多線程處理
                //制定通道類型爲NioServerSocketChannel,一種異步模式的可以監聽新進來的TCP連接的通道
                .channel(NioServerSocketChannel.class)
                //設置childHandler執行所有的連接請求
                //註冊handler
                .childHandler(new ChannelInitializer<Channel>() {
                    @Override
                    protected void initChannel(Channel ch) throws Exception {
                        ch.pipeline().addLast(new ServerHandler());
                    }
                });

        serverBootstrap.bind(8000);
    }

}

public class ServerHandler extends ChannelInboundHandlerAdapter {

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        System.out.println("server 讀取數據......");
        //讀取數據
        ByteBuf buf = (ByteBuf) msg;
        byte[] req = new byte[buf.readableBytes()];
        buf.readBytes(req);
        String body = new String(req, "UTF-8");
        System.out.println("接收客戶端數據:" + body);
        //向客戶端寫數據
        System.out.println("server向client發送數據");
        String currentTime = new Date(System.currentTimeMillis()).toString();
        ByteBuf resp = Unpooled.copiedBuffer(currentTime.getBytes());
        ctx.write(resp);
    }

    @Override
    public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
        System.out.println("server 讀取數據完畢...");
        ctx.flush();//刷新後纔將數據發出到SocketChannel
    }

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

}

可以先嚐試一下,後面會詳細講netty的原理

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