Nettyの実装例

Nettyの基本的な使用方法を示すために、1つのサーバーと5つのクライアントスレッド間の通信を実装します。クライアントはサーバーに接続後メッセージを送信し、サーバーは受信したメッセージに対して応答を返却します。クライアントは応答を受信後、ランダムな時間待機して再びメッセージ送信を繰り返します。

プロジェクト構成:

サーバー実装:

package Server;

import java.net.InetSocketAddress;
import java.util.concurrent.Executors;

import org.jboss.netty.bootstrap.ServerBootstrap;  
import org.jboss.netty.channel.*;  
import org.jboss.netty.channel.socket.nio.NioServerSocketChannelFactory;  
import org.jboss.netty.handler.execution.ExecutionHandler;
import org.jboss.netty.handler.execution.OrderedMemoryAwareThreadPoolExecutor;

import Util.Constant;


public class NettyServer {
    
    public static String ホストアドレス = "127.0.0.1";
    
    // スレッドプールの生成
    static ExecutionHandler 実行ハンドラ = new ExecutionHandler(new OrderedMemoryAwareThreadPoolExecutor(16, 1048576, 1048576));
    
    public static void main(String[] args) {
        // チャンネルファクトリの生成
        final ChannelFactory チャンネルファクトリ = new NioServerSocketChannelFactory(  
                // 接続処理用スレッドプール
                Executors.newCachedThreadPool(),  
                // NIOにより1つのワーカースレッドで複数チャネルを管理可能
                Executors.newCachedThreadPool()); 
        // サーバー設定
        ServerBootstrap ブートストラップ = new ServerBootstrap(チャンネルファクトリ);

        ServerPipelineFactory パイプラインファクトリ = new ServerPipelineFactory(実行ハンドラ);
        ブートストラップ.setPipelineFactory(パイプラインファクトリ); 
        
        // Nagleアルゴリズムの無効化
        ブートストラップ.setOption("child.tcpNoDelay", true);  
        // TCPキープアライブの有効化
        ブートストラップ.setOption("child.keepAlive", true); 
        
        // 複数ポートの監視
        ブートストラップ.bind(new InetSocketAddress(Constant.p1));
        System.out.println("ポート " + Constant.p1 + " を監視中...");
        ブートストラップ.bind(new InetSocketAddress(Constant.p2));
        System.out.println("ポート " + Constant.p2 + " を監視中...");
        ブートストラップ.bind(new InetSocketAddress(Constant.p3));
        System.out.println("ポート " + Constant.p3 + " を監視中...");
        ブートストラップ.bind(new InetSocketAddress(Constant.p4));
        System.out.println("ポート " + Constant.p4 + " を監視中...");
        ブートストラップ.bind(new InetSocketAddress(Constant.p5));
        System.out.println("ポート " + Constant.p5 + " を監視中...");
    }

}

パイプラインファクトリ:

package Server;

import org.jboss.netty.channel.ChannelPipeline;
import org.jboss.netty.channel.ChannelPipelineFactory;
import org.jboss.netty.channel.Channels;
import org.jboss.netty.handler.codec.string.StringDecoder;
import org.jboss.netty.handler.codec.string.StringEncoder;
import org.jboss.netty.handler.execution.ExecutionHandler;

public class ServerPipelineFactory implements ChannelPipelineFactory {
    
    private final ExecutionHandler 実行ハンドラ; 
    
    public ServerPipelineFactory(ExecutionHandler 実行ハンドラ){
        this.実行ハンドラ = 実行ハンドラ;
    }

    @Override
    public ChannelPipeline getPipeline() throws Exception {
        return Channels.pipeline( 
                new StringEncoder(),    
                new StringDecoder(), 
                // 複数パイプライン間で共有する実行ハンドラを配置
                実行ハンドラ,
                // ビジネスロジックハンドラ
                new MyServerHandler());
    } 

}

サーバーハンドラ:

package Server;

import org.jboss.netty.channel.Channel;
import org.jboss.netty.channel.ChannelHandlerContext;
import org.jboss.netty.channel.ChannelStateEvent;
import org.jboss.netty.channel.ExceptionEvent;
import org.jboss.netty.channel.MessageEvent;
import org.jboss.netty.channel.SimpleChannelHandler;
import Util.Tool;

public class MyServerHandler extends SimpleChannelHandler{
    
    @SuppressWarnings("static-access")
    @Override
    public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) throws Exception {
        System.out.println("サーバー受信:" + e.getMessage());
        // ランダム秒数の待機後送信
        Thread th = Thread.currentThread();
        int 間隔 = Tool.getInterval(100);
        th.sleep(間隔*1000);
        e.getChannel().write("サーバーからの応答: こんにちは!");
        super.messageReceived(ctx, e);
    }
    
    @Override  
    public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e) throws Exception {  
        e.getCause().printStackTrace();  
        Channel ch = e.getChannel();  
        ch.close(); 
        super.exceptionCaught(ctx, e);
    } 
    
    @Override  
    public void channelConnected(ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception {
        System.out.println("クライアント接続!");
        super.channelConnected(ctx, e); 
    }

}

クライアント実装:

package Client;

import java.net.InetSocketAddress;
import java.util.concurrent.Executors;

import org.jboss.netty.bootstrap.ClientBootstrap;  
import org.jboss.netty.channel.*;  
import org.jboss.netty.channel.socket.nio.NioClientSocketChannelFactory;  
import org.jboss.netty.handler.execution.ExecutionHandler;
import org.jboss.netty.handler.execution.OrderedMemoryAwareThreadPoolExecutor;

