这两天学习netty,记录一下。
netty 介绍不废话。
官方网站:http://netty.io/
使用netty高层次抽象的API,无需编写底层的NIO、或java7 的NIO.2 代码,并且完美兼容。
使用netty高层次抽象的API,完美融合并发编程中的futrue跟callback模式,编写出优雅且易维护,高性能的应用。
使用netty高层次抽象的API,可以实践传说中的SEDA,编写出号称支持高并发的应用。
使用netty高层次抽象的API,您再也不用为编写多线程应用难度而烦恼。
初初看,给我的感觉这样的,好好学习,天天向上。
传统Server端处理不同连接客户端的办法:
使用NIO之后:
实践Echo程序:
创建Maven工程。pom.xml需要的依赖。
<dependencies> <dependency> <groupId>io.netty</groupId> <artifactId>netty-all</artifactId> <version>4.0.0.Final</version> </dependency> </dependencies>
EchoServer.java
import io.netty.bootstrap.ServerBootstrap; import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelInitializer; import io.netty.channel.EventLoopGroup; import io.netty.channel.nio.NioEventLoopGroup; import io.netty.channel.socket.SocketChannel; import io.netty.channel.socket.nio.NioServerSocketChannel; import java.net.InetSocketAddress; import java.util.logging.Level; import java.util.logging.Logger; /** * * @author XzF */ public class EchoServer { private final int port; public EchoServer( int port ) { this.port = port; } public void start() throws InterruptedException { EventLoopGroup group = new NioEventLoopGroup(); try { ServerBootstrap bootstrap = new ServerBootstrap(); bootstrap.group( group )// .channel( NioServerSocketChannel.class )// .localAddress( new InetSocketAddress( port ) )// .childHandler( new ChannelInitializer<SocketChannel>() { @Override protected void initChannel( SocketChannel ch ) throws Exception { ch.pipeline().addLast( new EchoServerHandler() ); } } // ); ChannelFuture f = bootstrap.bind().sync(); System.out.println( EchoServer.class.getName() + " started and listen on " + f.channel().localAddress() ); f.channel().closeFuture().sync(); } finally { group.shutdownGracefully().sync(); } } public static void main( String[] args ) { int port = 9999; if ( args.length == 1 ) { port = Integer.parseInt( args[0] ); } System.out.println( "Usage: . + EchoServer.class.getSimpleName() +. <port> : " + port ); try { new EchoServer( port ).start(); } catch ( InterruptedException ex ) { Logger.getLogger( EchoServer.class.getName() ).log( Level.SEVERE, null, ex ); } } }
# 创建ServerBootstarp,用于Server端启动。
# 创建EventLoopGroup ,绑定于ServerBootstarp。
# 绑定使用NioServerSocketChannel
# 绑定端口
# 设置起动的ChannelInitialzer,在其管道对象中加入我们自定义的服务端处理的EchoHandler
# ServerBootstartp进行调用原来绑定,并阻塞等其执行完成,返回其ChannelFuture对象
# ChannelFuture.channel().closeFuture().sync() ,阻塞等待Server关闭。
# 释放EventLoopGroup所用的资源。
EchoServerHandler.java
@Sharable public class EchoServerHandler extends ChannelInboundHandlerAdapter { @Override public void channelRead( ChannelHandlerContext ctx, Object msg ) throws Exception { // System.out.println( "Server received :" + msg ); // ctx.write( msg ); ByteBuf in = (ByteBuf) msg; System.out.println( "Server received: " + ByteBufUtil .hexDump( in ) ); ctx.write( in ); } @Override public void channelReadComplete( ChannelHandlerContext ctx ) throws Exception { ctx.writeAndFlush( Unpooled.EMPTY_BUFFER ).addListener( ChannelFutureListener.CLOSE ); } @Override public void exceptionCaught( ChannelHandlerContext ctx, Throwable cause ) throws Exception { cause.printStackTrace(); ctx.close(); } }
# channelRead ,当获取到客户端信息时执行。通过ctx写回给客户端。
# 继承来自ChannelInboundHandlerAdapter,在执行完chanelRead方法后,释放相关NIO资源。
客户端
EchoClient.java
import io.netty.bootstrap.Bootstrap; import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelInitializer; import io.netty.channel.EventLoopGroup; import io.netty.channel.nio.NioEventLoopGroup; import io.netty.channel.socket.SocketChannel; import io.netty.channel.socket.nio.NioSocketChannel; import java.net.InetSocketAddress; /** * * @author XzF */ public class EchoClient { private final String host; private final int port; public EchoClient( String host, int port ) { this.host = host; this.port = port; } public void start() throws InterruptedException { EventLoopGroup group = new NioEventLoopGroup(); try { Bootstrap bootstrap = new Bootstrap(); bootstrap.group( group )// .channel( NioSocketChannel.class )// .remoteAddress( new InetSocketAddress( host, port ) )// .handler( new ChannelInitializer<SocketChannel>() { @Override protected void initChannel( SocketChannel ch ) throws Exception { ch.pipeline().addLast( new EchoClientHandler() ); } } ); ChannelFuture f = bootstrap.connect().sync(); f.channel().closeFuture().sync(); } finally { group.shutdownGracefully().sync(); } } public static void main( String[] args ) { String host = "127.0.0.1"; int port = 9999; if ( args.length > 0 ) { host = args[0]; port = Integer.parseInt( args[1] ); } System.out.println( "Usage: " + EchoClient.class.getSimpleName() + " <host> " + host + " <port> :" + port ); try { new EchoClient( host, port ).start(); } catch ( InterruptedException ex ) { ex.printStackTrace(); } } }#跟服务端大同小异,编写自定义EchoClientHandler
EchoClientHandler.java
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBufUtil; import io.netty.buffer.Unpooled; import io.netty.channel.ChannelHandler.Sharable; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.SimpleChannelInboundHandler; import io.netty.util.CharsetUtil; /** * * @author XzF */ @Sharable public class EchoClientHandler extends SimpleChannelInboundHandler<ByteBuf> { @Override public void channelActive( ChannelHandlerContext ctx ) throws Exception { String msg = "Netty rocks!"; System.out.println( "Clinent sended : " + msg ); ctx.writeAndFlush( Unpooled.copiedBuffer( msg, CharsetUtil.UTF_8 ) ); } @Override protected void channelRead0( ChannelHandlerContext ctx, ByteBuf in ) throws Exception { // System.out.println( "Client received : " // + ByteBufUtil.hexDump( in.readBytes( in.readableBytes() ) ) ); System.out.println("Client received: " + ByteBufUtil .hexDump(in)); } @Override public void exceptionCaught( ChannelHandlerContext ctx, Throwable cause ) throws Exception { cause.printStackTrace(); ctx.close(); } }# channelActive ,在连接建立时调用,发送信息~
# channelRead0 ,当读取到服务端数据时调用。
# 继承SimpleChannelInboundHandler , 调用channelRead0 方法时不会及时清空资源,些类维护了NIO资源的释放,因不能保证一次调用channelRead0 方法已经将读取到的bytes全部带过来,但能维护顺序。
maven打包,编译后执行。
服务端:
客户端: