一個異步無限發送的Netty實例

本博客 貓叔的博客,轉載請申明出處

閱讀本文約 “4分鐘”

適讀人羣:Java-Netty 初級

無限異步發送數據流

版本:netty 4.1.*

申明:本文旨在重新分享討論Netty官方相關案例,添加部分個人理解與要點解析。

這個是InChat的案例地址,裏面補充了詳細的註釋,比起官方會容易看一點。

官方案例地址:https://netty.io/4.1/xref/io/...

正文

  • DiscardClient(客戶端)
  • DiscardClientHandler
  • DiscardServer(服務端)
  • DiscardServerHandler

要點介紹

實現了抽象基類ChannelInboundHandler,因此也提供其所有方法的實現,子類可以重寫方法實現來改變它。

注意:channelRead(ChannelHandlerContext, Object) 方法自動返回後不會釋放消息。如果您正在尋找ChannelInboundHandler自動發佈收到的消息的實現,請參閱SimpleChannelInboundHandler

允許顯式只處理特定類型的消息

從當前開始 用(0x00)填充此緩衝區writerIndex並按writerIndex指定值增加length。如果this.writableBytes小於length,ensureWritable(int) 將調用以嘗試擴展容量以適應。

使用給定的初始值給ByteBuf分配直接值

每一個新分配的ByteBuf的引用計數值爲1,每對這個ByteBuf對象增加一個引用,需要調用ByteBuf.retain()方法,而每減少一個引用,需要調用ByteBuf.release()方法。當這個ByteBuf對象的引用計數值爲0時,表示此對象可回收

返回保留的緩衝區,該緩衝區共享此緩衝區的整個區域。修改返回的緩衝區或此緩衝區的內容會影響彼此的內容,同時它們會維護單獨的索引和標記。此方法的行爲類似於此方法,duplicate().retain()但此方法可能返回產生較少垃圾的緩衝區實現。

監聽一個ChannelFuture的結果。Channel一旦通過調用添加此偵聽器,將以ChannelFuture.addListener(GenericFutureListener)異步I / O的操作通知結果。

項目源碼

  • DiscardClient(客戶端)
/**
 * @ClassName DiscardClient
 * @Description TODO
 * @Author MySelf
 * @Date 2019/8/19 20:38
 * @Version 1.0
 **/
public final class DiscardClient {

    //判斷是否加密
    static final boolean SSL = System.getProperty("ssl") != null;
    //監聽本地服務
    static final String HOST = System.getProperty("host", "127.0.0.1");
    //監聽端口
    static final int PORT = Integer.parseInt(System.getProperty("port", "8007"));
    //發送消息的大小,用於EchoClientHandler
    static final int SIZE = Integer.parseInt(System.getProperty("size", "256"));

    public static void main(String[] args) throws Exception {
        //公共抽象類,安全套接字協議實現充當工廠SSLEngine和SslHandler。在內部,它通過JDK SSLContext或OpenSSL 實現SSL_CTX
        final SslContext sslCtx;
        if (SSL){
            //用於配置新SslContext以進行創建的構建器
            sslCtx = SslContextBuilder.forClient()
                    //用於驗證遠程端點證書的可信管理器
                    //InsecureTrustManagerFactory:在TrustManagerFactory沒有任何驗證的情況下信任所有X.509證書的不安全因素
                    //注:切勿TrustManagerFactory在生產中使用它。它純粹是出於測試目的,因此非常不安全。
                    .trustManager(InsecureTrustManagerFactory.INSTANCE).build();
        }else {
            sslCtx = null;
        }
        //事件循環
        EventLoopGroup group = new NioEventLoopGroup();
        try {
            Bootstrap b = new Bootstrap();
            b.group(group)
                    .channel(NioSocketChannel.class)
                    .option(ChannelOption.TCP_NODELAY,true)
                    .handler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel sc) throws Exception {
                            ChannelPipeline p = sc.pipeline();
                            //瞭解SslContext的用法
                            if (sslCtx != null){
                                p.addLast(sslCtx.newHandler(sc.alloc(),HOST,PORT));
                            }
                            p.addLast(new DiscardClientHandler());
                        }
                    });

            ChannelFuture f = b.connect(HOST,PORT).sync();

            f.channel().closeFuture().sync();
        }finally {
            group.shutdownGracefully();
        }
    }

}
  • DiscardClientHandler
