我们知道java的I/O模型一共有四种,分别是:传统的BIO,伪异步I/O,NIO和AIO。
Netty是一个高性能、异步事件驱动的NIO框架,它提供了对TCP、UDP和文件传输的支持,作为一个异步NIO框架,Netty的所有IO操作都是异步非阻塞的,通过Future-Listener机制,用户可以方便的主动获取或者通过通知机制获得IO操作结果。作为当前最流行的NIO框架,Netty在互联网领域、大数据分布式计算领域、游戏行业、通信行业等获得了广泛的应用,一些业界著名的开源组件也基于Netty的NIO框架构建。
maven 依赖
<dependency>
<groupId>io.nettygroupId>
<artifactId>netty-allartifactId>
<version>4.0.21.Finalversion>
dependency>
我们不需要使每一个inboundChannel继承于ChannelInboundHandler,这样会需要我们实现ChannelInboundHandler中的所有接口,在一般的channel中我们没有必要这样做,这样只会增加我们的额外的工作量,我们只需要继承ChannelInboundHandlerAdapter,继承它的适配就可以了,我们需要实现几个特别重要的方法,例如读取的方法channelRead和异常处理的方法exceptionCaught
src/main/java/com.abloume/Client/Client01Handler.java
package com.abloume.Client;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
public class Client01Handler extends ChannelInboundHandlerAdapter {
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
System.out.println("Client01Handler Active");
ctx.fireChannelActive(); // 若把这一句注释掉将无法将event传递给下一个ClientHandler
}
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
System.out.println("Client01Handler read Message: "+msg);
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
cause.printStackTrace();
ctx.close();
}
}
src/main/java/com.abloume/Client/Client02Handler.java
package com.abloume.Client;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
public class Client02Handler extends ChannelInboundHandlerAdapter {
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
System.out.println("Client02Handler Active");
}
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
System.out.println("Client02Handler read Message: "+msg);
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
cause.printStackTrace();
ctx.close();
}
}
src/main/java/com.abloume/Client/Client.java
package com.abloume.Client;
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.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;
import io.netty.bootstrap.Bootstrap;
public class Client {
static final String HOST = System.getProperty("host", "127.0.0.1");
static final int PORT = Integer.parseInt(System.getProperty("port", "8080"));
static final int SIZE = Integer.parseInt(System.getProperty("size", "256"));
public static void main(String[] args) throws Exception {
EventLoopGroup group = new NioEventLoopGroup();
try {
Bootstrap b = new Bootstrap();
b.group(group)
.channel(NioSocketChannel.class)
.option(ChannelOption.TCP_NODELAY, true)
.handler(new ChannelInitializer(){
@Override
public void initChannel(SocketChannel ch) throws Exception {
ChannelPipeline p = ch.pipeline();
p.addLast("decoder", new StringDecoder());
p.addLast("encoder", new StringEncoder());
p.addLast(new Client01Handler());
p.addLast(new Client02Handler());
}
});
ChannelFuture future = b.connect(HOST, PORT).sync();
future.channel().writeAndFlush("Hello Netty Server, I am a common client");
future.channel().closeFuture().sync();
} finally {
group.spliterator();
}
}
}
src/main/java/com.abloume/Server/ServerHandler.java
package com.abloume.Server;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
public class ServerHandler extends ChannelInboundHandlerAdapter {
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
System.out.println("Server channelRead....");
System.out.println(ctx.channel().remoteAddress()+"->Server :"+msg.toString());
ctx.write("Server write"+msg);
ctx.flush();
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
cause.printStackTrace();
ctx.close();
}
}
src/main/java/com.abloume/Server/Server.java
package com.abloume.Server;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
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 io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;
import java.net.InetSocketAddress;
public class Server {
private int port;
public Server(int port) {
this.port = port;
}
public void start() {
// netty服务端ServerBootstrap启动的时候,默认有两个eventloop分别是bossGroup和 workGroup
EventLoopGroup boosGroup = new NioEventLoopGroup(1); // bossGroup
EventLoopGroup workerGroup = new NioEventLoopGroup(); // workGroup
try {
ServerBootstrap sbs = new ServerBootstrap().group(boosGroup, workerGroup)
.channel(NioServerSocketChannel.class)
.localAddress(new InetSocketAddress(port))
.childHandler(new ChannelInitializer() {
protected void initChannel(SocketChannel ch) throws Exception {
ch.pipeline().addLast("decoder", new StringDecoder());
ch.pipeline().addLast("encoder", new StringEncoder());
ch.pipeline().addLast(new ServerHandler());
};
}).option(ChannelOption.SO_BACKLOG, 128)
.childOption(ChannelOption.SO_KEEPALIVE, true);
ChannelFuture future = sbs.bind(port).sync();
System.out.println("Server start listen at " + port);
future.channel().closeFuture().sync();
} catch (Exception e) {
boosGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
}
}
public static void main(String[] args) throws Exception {
int port;
if (args.length > 0) {
port = Integer.parseInt(args[0]);
} else {
port = 8080;
}
new Server(port).start();
}
}
一个ChannelHandler只能通过唤醒ChannelHandlerContext中event繁殖的方法,来将一个event递交给它的下一个handler。这些方法包括:
我们平时继承的最多的就是 ChannelInboundHandlerAdapter
和 ChannelOutboundHandlerAdapter
,这两个不是接口也不是抽象类,所以我们可以仅仅重写我们需要的方法。
AbstractBootstrap是抽象类,有两个具体的实现,Bootstrap和ServerBootstrap:
Bootstrap的使用很像Builder模式,Bootstrap就是Builder,EventLoopGroup、Channel和Handler等是各种Part。稍有不同的是,准备好各种Part后,并不是直接build出一个Product来,而是直接通过 connect()
方法使用这个Product。
Group和内部的Loop对象以及Executor创建完毕后什么时候被调用?就是在服务端bind和客户端connect时。