最近在学习Netty,因为要学习网络通讯,所以在看完socket后,又接着看NIO,然后是Netty,可以说的是:先看完NIO,看完了NIO以后,再来看Netty,这样比较好懂一些,因为这里毕竟用到了Chanel,了解下NIO,对于熟悉Netty就会达到事半功倍的效果。
Netty是业界最流行的NIO框架之一,它的健壮性、功能、性能、可定制性和可扩展性在同类框架中都是首屈一指的,它已经得到成百上千的商用项目验证,例如Hadoop的RPC框架avro使用Netty作为底层通信框架。很多其它业界主流的RPC框架,也使用Netty来构建高性能的异步通信能力。
通过对Netty的分析,我们将它的优点总结如下:
1) API使用简单,开发门槛低;
2) 功能强大,预置了多种编解码功能,支持多种主流协议;
3) 定制能力强,可以通过ChannelHandler对通信框架进行灵活的扩展;
4) 性能高,通过与其它业界主流的NIO框架对比,Netty的综合性能最优;
5) 成熟、稳定,Netty修复了已经发现的所有JDK NIO BUG,业务开发人员不需要再为NIO的BUG而烦恼;
6) 社区活跃,版本迭代周期短,发现的BUG可以被及时修复,同时,更多的新功能会被加入;
7) 经历了大规模的商业应用考验,质量已经得到验证。在互联网、大数据、网络游戏、企业应用、电信软件等众多行业得到成功商用,证明了它可以完全满足不同行业的商业应用。
正是因为这些优点,Netty逐渐成为Java NIO编程的首选框架。
下面是一个小demo:
首先是pom.xml里的配置
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>sockets</groupId> <artifactId>socketDemo</artifactId> <packaging>war</packaging> <version>1.0-SNAPSHOT</version> <name>socketDemo Maven Webapp</name> <url>http://maven.apache.org</url> <dependencies> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>3.8.1</version> <scope>test</scope> </dependency> <!--Netty all--> <dependency> <groupId>io.netty</groupId> <artifactId>netty-all</artifactId> <version>4.1.9.Final</version> </dependency> <!--Commons-codec --> <dependency> <groupId>commons-codec</groupId> <artifactId>commons-codec</artifactId> <version>1.10</version> </dependency> <!--dom4j--> <dependency> <groupId>dom4j</groupId> <artifactId>dom4j</artifactId> <version>1.6.1</version> </dependency> <!--fastjson--> <dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>1.2.33</version> </dependency> <!--log4j--> <dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> <version>1.2.17</version> </dependency> </dependencies> <build> <finalName>socketDemo</finalName> </build> </project>
第二,在resource里写一个配置文件 config.properties
#netty 服务端ip nioServerIp=127.0.0.1 #netty 服务端端口 nioServerPort=19990
第三步,开始写代码了:
先写Service的Handler :serverHandler
package com.NEttydemo.test; import io.netty.buffer.Unpooled; import io.netty.channel.ChannelFutureListener; import io.netty.channel.ChannelHandler; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelInboundHandlerAdapter; /** * Created by T430 on 2017/8/30. */ public class ServerHandler extends ChannelInboundHandlerAdapter { public void channelRead(ChannelHandlerContext ctx, Object msg) { System.out.println("server received data:" + msg); ctx.write(msg);//写入数据 } public void channelReadComplete(ChannelHandlerContext ctx) { ctx.writeAndFlush(Unpooled.EMPTY_BUFFER)//flush调所有写回的数据 .addListener(ChannelFutureListener.CLOSE);//当flash完成后关闭Channel } //重写了exceptionCaught方法,这里就是对当异常出现时的处理。 public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { cause.printStackTrace();//捕获异常信息 ctx.close();//出现异常时关闭channel } }在写server
package com.NEttydemo.test; import io.netty.bootstrap.ServerBootstrap; import io.netty.channel.*; import io.netty.channel.nio.NioEventLoopGroup; import io.netty.channel.socket.nio.NioServerSocketChannel; import io.netty.handler.codec.LengthFieldBasedFrameDecoder; import io.netty.handler.codec.LengthFieldPrepender; import io.netty.handler.codec.string.StringDecoder; import io.netty.handler.codec.string.StringEncoder; import io.netty.util.CharsetUtil; /** * * netty 服务器 * Created by T430 on 2017/8/30. */ public class NettyServer { //IP地址 private static String IP="127.0.0.1"; //默认端口 private static int port=5566; //组的数量 private static final int BIZGROUPSIZE= Runtime.getRuntime().availableProcessors()*2; //头部数量 private static final int BIZTHREADSIZE = 100; //创建领导组 private static final EventLoopGroup bossGroup = new NioEventLoopGroup(BIZGROUPSIZE); //创建工作组 private static final EventLoopGroup workerGroup = new NioEventLoopGroup(BIZTHREADSIZE); public static void init() throws Exception{ ServerBootstrap bootstrap=new ServerBootstrap();//声明 组 bootstrap.group(bossGroup,workerGroup);//添加组 bootstrap.channel(NioServerSocketChannel.class);//声明通道 bootstrap.childHandler(new ChannelInitializer<Channel>() { protected void initChannel(Channel channel) throws Exception { // TODO Auto-generated method stub //pipeline管理channel中的Handler ChannelPipeline pipeline = channel.pipeline(); pipeline.addLast(new LengthFieldBasedFrameDecoder(Integer.MAX_VALUE, 0, 4, 0, 4)); pipeline.addLast(new LengthFieldPrepender(4)); pipeline.addLast(new StringDecoder(CharsetUtil.UTF_8)); pipeline.addLast(new StringEncoder(CharsetUtil.UTF_8)); pipeline.addLast(new ServerHandler()); } }); IP =Configuration.getProperty("nioServerIp"); port=Integer.parseInt(Configuration.getProperty("nioServerPort")); System.out.println("【TCP服务器IP】"+IP+"【TCP服务器PORT】"+port); //配置完成,开始绑定server,通过调用sync同步方法阻塞直到绑定成功 ChannelFuture f = bootstrap.bind(IP, port).sync(); //应用程序会一直等待,直到channel关闭 f.channel().closeFuture().sync(); System.out.println("TCP服务器已启动"); } protected static void shutdown() { workerGroup.shutdownGracefully(); bossGroup.shutdownGracefully(); } public static void main(String[] args) throws Exception { System.out.println("初始化配置文件..."); Configuration.init(); System.out.println("开始启动TCP服务器..."); NettyServer.init(); // HelloServer.shutdown(); } }接着写client的Handler ClientHandler
package com.NEttydemo.test; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelInboundHandlerAdapter; /** * Created by T430 on 2017/8/30. */ public class ClientHandler extends ChannelInboundHandlerAdapter { public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { System.out.println("【client接收到服务器返回的消息】:" + msg); } public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { System.out.println("【client exception is general】"); } }
再写Client
package com.NEttydemo.test; import io.netty.bootstrap.Bootstrap; import io.netty.channel.*; import io.netty.channel.nio.NioEventLoopGroup; import io.netty.channel.socket.SocketChannel; import io.netty.channel.socket.nio.NioSocketChannel; import io.netty.handler.codec.LengthFieldBasedFrameDecoder; import io.netty.handler.codec.LengthFieldPrepender; import io.netty.handler.codec.string.StringDecoder; import io.netty.handler.codec.string.StringEncoder; import io.netty.util.CharsetUtil; /** * * 客户端 * Created by T430 on 2017/8/30. */ public class NettyClient implements Runnable{ public void run() { EventLoopGroup group = new NioEventLoopGroup(); try { Bootstrap b = new Bootstrap(); b.group(group); b.channel(NioSocketChannel.class).option(ChannelOption.TCP_NODELAY, true); b.handler(new ChannelInitializer<SocketChannel>() { @Override protected void initChannel(SocketChannel ch) throws Exception { ChannelPipeline pipeline = ch.pipeline(); pipeline.addLast("frameDecoder", new LengthFieldBasedFrameDecoder(Integer.MAX_VALUE, 0, 4, 0, 4)); pipeline.addLast("frameEncoder", new LengthFieldPrepender(4)); pipeline.addLast("decoder", new StringDecoder(CharsetUtil.UTF_8)); pipeline.addLast("encoder", new StringEncoder(CharsetUtil.UTF_8)); pipeline.addLast("handler", new ClientHandler()); } }); ChannelFuture f = b.connect("127.0.0.1", 19990).sync(); f.channel().writeAndFlush("Netty Hello Service!"+Thread.currentThread().getName()+":--->:"+Thread.currentThread().getId()); f.channel().closeFuture().sync(); } catch (Exception e) { } finally { group.shutdownGracefully(); } } public static void main(String[] args) throws Exception { for (int i = 0; i < 10; i++) { new Thread(new NettyClient(),"【this thread】 "+i).start(); } } }
最后得到的结果:
启动服务端的截图:
启动服务端,客户端后的截图分别是:
到此,demo结束!