Netty 初識 Demo

 這是一個netty demo程序,在此不解讀原理及API的詳情,主要用於學習保留,這個demo我只對部份朋友可見,如果沒接觸過netty,又想要學習netty的朋友,建議先了解JDK的IO模型。後續有時間的話會總結netty的底層原理(如:粘包拆包、心跳機制、底層零拷貝等等),如果有什麼問題也可以留言相互學習

服務端代碼

package com.patrick.netty.demo;

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;

public class NettyServer {
    public static void main(String[] args) throws Exception {

        //創建兩個線程組bossGroup和workerGroup, 含有的子線程NioEventLoop的個數默認爲cpu核數的兩倍
        // bossGroup只是處理連接請求 ,真正的和客戶端業務處理,會交給workerGroup完成
        EventLoopGroup bossGroup = new NioEventLoopGroup(1);
        EventLoopGroup workerGroup = new NioEventLoopGroup();
        try {
            //創建服務器端的啓動對象
            ServerBootstrap bootstrap = new ServerBootstrap();
            //使用鏈式編程來配置參數
            bootstrap.group(bossGroup, workerGroup) //設置兩個線程組
                    .channel(NioServerSocketChannel.class) //使用NioServerSocketChannel作爲服務器的通道實現
                    // 初始化服務器連接隊列大小,服務端處理客戶端連接請求是順序處理的,所以同一時間只能處理一個客戶端連接。
                    // 多個客戶端同時來的時候,服務端將不能處理的客戶端連接請求放在隊列中等待處理
                    .option(ChannelOption.SO_BACKLOG, 1024)
                    .childHandler(new ChannelInitializer<SocketChannel>() {//創建通道初始化對象,設置初始化參數

                        @Override
                        protected void initChannel(SocketChannel ch) throws Exception {
                            //對workerGroup的SocketChannel設置處理器codec
                            ch.pipeline().addLast(new NettyServerHandler()); //添加自定義的操作類
                        }
                    });
            System.out.println("netty server start。。");
            //綁定一個端口並且同步, 生成了一個ChannelFuture異步對象,通過isDone()等方法可以判斷異步事件的執行情況
            //啓動服務器(並綁定端口),bind是異步操作,sync方法是等待異步操作執行完畢
            ChannelFuture cf = bootstrap.bind(9000).sync();
            //給cf註冊監聽器,監聽我們關心的事件
           /*cf.addListener(new ChannelFutureListener() {
                @Override
                public void operationComplete(ChannelFuture future) throws Exception {
                    if (cf.isSuccess()) {
                        System.out.println("監聽端口9000成功");
                    } else {
                        System.out.println("監聽端口9000失敗");
                    }
                }
            });*/
            //對通道關閉進行監聽,closeFuture是異步操作,監聽通道關閉
            // 通過sync方法同步等待通道關閉處理完畢,這裏會阻塞等待通道關閉完成
            cf.channel().closeFuture().sync();
        } finally {
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }
    }

}



package com.patrick.netty.demo;

import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.util.CharsetUtil;

/**
 * 自定義Handler需要繼承netty規定好的某個HandlerAdapter(規範)
 */

public class NettyServerHandler extends ChannelInboundHandlerAdapter {

    /**
     * 讀取客戶端發送的數據
     *
     * @param ctx 上下文對象, 含有通道channel,管道pipeline
     * @param msg 就是客戶端發送的數據
     * @throws Exception
     */
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        System.out.println("服務器讀取線程 " + Thread.currentThread().getName());
        //Channel channel = ctx.channel();
        //ChannelPipeline pipeline = ctx.pipeline(); //本質是一個雙向鏈接, 出站入站
        //將 msg 轉成一個 ByteBuf,類似NIO 的 ByteBuffer
        ByteBuf buf = (ByteBuf) msg;
        System.out.println("客戶端發送消息是:" + buf.toString(CharsetUtil.UTF_8));
    }

    /**
     * 數據讀取完畢處理方法
     *
     * @param ctx
     * @throws Exception
     */
    @Override
    public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
        ByteBuf buf = Unpooled.copiedBuffer("HelloClient", CharsetUtil.UTF_8);
        ctx.writeAndFlush(buf);
    }

    /**
     * 處理異常, 一般是需要關閉通道
     *
     * @param ctx
     * @param cause
     * @throws Exception
     */
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        cause.printStackTrace();
        ctx.close();
    }
}



客戶端代碼

package com.patrick.netty.demo;

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;

public class NettyClient {
    public static void main(String[] args) throws Exception {
        //客戶端需要一個事件循環組
        EventLoopGroup group = new NioEventLoopGroup();
        try {
            //創建客戶端啓動對象
            //注意客戶端使用的不是 ServerBootstrap 而是 Bootstrap
            Bootstrap bootstrap = new Bootstrap();
            //設置相關參數
            bootstrap.group(group) //設置線程組
                    .channel(NioSocketChannel.class) // 使用 NioSocketChannel 作爲客戶端的通道實現
                    .handler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel channel) throws Exception {
                            //加入處理器
                            channel.pipeline().addLast(new NettyClientHandler());
                        }
                    });
            System.out.println("netty client start");
            //啓動客戶端去連接服務器端
            ChannelFuture channelFuture = bootstrap.connect("127.0.0.1", 9000).sync();


            //對關閉通道進行監聽
            channelFuture.channel().closeFuture().sync();
        } finally {
            group.shutdownGracefully();
        }
    }
}



package com.patrick.netty.demo;

import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.util.CharsetUtil;

public class NettyClientHandler extends ChannelInboundHandlerAdapter {

    /**
     * 當客戶端連接服務器完成就會觸發該方法
     *
     * @param ctx
     * @throws Exception
     */
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        ByteBuf buf = Unpooled.copiedBuffer("HelloServer", CharsetUtil.UTF_8);
        ctx.writeAndFlush(buf);
    }

    //當通道有讀取事件時會觸發,即服務端發送數據給客戶端
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        ByteBuf buf = (ByteBuf) msg;
        System.out.println("收到服務端的消息:" + buf.toString(CharsetUtil.UTF_8));
        System.out.println("服務端的地址: " + ctx.channel().remoteAddress());
    }

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

}

 

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