/**
 * @ClassName DiscardClientHandler
 * @Description TODO
 * @Author MySelf
 * @Date 2019/8/19 20:38
 * @Version 1.0
 **/
public class DiscardClientHandler extends SimpleChannelInboundHandler<Object> {

    private ByteBuf content;
    private ChannelHandlerContext ctx;

    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        this.ctx = ctx;

        // 初始化消息
        content = ctx.alloc().directBuffer(DiscardClient.SIZE).writeZero(DiscardClient.SIZE);

        // 發送初始信息
        generateTraffic();
    }

    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        //引用計數爲0,釋放
        content.release();
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        // 引發異常時關閉連接
        cause.printStackTrace();
        ctx.close();
    }

    @Override
    protected void channelRead0(ChannelHandlerContext channelHandlerContext, Object o) throws Exception {
        // 服務器應該不發送任何內容,但如果它發送什麼,丟棄它。
    }

    // 生成數據
    private void generateTraffic(){
        // 將出站緩衝區刷新到套接字
        // 刷新後,再次生成相同數量的流量
        ctx.writeAndFlush(content.retainedDuplicate()).addListener(trafficGenerator);
    }

    // 數據觸發
    private final ChannelFutureListener trafficGenerator = new ChannelFutureListener() {
        //完成操作後的方法調用,即只要成功無限調用generateTraffic()
        @Override
        public void operationComplete(ChannelFuture channelFuture) throws Exception {
            if (channelFuture.isSuccess()){
                generateTraffic();
            }else {
                channelFuture.cause().printStackTrace();
                channelFuture.channel().close();
            }
        }
    };

}
  • DiscardServer(服務端)
/**
 * @ClassName DiscardServer
 * @Description TODO
 * @Author MySelf
 * @Date 2019/8/19 20:38
 * @Version 1.0
 **/
public class DiscardServer {

    static final boolean SSL = System.getProperty("ssl") != null;
    static final int PORT = Integer.parseInt(System.getProperty("port", "8007"));

    public static void main(String[] args) throws Exception {
        final SslContext sslCtx;
        if (SSL){
            //SelfSignedCertificate:生成臨時自簽名證書以進行測試
            SelfSignedCertificate ssc = new SelfSignedCertificate();
            sslCtx = SslContextBuilder.forServer(ssc.certificate(), ssc.privateKey()).build();
        }else{
            sslCtx = null;
        }

        EventLoopGroup bossGroup = new NioEventLoopGroup(1);
        EventLoopGroup workerGroup = new NioEventLoopGroup();

        try {
            ServerBootstrap b = new ServerBootstrap();
            b.group(bossGroup,workerGroup)
                    .channel(NioServerSocketChannel.class)
                    .option(ChannelOption.SO_BACKLOG,100)
                    .handler(new LoggingHandler(LogLevel.INFO))
                    .childHandler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel ch) throws Exception {
                            ChannelPipeline p = ch.pipeline();
                            if (sslCtx != null){
                                p.addLast(sslCtx.newHandler(ch.alloc()));
                            }
                            p.addLast(new DiscardServerHandler());
                        }
                    });

            // 綁定並開始接受傳入連接
            ChannelFuture f = b.bind(PORT).sync();

            // 等待服務器套接字關閉
            // 這個例子,這不會發生,但你可以這樣優雅的做
            f.channel().closeFuture().sync();

        }finally {
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }

    }

}
  • DiscardServerHandler
/**
 * @ClassName DiscardServerHandler
 * @Description TODO
 * @Author MySelf
 * @Date 2019/8/19 20:39
 * @Version 1.0
 **/
public class DiscardServerHandler extends SimpleChannelInboundHandler<Object> {

    @Override
    protected void channelRead0(ChannelHandlerContext channelHandlerContext, Object o) throws Exception {
        // discard
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        // 引發異常時關閉連接
        cause.printStackTrace();
        ctx.close();
    }
}

公衆號:Java貓說

學習交流羣:728698035

現架構設計(碼農)兼創業技術顧問,不羈平庸,熱愛開源,雜談程序人生與不定期乾貨。

Image Text

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