import Util.Constant;

public class NettyClient extends Thread{
    
    public static String ホストアドレス = "127.0.0.1";
    ClientBootstrap ブートストラップ;
    int 接続先ポート;
    
    // スレッドプールの生成
    static ExecutionHandler 実行ハンドラ = new ExecutionHandler(new OrderedMemoryAwareThreadPoolExecutor(16, 1048576, 1048576));
    
    public NettyClient(int 接続先ポート) {
        this.接続先ポート = 接続先ポート;
        // チャンネルファクトリの生成
        final ChannelFactory チャンネルファクトリ = new NioClientSocketChannelFactory(  
                // 接続処理用スレッドプール
                Executors.newCachedThreadPool(),  
                // ワーカースレッドプール
                Executors.newCachedThreadPool());  
        // クライアント設定
        ブートストラップ = new ClientBootstrap(チャンネルファクトリ);
        
        ClientPipelineFactory パイプラインファクトリ = new ClientPipelineFactory(実行ハンドラ);
        ブートストラップ.setPipelineFactory(パイプラインファクトリ);
        ブートストラップ.setOption("tcpNoDelay" ,true);  
        ブートストラップ.setOption("keepAlive", true);  
        ブートストラップ.connect(new InetSocketAddress(接続先ポート));
 
    }
    
    public void run(){
        ChannelFuture フューチャー = ブートストラップ.connect(new InetSocketAddress(ホストアドレス, 接続先ポート));
        // 接続開始
        System.out.println(接続先ポート + " への接続を開始...");
        // 終了まで待機
        フューチャー.getChannel().getCloseFuture().awaitUninterruptibly(); 
        // リソース解放
        ブートストラップ.releaseExternalResources();
    }
    
    public static void main(String[] args) {
        NettyClient nc1 = new NettyClient(Constant.p1);
        NettyClient nc2 = new NettyClient(Constant.p2);
        NettyClient nc3 = new NettyClient(Constant.p3);
        NettyClient nc4 = new NettyClient(Constant.p4);
        NettyClient nc5 = new NettyClient(Constant.p5);
        
        nc1.start();
        nc2.start();
        nc3.start();
        nc4.start();
        nc5.start();
    }

}

クライアントパイプラインファクトリ:

package Client;

import org.jboss.netty.channel.ChannelPipeline;
import org.jboss.netty.channel.ChannelPipelineFactory;
import org.jboss.netty.channel.Channels;
import org.jboss.netty.handler.codec.string.StringDecoder;
import org.jboss.netty.handler.codec.string.StringEncoder;
import org.jboss.netty.handler.execution.ExecutionHandler;

public class ClientPipelineFactory implements ChannelPipelineFactory {
    
    private final ExecutionHandler 実行ハンドラ; 
    
    public ClientPipelineFactory(ExecutionHandler 実行ハンドラ){
        this.実行ハンドラ = 実行ハンドラ;
    }

    @Override
    public ChannelPipeline getPipeline() throws Exception {
        return Channels.pipeline( 
                new StringEncoder(),    
                new StringDecoder(), 
                // 複数パイプライン間で共有する実行ハンドラを配置
                実行ハンドラ,
                // ビジネスロジックハンドラ
                new MyClientHandler());
    } 
}

クライアントハンドラ:

package Client;

import org.jboss.netty.channel.Channel;
import org.jboss.netty.channel.ChannelHandlerContext;
import org.jboss.netty.channel.ChannelStateEvent;
import org.jboss.netty.channel.ExceptionEvent;
import org.jboss.netty.channel.MessageEvent;
import org.jboss.netty.channel.SimpleChannelHandler;
import org.jboss.netty.channel.SimpleChannelUpstreamHandler;

import Util.Tool;

public class MyClientHandler extends SimpleChannelHandler{
    
      
    // サーバー接続時のメッセージ送信
    @Override
    public void channelConnected(ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception { 
        System.out.println("サーバーに接続しました!");
        e.getChannel().write("クライアントからの挨拶: こんにちは! " + System.currentTimeMillis()); 
        super.channelConnected(ctx, e);
    }  
  
    @SuppressWarnings("static-access")
    @Override
    public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) throws Exception {
        System.out.println("クライアント受信:" + e.getMessage());
        // ランダム秒数の待機後送信
        Thread th = Thread.currentThread();
        int 間隔 = Tool.getInterval(5);
        th.sleep(間隔*1000);
        e.getChannel().write("クライアントからの挨拶: こんにちは! "  + System.currentTimeMillis());
        super.messageReceived(ctx, e);
    }  
      
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e) throws Exception {  
        e.getCause().printStackTrace();  
        Channel ch = e.getChannel();  
        ch.close(); 
        super.exceptionCaught(ctx, e);
    } 

}

定数クラス:

package Util;

public class Constant {
    final static int ベースポート = 10000;
    public static int p1 = ベースポート + 1;
    public static int p2 = ベースポート + 2;
    public static int p3 = ベースポート + 3;
    public static int p4 = ベースポート + 4;
    public static int p5 = ベースポート + 5;
}

ユーティリティ:

package Util;

import java.util.Random;

public class Tool {
    
    static Random ランダムジェネレータ = new Random();
    
    public static int getInterval(int 最大値){
        return ランダムジェネレータ.nextInt(最大値);
    }
}

タグ: Netty NIO ThreadPools TCP設定 メッセージハンドリング

8月3日 05:00 投稿