Netty初次使用

来源:互联网 发布:淘宝主图尺寸怎么调整 编辑:程序博客网 时间:2024/06/06 15:37

Netty初次使用

简介

Netty是由JBOSS提供的一个java开源框架。Netty提供异步的、事件驱动的网络应用程序框架和工具,用以快速开发高性能、高可靠性的网络服务器和客户端程序。 也就是说,Netty 是一个基于NIO的客户、服务器端编程框架,使用Netty 可以确保你快速和简单的开发出一个网络应用,例如实现了某种协议的客户,服务端应用。Netty相当简化和流线化了网络应用的编程开发过程,例如,TCP和UDP的socket服务开发。

使用

服务端的建立:

1 创建ServerBootstrap实例。ServerBootstrap是Netty服务端的启动辅助类,它提供了一系列的方法用于设置服务端启动相关的参数。底层通过门面模式对各种能力进行抽象和封装,尽量不需要用户跟过多的底层API打交道,降低用户的开发难度。

2 设置并绑定Reactor线程池。Netty的Reactor线程池是EventLoopGroup,它实际就是EventLoop的数组。EventLoop的职责是处理所有注册到本线程多路复用器Selector上的Channel,Selector的轮询操作由绑定的EventLoop线程run方法驱动,在一个循环体内循环执行。值得说明的是,EventLoop的职责不仅仅是处理网络I/O事件,用户自定义的Task和定时任务Task也统一由EventLoop负责处理,这样线程模型就实现了统一。从调度层面看,也不存在在EventLoop线程中再启动其它类型的线程用于异步执行其它的任务,这样就避免了多线程并发操作和锁竞争,提升了I/O线程的处理和调度性能。

3 设置并绑定服务端Channel。作为NIO服务端,需要创建ServerSocketChannel,Netty对原生的NIO类库进行了封装,对应实现是NioServerSocketChannel。对于用户而言,不需要关心服务端Channel的底层实现细节和工作原理,只需要指定具体使用哪种服务端Channel即可。因此,Netty的ServerBootstrap方法提供了channel方法用于指定服务端Channel的类型。Netty通过工厂类,利用反射创建NioServerSocketChannel对象。由于服务端监听端口往往只需要在系统启动时才会调用,因此反射对性能的影响并不大。

4 链路建立的时候创建并初始化ChannelPipeline。ChannelPipeline并不是NIO服务端必需的,它本质就是一个负责处理网络事件的职责链,负责管理和执行ChannelHandler。网络事件以事件流的形式在ChannelPipeline中流转,由ChannelPipeline根据ChannelHandler的执行策略调度ChannelHandler的执行。

5 初始化ChannelPipeline完成之后,添加并设置ChannelHandler。ChannelHandler是Netty提供给用户定制和扩展的关键接口。利用ChannelHandler用户可以完成大多数的功能定制,例如消息编解码、心跳、安全认证、TSL/SSL认证、流量控制和流量整形等。Netty同时也提供了大量的系统ChannelHandler供用户使用,比较实用的系统ChannelHandler总结如下:

  • 系统编解码框架-ByteToMessageCodec;

  • 通用基于长度的半包解码器-LengthFieldBasedFrameDecoder;

  • 码流日志打印Handler-LoggingHandler;

  • SSL安全认证Handler-SslHandler;

  • 链路空闲检测Handler-IdleStateHandler;

  • 流量整形Handler-ChannelTrafficShapingHandler;

  • Base64编解码-Base64Decoder和Base64Encoder。

6 绑定并启动监听端口。在绑定监听端口之前系统会做一系列的初始化和检测工作,完成之后,会启动监听端口,并将ServerSocketChannel注册到Selector上监听客户端连接

7 Selector轮询。由Reactor线程NioEventLoop负责调度和执行Selector轮询操作,选择准备就绪的Channel集合

8 当轮询到准备就绪的Channel之后,就由Reactor线程NioEventLoop执行ChannelPipeline的相应方法,最终调度并执行ChannelHandler

9 执行Netty系统ChannelHandler和用户添加定制的ChannelHandler。ChannelPipeline根据网络事件的类型,调度并执行ChannelHandler

简单实例:

服务端:

