NettyプロトコルのTCP(一)

23873 ワード

前言
この章では、Nettyを使用してTCPサーバを構築する方法について説明します.このシリーズでは、Netty自体のフレームワークについては詳しく説明しません.
TCPプロトコル
トランスポート制御プロトコル(TCP,Transmission Control Protocol)は、接続向けで信頼性が高く、バイトストリームベースのトランスポート層通信プロトコルである.
Nettyのサポート
依存パッケージのインポート
// gradle 
compile group: 'io.netty', name: 'netty-all', version: '4.1.50.Final'
// maven
<dependency>
    <groupId>io.netty</groupId>
    <artifactId>netty-all</artifactId>
    <version>4.1.50.Final</version>
</dependency>

Serverサービス
イベントスレッドグループの作成
EventLoopGroup bossGroup = new NioEventLoopGroup(1);
EventLoopGroup workerGroup = new NioEventLoopGroup(6);

bossGroupでは、すべてのクライアント接続の要求を処理し、新しい接続(channel)を作成し、workerGroupのNioEventLoopに登録する役割を果たすスレッド(ここではNioEventLoop、NIO SELECTOR、キュー、スレッドを含む)を初期化します.その後、チャネルのすべての読み書き操作は、チャネルにバインドされたNioEventLoopによって処理される.
workerGroupは上記のように、主に読み取り/書き込み操作を処理し、ここでは6つのスレッド(NioEventLoop)を初期化して実行します.
Channelのライフサイクル全体は同じNioEventLoopによって処理されます.
サービスイニシエータ
ServerBootstrap serverBootstrap = new ServerBootstrap();
serverBootstrap.group(bossGroup,workerGroup)
        .channel(NioServerSocketChannel.class)
        .option(ChannelOption.SO_BACKLOG, 1024)
        .childOption(ChannelOption.SO_KEEPALIVE, true)
        .childHandler(new ChannelInitializer<SocketChannel>() {
            @Override
            protected void initChannel(SocketChannel socketChannel) throws Exception {
                //        
                socketChannel.pipeline()
                        .addLast(new StringEncoder())
                        .addLast(new StringDecoder())
                        .addLast(new TcpNettyHandler());
            }
        });
  • serverBootstrap:サービス起動ブートには、プロセッサ、起動ポート、バインドIP、チャネル構成などを設定できます.
  • group:スレッドグループをバインドします.
  • NioServerSocketChannel:NIOモード.
  • option(ChannelOption.SO_BACKLOG,1024):サーバ要求処理スレッドが完全に満たされているときに、3回の握手を完了した要求を一時的に格納するためのキューの最大長を識別する.
  • childOption(ChannelOption.SO_KEEPALIVE,true):心拍保護機構を有効にする
  • ここでは、optionとchildOptionの違いについて説明する(以下childHandlerと同様).
  • optionは主にbossスレッドに作用し、新しい接続を処理するために使用される.
  • childOptionの主な役割は、workerスレッド、すなわち作成されたchannelです.

  • handlerプロセッサ
    以上がチャネルの構成であり,ここまでがデータ処理の鍵である.
    コーデック
    前述したように、TCPはバイトストリームに基づく伝送プロトコルであるため、バイトストリームを処理するためにコーデックを使用する必要がある.本稿では,文字列処理とする.Nettyは、自分で作成する必要がなく、多くのプロトコルコーデックを提供しています.
    StringDecoder:入力したバイトストリームを文字列に復号する文字列デコーダ.StringEncoder:文字列エンコーダで、出力されたStringをバイトストリームに符号化します.
    ソースコードはhandlerに属していることがわかります.
    TcpServerNettyHandler
    import io.netty.channel.ChannelHandler;
    import io.netty.channel.ChannelHandlerContext;
    import io.netty.channel.SimpleChannelInboundHandler;
    
    /**
     * Created by chenws on 2020/3/24.
     */
    @ChannelHandler.Sharable
    public class TcpServerNettyHandler extends SimpleChannelInboundHandler<String> {
    
        @Override
        protected void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception {
            System.out.println(msg);
            //        
            ctx.write("I get the message " + msg);
        }
    
        @Override
        public void channelReadComplete(ChannelHandlerContext ctx) {
            //     ,      
            ctx.flush();
        }
    
        @Override
        public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
            //    ,   handler   ,        handler
            cause.printStackTrace();
        }
    
        @Override
        public void channelActive(ChannelHandlerContext ctx) throws Exception {
            ctx.writeAndFlush("connect success!");
        }
    }
    
    

    このhandlerはSimpleChannelInboundHandlerを継承し、ChannelInboundHandler Adapterを継承しています.実はデータのタイプを変換して、汎用的に体現しています.ここではを指定しました.
  • channelRead 0:channelがデータを受信すると、このメソッドが実行されます.
  • channelReadComplete:この回数は伝送受信が完了すると、この方法が実行されます.
  • exceptionCaught:データ受信中に例外が発生し、このメソッドが実行され、実装されない場合、例外は次のhandlerに渡されます.
  • channelActive:channelの確立に成功すると、この方法が実行されます.

  • ここで質問があります.channelReadCompleteはいつトリガーされますか?言い換えれば、Nettyはどのようにして今回の伝送が読み終わったことを知っていますか?2つの条件があり、1つを満たすと実行されます.
  • readは0バイト
  • を返す
  • readがそのバッファに渡す1024バイトは充填されるが、最終的に1024バイト未満は充填される.

  • Bind
    ChannelFuture channelFuture = serverBootstrap.bind(8080).sync();
    

    最後のステップはポートをバインドし、ネイティブのIPアドレスをデフォルトでバインドします.
    これでTCP Nettyのサービスは完了しました.次にクライアントを見てみましょう.
    Clientクライアント
    ほとんどのコードのロジックはserverサービスとあまり差がありません.これもnettyの強みです.次は直接貼ります.
    EventLoopGroup eventLoopGroup = new NioEventLoopGroup(1);
    try{
        Bootstrap bootstrap = new Bootstrap();
        bootstrap.group(eventLoopGroup)
    	    .channel(NioServerSocketChannel.class)
    	    .option(ChannelOption.SO_BACKLOG, 1024)
    	    .handler(new ChannelInitializer<SocketChannel>() {
    	        @Override
    	        protected void initChannel(SocketChannel socketChannel) throws Exception {
    	            socketChannel.pipeline()
    	                    .addLast(new StringEncoder())
    	                    .addLast(new StringDecoder())
    	                    .addLast(new TcpClientNettyHandler());
    	        }
    	    });
        ChannelFuture channelFuture = bootstrap.connect("127.0.0.1", 8080).sync();
    } catch (InterruptedException e) {
        e.printStackTrace();
    } finally {
        eventLoopGroup.shutdownGracefully();
    }
    
    import io.netty.channel.ChannelHandlerContext;
    import io.netty.channel.SimpleChannelInboundHandler;
    import lombok.extern.slf4j.Slf4j;
    
    @Slf4j
    public class TcpClientNettyHandler extends SimpleChannelInboundHandler<String> {
    
        @Override
        protected void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception {
            log.info("          :{}", msg);
            ctx.write(msg);
        }
    
        @Override
        public void channelActive(ChannelHandlerContext ctx) throws Exception {
            super.channelActive(ctx);
        }
    
        @Override
        public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
            //    
            ctx.flush();
        }
    
        @Override
        public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
            super.exceptionCaught(ctx, cause);
        }
    }
    
    

    主な違いは、サーバBootstrapを使用せず、Bootstrapを使用してクライアントを起動することです.コードには難易度はありませんが、ここでは展開しません.
    まとめ
    ここまで、NettyとTCPを組み合わせて紹介しましたが、総じて言えば、Nettyは私たちにいろいろなことをしてくれました.Nettyに興味があれば、いつでも私のブログに注目してください.
    ソース:Netty-Larn