netty简介

官网的大字:

Netty is an asynchronous event-driven network application framework for rapid development of maintainable high performance protocol servers & clients.

netty是一个异步的,事件驱动的网络应用框架,用来开发高性能的server和client。

我们跟着官网走一遍例子,就能了解它了。


引入依赖:

	<dependency>
		<groupId>io.netty</groupId>
		<artifactId>netty-all</artifactId>
		<version>4.1.42.Final</version>
	</dependency>

Inbound Handler

我们先写server的一个handler,并且不对client的信息进行处理:

/**
 * handles a server-side channel
 */

public class DiscardServerHandler extends ChannelInboundHandlerAdapter {
	@Override
	public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
		((ByteBuf)msg).release();
	}

	@Override
	public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
		cause.printStackTrace();
		ctx.close();
	}
}

ChannelInboundHandlerAdapter实现了ChannelInboundHandler

对于后者:

/**
 * {@link ChannelHandler} which adds callbacks for state changes. This allows the user to hook in to state changes easily.
 */

它提供了许多状态改变的回掉函数。

我们当前用的适配器会把操作推向下一个ChannelHandler。

/**
This implementation just forward the operation to the next {@link ChannelHandler} in the {@link ChannelPipeline}. 
*/

这里的代码注意两点:

  • 你要手动释放消息
  • 我们尝试捕捉IO异常

Server

现在我们要写一个Server,并且携带上上面的DiscardServerHandler


/**
 * Discards any incoming data
 */
public class DiscardServer {
	private int port;

	public DiscardServer(int port){
		this.port=port;
	}

	public void run() throws Exception{
		EventLoopGroup bossGroup = new NioEventLoopGroup();
		EventLoopGroup workerGroup = new NioEventLoopGroup();

		try{
			ServerBootstrap b = new ServerBootstrap();
			b.group(bossGroup,workerGroup)
					.channel(NioServerSocketChannel.class)
					.childHandler(new ChannelInitializer<SocketChannel>() {
						@Override
						protected void initChannel(SocketChannel ch) throws Exception {
							ch.pipeline().addLast(new DiscardServerHandler());
						}
					})
					.option(ChannelOption.SO_BACKLOG,128)
					.childOption(ChannelOption.SO_KEEPALIVE,true);

			//bind and start to accept incoming connections
			ChannelFuture f = b.bind(port).sync();

			//wait until the server socket is closed


			//shut down your server
			f.channel().closeFuture().sync();
		}finally {
			workerGroup.shutdownGracefully();
			bossGroup.shutdownGracefully();
		}
	}

	public static void main(String[] args) throws Exception {
		int port = 8080;
		if(args.length>0){
			port=Integer.parseInt(args[0]);
		}

		new DiscardServer(port).run();
	}
}

这里的代码要一句句分析。

	public DiscardServer(int port){
		this.port=port;
	}

port构建DiscardServer


EventLoopGroup bossGroup = new NioEventLoopGroup();

EventLoopGroup workerGroup = new NioEventLoopGroup();

我们建了两个EventLoopGroup

/**
allows registering {@link Channel}s that 
get processed for later selection during the event loop.

它是用来注册Channel的。

我们用NioEventLoopGroup作为实现:

/**
used for NIO {@link Selector} based {@link Channel}s.
*/

它用于我们熟悉的基于SelectorChannel

但是我们整了两个group,一个是bossGroup,一个是workerGroup

boss是用来接受客户端的请求的,请求接受之后要监听客户端的数据传输,这时候就要worker工作了。


 ServerBootstrap b = new ServerBootstrap();

ServerBootstrap是用来构建ServerChannel的工具,你可以手动构建,也可以用ServerBootstrap


.channel(NioServerSocketChannel.class)

我们要用的Channel是什么,就是NioServerSocketChannel。这是基于nio的Channel

/**
 * A {@link io.netty.channel.socket.ServerSocketChannel} implementation which uses
 * NIO selector based implementation to accept new connections.
 */

.childHandler(new ChannelInitializer<SocketChannel>() {
	@Override
    protected void initChannel(SocketChannel ch) throws Exception {
	ch.pipeline()
	.addLast(new DiscardServerHandler());
						
		}
	}
)

我们关注ChannelInitializer这个抽象类:

/**
 * A special {@link ChannelInboundHandler} which offers an easy way to initialize a {@link Channel} once it was
 * registered to its {@link EventLoop}.
 */

当一个Channel注册到EventLoop后,ChannelInitializer负责初始化这个Channel

代码示例:

* <pre>
 *
 * public class MyChannelInitializer extends {@link ChannelInitializer} {
 *     public void initChannel({@link Channel} channel) {
 *         channel.pipeline().addLast("myHandler", new MyHandler());
 *     }
 * }
 *
 * {@link ServerBootstrap} bootstrap = ...;
 * ...
 * bootstrap.childHandler(new MyChannelInitializer());
 * ...
 * </pre>

这和我们用匿名内部类的方式是一样的。


注意:

ch.pipeline()

返回一个ChannelPipeline,这个东西是什么呢?

/**
 * A list of {@link ChannelHandler}s which handles or intercepts inbound events and outbound operations of a {@link Channel}.
 * /

它是ChannelHandler的集合,用来处理或者拦截一个Channel中到达的事件和发出的操作。

简单说,Pipeline是一个抽象,它就是一组Handler

/**
Each channel has its own pipeline and 
it is created automatically 
when a new channel is created.
*。

一个Channel对应着一个Pipeline

一个IO事件被ChannelInboundHandler或者被ChannelOutboundHandler处理,然后通过

ChannelHandlerContext.fireChannelRead(Object)

或者是

ChannelHandlerContext.write(Object)

方法将IO事件传递给最近的Handler


addLast(new DiscardServerHandler())

将我们写的Inbound Handler添加到Pipeline


.option(ChannelOption.SO_BACKLOG,128)

设置了等待连接的队列长度,我们现在是在listen,并没有accept,所以打过来的请求先进listen队列中等着。


.childOption(ChannelOption.SO_KEEPALIVE, true);

要求保持连接。

option()是对NioServerSocketChannel而言的,是针对bossGroup的,而childOption是针对workerGroup的。


ChannelFuture f = b.bind(port).sync();

绑定端口。


修改Inbound Handler

为了更好的测试,我们修改一下DiscardServerHandler,让它能够打印客户端发来的数据:

@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
		ByteBuf in = (ByteBuf) msg;
		try{
			while(in.isReadable()){
				System.out.print((char)in.readByte());
				System.out.flush();
			}
		}finally {
			ReferenceCountUtil.release(msg);
		}

	}

一旦有数据发来,channelRead就会被调用。


测试:

开启server,在命令行输入telnet localhost 8080,此时就可以往服务端发送信息了:

展开阅读全文

没有更多推荐了,返回首页

©️2019 CSDN 皮肤主题: 游动-白 设计师: 上身试试
应支付0元
点击重新获取
扫码支付

支付成功即可阅读