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 TimeServerHandler extends SimpleChannelHandler { @Override /* channelConnected方法在连接建立的时候被调用。我们在这 *里发送32位整数来表示当前时间(单位:秒)。 */ public void channelConnected(ChannelHandlerContext ctx, ChannelStateEvent e) { Channel ch = e.getChannel(); /*为了发送新消息,需要分配一个用来包含消息的缓存。 *我们需要发送的是32位整数,所以我们需要一个4字节容 *量的ChannelBuffer。这个ChannelBuffers工具类用来分 *配新的缓存。除了这个buffer 方法, ChannelBuffers *提供了很多和ChannelBuffer相关的有用的方法,请参考 *API手册。 此外,静态导入ChannelBuffers是一种好的做法: *import static org.jboss.netty.buffer.ChannelBuffers.*; *... *ChannelBuffer dynamicBuf = dynamicBuffer(256); *ChannelBuffer ordinaryBuf = buffer(1024); */ ChannelBuffer time = ChannelBuffers.buffer(4); time.writeInt(System.currentTimeMillis() / 1000); /*一般而言,我们编写结构化的消息。 *但等一下,flip呢? *我们过去在NIO中发送一个消息之前,不是调用ByteBuffer.flip()吗? *因为ChannelBuffer有两个指针,所以没有这个方法。 一个指针用于读 *操作,一个用于写操作。这个写操作的索引在你向ChannelBuffer 中写 *入内容时增加,同时读操作的索引不改变。这个读写的索引分别表示消息 *开始和结束的位置。 相反,不调用flip方法的话,NIO缓存不提供一个清 *晰的方式来搞清一个消息内容的起始位置。如果你忘记调用flip的话,你 *会遇到麻烦:错误的数据发出,或者什么也不发出。因为不同的操作类型 *有不同的指针,所以这种情况对Netty而言是不会出现的。你会发现你在这 *个不需要flip的环境中,非常的适应、非常舒服。 另一个需要明确的是这 *个write 方法返回一个代表尚未发生的后续I/O操作的 ChannelFuture。 这 *意味着,因为Netty中的所有操作都是异步的,调用的任何操作都可能尚未真 *的执行。比如下面的代码甚至在一个消息尚未发出前关闭连接: *Channel ch = ...; *ch.write(message); *ch.close(); *因此,你需要在write方法返回的ChannelFuture提醒你写操作完成之后,调用 *close 方法。请注意, close方法同样也可能不是立即关闭,它也返回 ChannelFuture。 */ ChannelFuture f = ch.write(time); /*那么当write调用完成的时候,我们怎么获得提醒?简单的给返回的 *ChannelFuture增加一个ChannelFutureListener 即可。这里我们构建 *一个新的匿名的ChannelFutureListener以实现在调用完成后关闭 Channel。 *另外一种做法,你可以使用一个预定义好的Listener来简化代码: *f.addListener(ChannelFutureListener.CLOSE); */ f.addListener(new ChannelFutureListener() { public void operationComplete(ChannelFuture future) { Channel ch = future.getChannel(); ch.close(); } }); } @Override public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e) { e.getCause().printStackTrace(); e.getChannel().close(); } } import java.net.InetSocketAddress; import java.util.concurrent.Executors; import org.jboss.netty.bootstrap.ClientBootstrap; 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.NioClientSocketChannelFactory; public class TimeClient { public static void main(String[] args)throws Exception { String host=args[0]; int port = Integer.parseInt(args[1]); /*创建客户端的Channel使用的是NioClientSocketChannelFactory, *而不是 NioServerSocketChannelFactory。 */ ChannelFactory factory = new NioClientSocketChannelFactory( Executors.newCachedThreadPool(), Executors.newCachedThreadPool()); //ClientBootstrap 在客户端,对应服务器端的 ServerBootstrap。 ClientBootstrap bootstrap = new ClientBootstrap(factory); bootstrap.setPipelineFactory(new ChannelPipelineFactory() { public ChannelPipeline getPipeline() { return Channels.pipeline(new TimeClientHandler()); } }); //请注意到,这次没有"child."前缀。客户端的SocketChannel没有上一级根。 bootstrap.setOption("tcpNoDelay",true); bootstrap.setOption("keepAlive",true); //应该调用connect方法而不是bind 方法。 bootstrap.connect(new InetSocketAddress(host,port)); } } import java.util.Date; import org.jboss.netty.buffer.ChannelBuffer; import org.jboss.netty.channel.ChannelHandlerContext; import org.jboss.netty.channel.ExceptionEvent; import org.jboss.netty.channel.MessageEvent; import org.jboss.netty.channel.SimpleChannelHandler; public class TimeClientHandler extends SimpleChannelHandler { @Override public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) { ChannelBuffer buf = (ChannelBuffer)e.getMessage(); long currentTimeMillis = buf.readInt()*1000L; System.out.println(new Date(currentTimeMillis)); e.getChannel().close(); } @Override public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e) { e.getCause().printStackTrace(); e.getChannel().close(); } }