package mynetty;import java.nio.channels.SocketChannel;import io.netty.bootstrap.ServerBootstrap;import io.netty.channel.Channel;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.nio.NioServerSocketChannel;public class Server {int port;public Server(int port) {this.port = port;}public void start() throws InterruptedException {EventLoopGroup eventLoopGroup = new NioEventLoopGroup();//用于服务器端接受客户端的连接ServerBootstrap serverBootstrap = new ServerBootstrap();//用于网络事件的处理serverBootstrap.group(eventLoopGroup) //绑定线性池.channel(NioServerSocketChannel.class) //指定使用的channel.localAddress(this.port).childHandler(new ChannelInitializer<Channel>() { //绑定客户端连接后触发的操作@Overrideprotected void initChannel(Channel arg0) throws Exception {System.out.println("等待连接。。。。。。。");arg0.pipeline().addLast(new ServerHandler());//客户端触发操作}});ChannelFuture cf = serverBootstrap.bind().sync();//服务端异步创建绑定System.out.println("服务器开始在端口"+this.port+"监听");cf.channel().closeFuture().sync(); //关闭服务器通道}  public static void main(String[] args) throws Exception {  Server server = new Server(3391);  server.start();  }}

服务端对客户端的处理:

package mynetty;import io.netty.buffer.Unpooled;import io.netty.channel.ChannelFutureListener;import io.netty.channel.ChannelHandlerContext;import io.netty.channel.ChannelInboundHandlerAdapter;public class ServerHandler extends ChannelInboundHandlerAdapter{/* * (non-Javadoc) * @see io.netty.channel.ChannelInboundHandlerAdapter#channelRead(io.netty.channel.ChannelHandlerContext, java.lang.Object) * 当收到对方发来的数据后,就会触发,参数msg就是发来的信息,可以是基础类型,也可以是序列化的复杂对象。 */@Overridepublic void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {ctx.write(msg);}/* * (non-Javadoc) * @see io.netty.channel.ChannelInboundHandlerAdapter#channelReadComplete(io.netty.channel.ChannelHandlerContext) * channelRead执行后触发 */@Overridepublic void channelReadComplete(ChannelHandlerContext ctx) throws Exception {ctx.writeAndFlush(Unpooled.EMPTY_BUFFER).addListener(ChannelFutureListener.CLOSE);}}

客户端:

package mynetty;import io.netty.bootstrap.Bootstrap;import io.netty.channel.Channel;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.nio.NioSocketChannel;public class Client {String hoString;int port;public Client(String hoString,int port) {this.hoString = hoString;this.port = port;}public void start() throws InterruptedException {EventLoopGroup  eventLoopGroup = new NioEventLoopGroup();Bootstrap bootstrap = new Bootstrap();bootstrap.group(eventLoopGroup) //绑定线程池.channel(NioSocketChannel.class) //使用NioSocketChannel来作为连接用的channel类.remoteAddress(this.hoString,this.port) //绑定端口和host信息.handler(new ChannelInitializer<Channel>() {  //绑定连接初始化器@Overrideprotected void initChannel(Channel arg0) throws Exception {arg0.pipeline().addLast(new ClientHandler());}});System.out.println("创建客户端。。。。");ChannelFuture cf = bootstrap.connect().sync(); //异步连接服务器System.out.println("创建成功");cf.channel().closeFuture().sync(); //异步关闭连接通道}public static void main(String[] args) throws InterruptedException {Client client = new Client("127.0.0.1",3391);client.start();}}

客户端处理:

package mynetty;import java.nio.charset.Charset;import io.netty.buffer.ByteBuf;import io.netty.buffer.Unpooled;import io.netty.channel.ChannelHandlerContext;import io.netty.channel.SimpleChannelInboundHandler;import io.netty.util.CharsetUtil;public class ClientHandler extends SimpleChannelInboundHandler<ByteBuf> {/* * (non-Javadoc) * @see io.netty.channel.SimpleChannelInboundHandler#channelRead0(io.netty.channel.ChannelHandlerContext, java.lang.Object) * 接收到服务器数据后被调用 */@Overrideprotected void channelRead0(ChannelHandlerContext arg0, ByteBuf arg1) throws Exception {System.out.println("客户端进行读取");ByteBuf buf = arg1.readBytes(arg1.readableBytes());System.out.println(buf.toString(Charset.forName("utf-8")));}/* * (non-Javadoc) * @see io.netty.channel.ChannelInboundHandlerAdapter#channelActive(io.netty.channel.ChannelHandlerContext) * 连接到服务器后被调用 */@Overridepublic void channelActive(ChannelHandlerContext ctx) throws Exception {System.out.println("客户端创建成功");ctx.writeAndFlush(Unpooled.copiedBuffer("Hello",CharsetUtil.UTF_8));}@Overridepublic void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {super.exceptionCaught(ctx, cause);}}