netty是个非常好用的nio框架,提供了安全、快捷的tcp/ip、udp/ip通讯方式。网上也有好多文章,netty3.6的官方文档是这个http://netty.io/3.6/guide/,写得很好。
下面就读着这篇文档,做下笔记:
先看两段代码:
一:DiscardServer,这是个服务端的代码,是服务端的启动程序(创建channel,指定通道处理程序)
public class DiscardServer { private final int port; public DiscardServer(int port) { this.port = port; } public void run() { // Configure the server. ServerBootstrap bootstrap = new ServerBootstrap( new NioServerSocketChannelFactory( Executors.newCachedThreadPool(), Executors.newCachedThreadPool())); // Set up the pipeline factory. bootstrap.setPipelineFactory(new ChannelPipelineFactory() { public ChannelPipeline getPipeline() throws Exception { return Channels.pipeline(new DiscardServerHandler()); } }); // Bind and start to accept incoming connections. bootstrap.bind(new InetSocketAddress(port)); } public static void main(String[] args) throws Exception { int port; if (args.length > 0) { port = Integer.parseInt(args[0]); } else { port = 8080; } new DiscardServer(port).run(); } }
既然是通信,那当然有通道,Channel就是通道,通道是谁创建的呢,当然是ChannelFactory
代码中的NioServerSocketChannelFactory就是服务端的NIO的Socket的通道工厂,名字起得很直白吧。
创建这个工厂需要两个Executor(Executors.newCachedThreadPool()返回的ExecutorService,ExecutorService继承自Executor),这两个Executor一个用来执行boss线程,一个用来执行I/O工人线程。
工厂设置好之后, 就该创建Channel了,创建Channel是个复杂的过程,netty提供了一个帮助类ServerBootstrap,使用它可以便捷的执行这个创建过程。
有了通道后,当然会有相关的通讯处理,这些处理就是ChannelHandler(DiscardServerHandler就是自定义的handler,代码见二:DiscardServerHandler),ChannelHandler使用ChannelPipeline来设置(添加、移除、获取),ChannelPipelineFactory当然是用来创建ChannelPipeline的了。
最后,绑定个端口号,开始启动服务吧
二:DiscardServerHandler,通道处理程序
public class DiscardServerHandler extends SimpleChannelUpstreamHandler { private static final Logger logger = Logger.getLogger( DiscardServerHandler.class.getName()); private long transferredBytes; public long getTransferredBytes() { return transferredBytes; } @Override public void handleUpstream(ChannelHandlerContext ctx, ChannelEvent e) throws Exception { if (e instanceof ChannelStateEvent) { logger.info(e.toString()); } // Let SimpleChannelHandler call actual event handler methods below. super.handleUpstream(ctx, e); } @Override public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) { // Discard received data silently by doing nothing. transferredBytes += ((ChannelBuffer) e.getMessage()).readableBytes(); } @Override public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e) { // Close the connection when an exception is raised. logger.log( Level.WARNING, "Unexpected exception from downstream.", e.getCause()); e.getChannel().close(); } }
这个通道处理程序继承自SimpleChannelUpstreamHandler,最终实现了ChannelHandler,netty是基于事件驱动的,所以DiscardServerHandler实现的方法中都会有相关的事件
messageReceived:当消息到达时执行
exceptionCaught:当发生异常时执行
messageReceived有两个参数,一个是ChannelHandlerContext,一个是MessageEvent,messageEvent就是消息事件,从消息事件中可以取出消息(getMessage())
服务端的代码搞定了,下面上客户端的代码,客户端的代码跟服务端的大体一致:
一:DiscardClient
public class DiscardClient { private final String host; private final int port; private final int firstMessageSize; public DiscardClient(String host, int port, int firstMessageSize) { this.host = host; this.port = port; this.firstMessageSize = firstMessageSize; } public void run() { // Configure the client. ClientBootstrap bootstrap = new ClientBootstrap( new NioClientSocketChannelFactory( Executors.newCachedThreadPool(), Executors.newCachedThreadPool())); // Set up the pipeline factory. bootstrap.setPipelineFactory(new ChannelPipelineFactory() { public ChannelPipeline getPipeline() throws Exception { return Channels.pipeline( new DiscardClientHandler(firstMessageSize)); } }); // Start the connection attempt. ChannelFuture future = bootstrap.connect(new InetSocketAddress(host, port)); // Wait until the connection is closed or the connection attempt fails. future.getChannel().getCloseFuture().awaitUninterruptibly(); // Shut down thread pools to exit. bootstrap.releaseExternalResources(); } public static void main(String[] args) throws Exception { // Print usage if no argument is specified. if (args.length < 2 || args.length > 3) { System.err.println( "Usage: " + DiscardClient.class.getSimpleName() + " <host> <port> [<first message size>]"); return; } // Parse options. final String host = args[0]; final int port = Integer.parseInt(args[1]); final int firstMessageSize; if (args.length == 3) { firstMessageSize = Integer.parseInt(args[2]); } else { firstMessageSize = 256; } new DiscardClient(host, port, firstMessageSize).run(); } }
二:
public class DiscardClientHandler extends SimpleChannelUpstreamHandler { private static final Logger logger = Logger.getLogger( DiscardClientHandler.class.getName()); private long transferredBytes; private final byte[] content; public DiscardClientHandler(int messageSize) { if (messageSize <= 0) { throw new IllegalArgumentException( "messageSize: " + messageSize); } content = new byte[messageSize]; } public long getTransferredBytes() { return transferredBytes; } @Override public void handleUpstream(ChannelHandlerContext ctx, ChannelEvent e) throws Exception { if (e instanceof ChannelStateEvent) { if (((ChannelStateEvent) e).getState() != ChannelState.INTEREST_OPS) { logger.info(e.toString()); } } // Let SimpleChannelHandler call actual event handler methods below. super.handleUpstream(ctx, e); } @Override public void channelConnected(ChannelHandlerContext ctx, ChannelStateEvent e) { // Send the initial messages. generateTraffic(e); } @Override public void channelInterestChanged(ChannelHandlerContext ctx, ChannelStateEvent e) { // Keep sending messages whenever the current socket buffer has room. generateTraffic(e); } @Override public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) { // Server is supposed to send nothing. Therefore, do nothing. } @Override public void writeComplete(ChannelHandlerContext ctx, WriteCompletionEvent e) { transferredBytes += e.getWrittenAmount(); } @Override public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e) { // Close the connection when an exception is raised. logger.log( Level.WARNING, "Unexpected exception from downstream.", e.getCause()); e.getChannel().close(); } private void generateTraffic(ChannelStateEvent e) { // Keep generating traffic until the channel is unwritable. // A channel becomes unwritable when its internal buffer is full. // If you keep writing messages ignoring this property, // you will end up with an OutOfMemoryError. Channel channel = e.getChannel(); while (channel.isWritable()) { ChannelBuffer m = nextMessage(); if (m == null) { break; } channel.write(m); } } private ChannelBuffer nextMessage() { return ChannelBuffers.wrappedBuffer(content); } }