読者です 読者をやめる 読者になる 読者になる

snuffkinの遊び場

IT関係、スポーツ、数学等に関することを、気が向いたときに書いてます。

Netty3からNetty4へのなんちゃって移植

2012/07/07追記
Netty4のAPIはその後更新され、Netty3でのAPIは残していないようです。
そのため、ここに書いた内容は現在では使えませんのでご注意ください。

2013/10/17追記
Netty3からNetty4へのちゃんとしたマイグレーション方法は「How to Migrate Netty 3 to 4 (Netty 番外編) - Taste of Tech Topics」に記載しましたので、こちらをご覧ください。

以前、Nettyのことを書いてから全然続きを書いていませんでした。その間にNettyの開発は進み、Netty3からNetty4に進化しようとしています。Nettyのgithubに行くと、開発中のNetty4のコードがあるので遊んでみました。Netty4では"next-api"と呼ばれている新しいAPIを使っているのですが、ひとまず、以前の書いたソースをNetty3からNetty4に移植してみました。すると、next-apiを使わずに、パッケージ名を変えただけでほとんど動きました。どうやら、以前のAPIも残しているようですね。

echoサンプルくらいなら、以下の2種類の修正だけで、Netty4に対応することができました(ただし、Netty4は開発中なので、APIは今後変わるかもしれません。また、使うAPIによってはNetty4でそのまま使えないかもしれません)。

  1. importするパッケージを"org.jboss.netty"から"io.netty"に変更する
  2. NioClientSocketChannelFactoryのコンストラクタ引数の変更に対応する

サンプルソースは以下の通りです。

  • EchoServer
package jp.gr.java_conf.snuffkin.sandbox.netty_next.echo;

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

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFactory;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.ChannelPipelineFactory;
import io.netty.channel.Channels;
import io.netty.channel.socket.nio.NioServerSocketChannelFactory;
import io.netty.handler.codec.frame.LengthFieldBasedFrameDecoder;
import io.netty.handler.codec.frame.LengthFieldPrepender;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;

/**
 * サーバ側メインクラス
 */
public class EchoServer {

    public static void main(String[] args) {
        ChannelFactory factory = 
            new NioServerSocketChannelFactory( // server
                    Executors.newCachedThreadPool(),
                    Executors.newCachedThreadPool()
                    );
        
        ServerBootstrap bootstrap = new ServerBootstrap(factory);
        bootstrap.setPipelineFactory(new ChannelPipelineFactory() {
            public ChannelPipeline getPipeline() {
                ChannelPipeline pipeline = Channels.pipeline();
                // Downstream(送信)
                pipeline.addLast("frameEncoder", new LengthFieldPrepender(4));
                pipeline.addLast("stringEncoder", new StringEncoder());
                // Upstream(受信)
                pipeline.addLast("frameDecoder", new LengthFieldBasedFrameDecoder(8192, 0, 4, 0, 4));
                pipeline.addLast("stringDecoder", new StringDecoder());
                // Application Logic Handler
                pipeline.addLast("handler", new EchoServerHandler()); // server
                
                return pipeline;
            }
        });
        
        bootstrap.bind(new InetSocketAddress(9999)); // 9999番ポートでlisten
    }
}
  • EchoServerHandler
package jp.gr.java_conf.snuffkin.sandbox.netty_next.echo;

import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.MessageEvent;
import io.netty.channel.SimpleChannelHandler;

/**
 * サーバ側アプリケーションロジック
 */
public class EchoServerHandler extends SimpleChannelHandler {
    /**
     * クライアントから電文を受信した際に呼び出されるメソッド
     */
    @Override
    public void messageReceived(ChannelHandlerContext ctx, MessageEvent event) {
        String msg = (String) event.getMessage(); // 受信電文を取りだす
        ctx.getChannel().write(msg); // クライアントに送信
    }
}
package jp.gr.java_conf.snuffkin.sandbox.netty_next.echo;

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

import io.netty.bootstrap.ClientBootstrap;
import io.netty.channel.ChannelFactory;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.ChannelPipelineFactory;
import io.netty.channel.Channels;
import io.netty.channel.socket.nio.NioClientSocketChannelFactory;
import io.netty.handler.codec.frame.LengthFieldBasedFrameDecoder;
import io.netty.handler.codec.frame.LengthFieldPrepender;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;

/**
 * クライアント側メインクラス
 */
public class EchoClient {

    public static void main(String[] args) {
        ChannelFactory factory =
            new NioClientSocketChannelFactory( // client
                    Executors.newCachedThreadPool() // ★引数変えています
                    );
        
        ClientBootstrap bootstrap = new ClientBootstrap(factory);
        bootstrap.setPipelineFactory(new ChannelPipelineFactory() {
            public ChannelPipeline getPipeline() {
                ChannelPipeline pipeline = Channels.pipeline();
                // Downstream(送信)
                pipeline.addLast("frameEncoder", new LengthFieldPrepender(4));
                pipeline.addLast("stringEncoder", new StringEncoder());
                // Upstream(受信)
                pipeline.addLast("frameDecoder", new LengthFieldBasedFrameDecoder(8192, 0, 4, 0, 4));
                pipeline.addLast("stringDecoder", new StringDecoder());
                // Application Logic Handler
                pipeline.addLast("handler", new EchoClientHandler()); // client

                return pipeline;
            }
        });
        
        ChannelFuture future = bootstrap.connect(new InetSocketAddress("localhost", 9999)); // 9999番ポートにconnect
        future.getChannel().getCloseFuture().awaitUninterruptibly();
        bootstrap.releaseExternalResources();
    }
}
  • EchoClientHandler
package jp.gr.java_conf.snuffkin.sandbox.netty_next.echo;

import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelStateEvent;
import io.netty.channel.MessageEvent;
import io.netty.channel.SimpleChannelHandler;

/**
 * クライアント側アプリケーションロジック
 */
public class EchoClientHandler extends SimpleChannelHandler {
    /**
     * サーバに接続した際に呼び出されるメソッド
     */
    @Override
    public void channelConnected(ChannelHandlerContext ctx, ChannelStateEvent event) {
        ctx.getChannel().write("Hello, World!");
    }
    
    /**
     * サーバから電文を受信した際に呼び出されるメソッド
     */
    @Override
    public void messageReceived(ChannelHandlerContext ctx, MessageEvent event) {
        String msg = (String) event.getMessage();
        System.out.println(msg);
    }
}

これだけで、動きました。next-apiを使っていなので、これを移植と言ってはズルイ気がしますが。。。次はもうちょっと真面目に書きます^^