Netty是一个易于使用的NIO客户端/服务器框架,它利用Java的高级网络功能来隐藏其背后的复杂性(JavaAPI)。它极大地简化和优化了TCP和UDP套接字服务器等网络编程,性能和安全性等诸多方面更胜一筹。支持多种协议,如FTP、SMTP、HTTP和各种二进制和基于文本的遗留协议。官方的总结是:Netty成功地找到了一种在不牺牲可维护性和性能的情况下实现易开发性、性能、稳定性和灵活性的方法。为什么使用NettyNetty作为一个优秀的网络框架,自然具有令人信服的特点:设计:同一个接口用于多种传输类型。简单但更强大的线程模型。真正的无连接数据报套接字支持。链接逻辑重用。性能:Netty的高性能是其被广泛使用的重要原因。我们可能都认为Java不适合写游戏服务器程序,但是Netty的到来无疑减少了质疑的声音。比原生JavaAPI吞吐量更高,延迟更低。更少的资源消耗(共享池和重用)。减少内存复制。健壮性:原生NIO的client/server编程比较繁琐。如果某个地方处理不好,可能会导致一些意想不到的异常,比如内存溢出,死循环等,而Netty为我们简化了。它消除了对原生API的使用,这使得我们编写的程序更不容易出错。社区:Netty发展迅速的一个重要原因是它的社区非常活跃,这也使得越来越多的开发者采用它。Netty的简单使用,左边是服务端代码,右边是客户端代码。上面的代码基本上就是模板代码,每次使用都是一样的套路。我们唯一需要开发的部分是在handler(…)和childHandler(…)方法中指定的处理程序,例如EchoServerHandler和EchoClientHandler。当然,Netty源码也给了我们很多的handler,比如上面的LoggingHandler,Netty源码里就给我们提供了,需要的时候可以直接使用。我们来看看上面代码中涉及到的一些内容:ServerBootstrap类用于创建服务端实例,Bootstrap类用于创建客户端实例。两个EventLoopGroup:bossGroup和workerGroup与Netty的线程模型有关。可以看到服务器端有两组,客户端只有一组。它们是Netty中的线程池。Netty中的Channel并没有直接使用Java原生的ServerSocketChannel和SocketChannel,而是包装了NioServerSocketChannel和NioSocketChannel与之对应。当然也有对其他协议的支持,比如支持UDP协议的NioDatagramChannel。本文只关心TCP。左边的handler(…)方法指定了一个handler(LoggingHandler),用于服务器端收到新请求时的处理。右侧的handler(...)方法指定客户端在处理请求时需要使用的处理程序。如果想在EchoServer中指定多个handler,也可以像右边的EchoClient一样,使用左边的ChannelInitializerchildHandler(…)来指定childHandler。这里的处理程序用于新创建的连接。我们知道服务器端的ServerSocketChannel在接受一个连接后,需要创建一个SocketChannel的实例。childHandler(…)中设置的处理程序用于处理新创建的SocketChannel,而不是ServerSocketChannel实例。pipeline:handler可以指定多个(需要上面ChannelInitializer类的辅助),它们会组成一个pipeline,它们其实类似于拦截器的概念,现在只要记住每个NioSocketChannel或者NioServerSocketChannel实例里面都会有一个pipeline实例。处理程序的执行顺序也参与管道。ChannelFuture:这个涉及到Netty中的异步编程,类似于JDK中的Future接口。Netty核心组件Bytebuf(字节容器)网络通信最终是通过字节流来传输的。ByteBuf是Netty提供的一个字节容器,里面是一个字节数组。我们通过Netty传输数据的时候,就是通过ByteBuf。我们可以认为ByteBuf是Netty对JavaNIO提供的ByteBuffer字节容器的封装和抽象。很多朋友可能会问:为什么不直接使用JavaNIO提供的ByteBuffer呢?因为ByteBuffer类太复杂,使用起来太麻烦。Bootstrap和ServerBootstrap(引导类)Bootstrap是客户端的引导类/辅助类。具体使用方法如下:EventLoopGroupgroup=newNioEventLoopGroup();try{//创建客户端引导/辅助类:BootstrapBootstrapb=newBootstrap();//指定线程模型b.group(group)。......//尝试建立连接ChannelFuturef=b.connect(host,port).sync();f.channel().closeFuture().sync();}finally{//优雅关闭相关线程组资源group.shutdownGracefully();}ServerBootstrap客户端的启动引导类/辅助类,具体使用方法如下://1.bossGroup用于接收连接,workerGroup用于具体处理EventLoopGroupbossGroup=newNioEventLoopGroup(1);EventLoopGroupworkerGroup=newNioEventLoopGroup();尝试{//2。创建服务器引导程序/辅助类:ServerBootstrapServerBootstrapb=newServerBootstrap();//3.为boot类配置两大线程组,确定线程模型b.group(bossGroup,workerGroup)。......//6.绑定端口ChannelFuturef=b.bind(port).sync();//等待连接关闭f.channel().closeFuture().sync();}最后{//7。优雅关闭相关线程组资源bossGroup.shutdownGracefully();workerGroup.shutdownGracefully();从上面的例子我们可以看出,Bootstrap通常使用connet()方法连接到远程主机和端口,作为NettyTCP协议通信中的客户端。另外,Bootstrap还可以通过bind()方法绑定一个本地端口作为UDP协议通信的一端。ServerBootstrap通常使用bind()方法绑定到本地端口,然后等待客户端的连接。Bootstrap只需要配置一个线程组——EventLoopGroup,而ServerBootstrap需要配置两个线程组——EventLoopGroup,一个用于接收连接,一个用于具体的IO处理。Channel(网络操作抽象类)Channel接口是Netty对网络操作的抽象类。通过Channel我们可以进行I/O操作。一旦客户端成功连接到服务器,一个新的Channel将被创建并绑定到客户端。示例代码如下://通过Bootstrap的connect方法连接到服务器publicChanneldoConnect(InetSocketAddressinetSocketAddress){CompletableFuture
