NettyのSocketプログラミングの詳細-サービス側とクライアントを構築し、データ転送を行う

7637 ワード

シーン


NettyはIDEAでHelloWorldサービス・エンドを構築し、Nettyの実行プロセスと重要なコンポーネントについて説明します.
https://blog.csdn.net/BADAO_LIUMANG_QIZHI/article/details/108592418
Nettyを構築するHelloWorldはすでに実装されており、Nettyを使用してSocketプログラミングを行い、サービス側とクライアントとの通信伝送データを構築する方法について説明します.
注意:
ブログ:https://blog.csdn.net/badao_liumang_qizhi公衆号の覇道に注目するプログラム猿はプログラミング関連の電子書籍、チュートリアルのプッシュと無料ダウンロードを獲得した.

インプリメンテーション


Socketサービス側構築


srcでパッケージを新規作成し、パッケージでSocketクラスをサービス側として新規作成し、mainメソッドを新規作成します.
package com.badao.nettySocket;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;

public class SocketServer {
    public static void main(String[] args) throws  Exception
    {
        EventLoopGroup bossGroup = new NioEventLoopGroup();
        EventLoopGroup workerGroup = new NioEventLoopGroup();
        try{
            // -Netty 
            ServerBootstrap serverBootstrap = new ServerBootstrap();
            //     
            //childHandler  
            serverBootstrap.group(bossGroup,workerGroup).channel(NioServerSocketChannel.class)
                    .childHandler(new SocketServerInitializer());
            // 
            ChannelFuture channelFuture = serverBootstrap.bind(70).sync();
            channelFuture.channel().closeFuture().sync();
        }finally {
            // 
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }
    }
}

上記のブログの流れと同様に、サービス側にもSocketサービス側のイニシエータSocketServer Initializer()が必要です.
新しいクラスSocketServerInitializerは、サービス側の初期化器として使用されます.
package com.badao.nettySocket;

import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.socket.SocketChannel;
import io.netty.handler.codec.LengthFieldBasedFrameDecoder;
import io.netty.handler.codec.LengthFieldPrepender;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;
import io.netty.util.CharsetUtil;

public class SocketServerInitializer extends ChannelInitializer {
    @Override
    protected void initChannel(SocketChannel ch) throws Exception {
        ChannelPipeline pipeline = ch.pipeline();
        pipeline.addLast(new LengthFieldBasedFrameDecoder(Integer.MAX_VALUE,0,4,0,4));
        pipeline.addLast(new LengthFieldPrepender(4));
        pipeline.addLast(new StringDecoder(CharsetUtil.UTF_8));
        pipeline.addLast(new StringEncoder(CharsetUtil.UTF_8));
        pipeline.addLast(new SocketServerHandler());
    }
}

イニシエータでは、ChannelInitializerを継承するだけで、汎用タイプはSocketChannnelです.
次に、initChannelメソッドを書き換え、チャネルを初期化する必要があります.
メソッドには、主に符号化復号のフォーマットであるNettyが付属するプロセッサを追加します.
最後にカスタムプロセッサを追加してリクエストを処理します.
カスタムサービス・エンド・プロセッシング・クラスSockerServer Handlerを追加します
package com.badao.nettySocket;

import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;

public class SocketServerHandler extends SimpleChannelInboundHandler {
    @Override
    protected void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception {
        System.out.println(" "+ctx.channel().remoteAddress()+" "+msg);
        ctx.channel().writeAndFlush(" :( : )");
    }

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

プロセッサではSimpleChannelInboundHandlerを継承する必要があります.汎用タイプはデータを転送するタイプStringです.
次に、chanelRead 0メソッドを書き換えます.このメソッドが具体的な処理方法です.
メソッドでクライアントのデータを受信し、クライアントにデータを送信します.
次にexceptionCaughtメソッドを書き換え、例外をキャプチャして出力し、例外が発生すると接続を閉じます.

Socketクライアント構築


Socketサービス側の構築と一致し、Socketクライアントの構築も同様のプロセスです.
クライアントクラスとして新しいSocketClientを作成し、mainメソッドを追加
package com.badao.nettySocket;

import io.netty.bootstrap.Bootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioSocketChannel;

public class SocketClient {
    public static void main(String[] args) throws  Exception {
        EventLoopGroup eventLoopGroup = new NioEventLoopGroup();
        try {

            Bootstrap bootstrap = new Bootstrap();

            bootstrap.group(eventLoopGroup).channel(NioSocketChannel.class)
                    .handler(new SocketClientInitializer());
            // 
            ChannelFuture channelFuture = bootstrap.connect("localhost", 70);
            channelFuture.channel().closeFuture().sync();
        } finally {
            // 
            eventLoopGroup.shutdownGracefully();

        }
    }
}

mainメソッドでは、上のサービス側がバインドした70ポートを接続します.ここでの起動クラスはBootstrapを使用していることに注意してください.
クライアントはhandlerメソッドを使用しており,サービス側との違いに注意する.
そしてクライアントにもクライアントのイニシエータが使用されているので新しいSocketClientInitializer
package com.badao.nettySocket;

import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.socket.SocketChannel;
import io.netty.handler.codec.LengthFieldBasedFrameDecoder;
import io.netty.handler.codec.LengthFieldPrepender;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;
import io.netty.util.CharsetUtil;

public class SocketClientInitializer extends ChannelInitializer {
    @Override
    protected void initChannel(SocketChannel ch) throws Exception {
        ChannelPipeline pipeline = ch.pipeline();
        pipeline.addLast(new LengthFieldBasedFrameDecoder(Integer.MAX_VALUE,0,4,0,4));
        pipeline.addLast(new LengthFieldPrepender(4));
        pipeline.addLast(new StringDecoder(CharsetUtil.UTF_8));
        pipeline.addLast(new StringEncoder(CharsetUtil.UTF_8));
        pipeline.addLast(new SocketClientHandler());
    }
}

追加されたプロセッサはサービス側と一致し、カスタムプロセッサSocketClientHandlerも追加されました.
新しいクラスSocketClientHandler
package com.badao.nettySocket;

import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;

public class SocketClientHandler extends SimpleChannelInboundHandler {
    @Override
    protected void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception {
        System.out.println(" "+ctx.channel().remoteAddress()+" "+msg);
        ctx.channel().writeAndFlush(" :( : )");
    }

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


}

これで、サービス側とクライアントの構築が完了しました.
SocketServerのmainメソッドとSocketClientのmainメソッドを順次実行し、エラーが報告されなければ構築に成功します.
しかし、クライアントとサービス側は接続を確立しているが、データと出力をインタラクティブに転送することはできない.
ここでは、クライアントまたはサービス側が要求を出していないためです.
クライアントのプロセッサSocketClientHandlerでは
チャネルがアクティブになって接続が確立された後に実行されるchannelActiveメソッドを書き換えます.
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        ctx.writeAndFlush(" ");
    }

このメソッドでは、文字列メッセージをサービス側に送信します.
サービス側のプロセッサのchannelRead 0メソッドがアクティブになり、実行され、データが出力され、クライアントに送信されます.
クライアントのプロセッサ内のchannelRead 0もアクティブ化され実行され、クライアントは受信したサービス側のデータを出力し、サービス側にデータを送信します.
したがって、クライアントとサービス側は常にデータを送信します.
サービス側とクライアントのmainメソッドを順次実行し、出力を表示します.