Practical Netty (1) 基於Netty實現的一個rdate server例項

鍾超發表於2012-12-28

Practical Netty (1) 基於Netty實現的一個rdate server例項


Netty 是一個高效的 Java 網路框架,簡單的介紹可參見《Java NIO框架Mina、Netty、Grizzly介紹與對比》一文。對於不熟悉 NIO 的 Java 開發者,Netty 的使用需要一小段的時間來熟悉。當然 Netty 已經更好地封裝了 NIO,所以您大可不必先去了解完 NIO,再來了解 Netty。

下面是一個簡單的 rdate server 例項,大部分程式碼參考 Netty Official Examples。什麼是 rdate?如果你是 *nix 使用者的話,就一定知道:

rdate is a command to set the system's date from a remote host.

具體如下:

rdate displays and sets the local date and time from the host name or
address given as the argument.  The time source may be an RFC 868 TCP
protocol server, which is usually implemented as a built-in service of
inetd(8), or an RFC 2030 protocol SNTP/NTP server.  By default, rdate
uses the RFC 868 TCP protocol.

RdateServer.java

package com.sinosuperman.test.netty;

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

import org.jboss.netty.bootstrap.ServerBootstrap;
import org.jboss.netty.channel.ChannelFactory;
import org.jboss.netty.channel.ChannelPipeline;
import org.jboss.netty.channel.ChannelPipelineFactory;
import org.jboss.netty.channel.Channels;
import org.jboss.netty.channel.socket.nio.NioServerSocketChannelFactory;

public class RdateServer {
    private static int port = 28080;

    public static void main(String[] args) {

        // ChannelFactory is a factory which creates and manages Channels and
        // its related resources. 
        ChannelFactory factory =
                new NioServerSocketChannelFactory(
                        Executors.newCachedThreadPool(),  // boss executor
                        Executors.newCachedThreadPool()); // worker executor

        // ServerBootstrap is a helper class that sets up a server. You can set
        // up the server using a Channel directly. However, please note that
        // this is a tedious process and you do not need to do that in most
        // cases.
        ServerBootstrap bootstrap = new ServerBootstrap(factory);

        bootstrap.setPipelineFactory(new ChannelPipelineFactory() {
            @Override
            public ChannelPipeline getPipeline() throws Exception {
                return Channels.pipeline(new RdateServerHandler());
            }
        });

        bootstrap.setOption("child.tcpNoDelay", true);
        bootstrap.setOption("child.keepAlive", true);

        bootstrap.bind(new InetSocketAddress(port));
    }
}

RdateServerHandler.java

下面比較有意思的就是和 NIO 中的 ByteBuffer 類似的 ChannelBuffer,很好用。ChannelFuture 也是 Netty 中經常使用到的。

package com.sinosuperman.test.netty;

import org.jboss.netty.buffer.ChannelBuffer;
import org.jboss.netty.buffer.ChannelBuffers;
import org.jboss.netty.channel.Channel;
import org.jboss.netty.channel.ChannelFuture;
import org.jboss.netty.channel.ChannelFutureListener;
import org.jboss.netty.channel.ChannelHandlerContext;
import org.jboss.netty.channel.ChannelStateEvent;
import org.jboss.netty.channel.ExceptionEvent;
import org.jboss.netty.channel.SimpleChannelHandler;

public class RdateServerHandler extends SimpleChannelHandler {
    @Override
    public void channelConnected(ChannelHandlerContext ctx, ChannelStateEvent e)
            throws Exception {
        Channel ch = e.getChannel();

        // To send a new message, we need to allocate a new buffer which will
        // contain the message. We are going to write a 32-bit integer, and
        // therefore we need a ChannelBuffer whose capacity is 4 bytes. The
        // ChannelBuffers helper class is used to allocate a new buffer. Besides
        // the buffer method, ChannelBuffers provides a lot of useful methods
        // related to the ChannelBuffer. For more information, please refer to
        // the API reference.
        ChannelBuffer time = ChannelBuffers.buffer(4);
        time.writeInt((int) (System.currentTimeMillis() / 1000L + 2208988800L));

        ChannelFuture f = ch.write(time);

        f.addListener(ChannelFutureListener.CLOSE);
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e)
            throws Exception {
        e.getCause().printStackTrace();
        Channel ch = e.getChannel();
        ch.close();
    }
}

這裡比較重要的是時間的計算公式:

System.currentTimeMillis() / 1000L + 2208988800L

具體不細說了,算是一個小常識吧。

客戶端測試

客戶端就是 Linux 提供的 rdate 命令,如下方式測試:

$ rdate -o <rdate_server_port> -p <rdate_server_ip>

這裡預設使用的是 28080。

$rdate -o 28080 -p 127.0.0.1
Fri Dec 28 14:21:05 CST 2012

-

轉載請註明來自鍾超(Poechant)的 CSDN 部落格:http://blog.csdn.net/poechant,作者微博:http://weibo.com/lauginhom

-

相關文章