netty 5正式版还没有出,项目准备从netty 3升级到netty4。
mina与netty 是同一个人写的,mina将内核和一些特性的联系过于紧密,使得用户在不需要这些特性的时候无法脱离,相比下性能会有所下降;netty解决了这个设计问题,按照这个理论netty 是mina 的升级。
从netty3,到netty4的代码变话还是挺多的,首先看看bootstrap,它的结构可以说比较简单,涉及的类和接口很少;如图:
Bootstrap则是客户端程序用的引导类,ServerBootstrap是服务端程序用的引导类,Bootstrap 和ServerBootstrap都继承自AbstractBootstrap,ChannelFactory用于处理channel。
ChannelFuture被拆分为ChannelFuture和ChannelPromise。这不仅仅是让异步操作里的生产者和消费者间的约定更明显,同样也是得在使用从链中返回的ChannelFuture更加安全,因为ChannelFuture的状态是不能改变的。
由于这个编号,一些方法现在都采用ChannelPromiser而不是ChannelFuture来改变它的状态。
在编码解码器框架里有实质性的内部改变,因为4.0需要一个处理器来创建和管理它的缓存(看这篇文章的每个处理器缓存部分。)然而,从用户角度来看这些变化都不是很大的。核心编码界面器类移到io.netty.handler.codec包里。FrameDecoder重命名为ByteToMessageDecoder。OneToOneEncoder和OneToOneDecoder由MessageToMessageEncoder和MessageToMessageDecoder替换。
看了看官网netty 4的入门实践 http://netty.io/wiki/user-guide-for-4.x.html 写了个例子,体验和一步一步来研究一下netty 4
首先来写服务端:
public class ByteServer {
private final static String host = "127.0.0.1";
private final static Integer port = 8898;
public static void main(String[] args) {
/***
* ·NioEventLoopGroup 实际上是个连接池,NioEventLoopGroup在后台启动了n个NioEventLoop
* 来处理Channel事件,每个NioEventLoop负责m个Channel
* ·NioEventLoopGroup从NioEventLoop数组集中挨个取出NioEventLoop用以处理Channel
*/
EventLoopGroup bossGroup = new NioEventLoopGroup();
EventLoopGroup workerGroup = new NioEventLoopGroup();
try {
ServerBootstrap serverBootstrap = new ServerBootstrap();
serverBootstrap.group(bossGroup, workerGroup);
// 设置 nio 类型的 channel
serverBootstrap.channel(NioServerSocketChannel.class);
serverBootstrap.childHandler(new ByteServerInitializer());
//此处在写一个TCP/IP 的服务端,因此我们被允许设置 socket 的参数选项比如tcpNoDelay 和 keepAlive。
serverBootstrap.option(ChannelOption.SO_BACKLOG, 128);
/***
* option() 是提供NioServerSocketChannel用来接收进来的连接。
* childOption() 是提供父管道ServerChannel接收到的
* 连接(此例是 NioServerSocketChannel)。
*/
serverBootstrap.childOption(ChannelOption.SO_KEEPALIVE, true);
// 服务器绑定端口监听(sync,同步方法阻塞直到绑定成功)
ChannelFuture channelFuture = serverBootstrap.bind(host, port).sync();
// 监听服务器关闭监听(应用程序等待直到channel关闭)
channelFuture.channel().closeFuture().sync();
// logger.info("TCP服务器已启动");
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
//关闭EventLoopGroup释放资源包
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
}
}
}
是不是感觉比netty 3 的代码变化了好多,接下来写ByteServerInitializer类:
public class ByteServerInitializer extends ChannelInitializer<SocketChannel> {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ChannelPipeline pipeline = ch.pipeline();
/***
* 个地方的 必须和服务端对应上。否则无法正常解码和编码
*/
pipeline.addLast(new LengthFieldBasedFrameDecoder(Integer.MAX_VALUE, 0, 8, 0, 8));
pipeline.addLast(new LengthFieldPrepender(8));
//当 read 的时候
pipeline.addLast(new StringDecoder(CharsetUtil.UTF_8));
//当 send 的时候
pipeline.addLast(new StringEncoder(CharsetUtil.UTF_8));
//服务端逻辑
pipeline.addLast(new ByteServerHandler());
}
}
接下来写服务端handler 类,处理数据:
public class ByteServerHandler extends ChannelInboundHandlerAdapter {
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
if (null != msg) {
// ByteBuf byteBuf = ((ByteBuf) msg);
StringBuffer sbf = new StringBuffer("收到客户端->");
sbf.append(ctx.channel().remoteAddress());
sbf.append("的消息:");
sbf.append(msg);
System.out.println(sbf);
ctx.writeAndFlush("ok");
// byteBuf.release();
ctx.close();
}
}
/***
* 覆盖了 channelActive() 事件处理方法。服务端监听到客户端活动
*
* @param ctx
* @throws Exception
*/
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
System.out.println("channelActive>>>> Client:" + ctx.channel().remoteAddress() + "在线");
ctx.fireChannelActive();
}
/***
* 监听客户端掉线
*
* @param ctx
* @throws Exception
*/
@Override
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
// System.out.println("channelInactive>>>> Client:"+ctx.channel().remoteAddress()+"掉线");
super.channelInactive(ctx);
}
/*****
* 异常信息 (根据需要,选择是否关闭)
*
* @param ctx
* @param cause
* @throws Exception
*/
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
System.out.println("server exception is general");
ctx.fireExceptionCaught(cause);
// ctx.close();
}
}
客户端
public class ByteClient {
private final static String host = "127.0.0.1";
private final static Integer port = 8898;
public static void main(String[] args) {
EventLoopGroup group = null;
try {
group = new NioEventLoopGroup();
Bootstrap bootstrap = new Bootstrap();
bootstrap.group(group);
bootstrap.channel(NioSocketChannel.class);
bootstrap.handler(new ByteClientInitializer());
bootstrap.option(ChannelOption.SO_KEEPALIVE, true);
Channel ch = bootstrap.connect(host, port).sync().channel();
ch.writeAndFlush("发送一条指令:我的小鱼你醒了,还认识早晨吗?" + Thread.currentThread().getName());
ch.closeFuture().sync();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
if(null!=group){
group.shutdownGracefully();
}
}
}
}
public class ByteClientInitializer extends ChannelInitializer<SocketChannel> {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ChannelPipeline pipeline = ch.pipeline();
/****
* 个地方的 必须和服务端对应上。否则无法正常解码和编码
*/
pipeline.addLast("frameDecoder", new LengthFieldBasedFrameDecoder(Integer.MAX_VALUE, 0, 8, 0, 8));
pipeline.addLast("frameEncoder", new LengthFieldPrepender(8));
//当 read 的时候
pipeline.addLast("decoder", new StringDecoder(CharsetUtil.UTF_8));
//当 send 的时候
pipeline.addLast("encoder", new StringEncoder(CharsetUtil.UTF_8));
// 客户端的逻辑
pipeline.addLast("handler", new ByteClientHander());
}
}
public class ByteClientHander extends ChannelInboundHandlerAdapter {
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
if(null!=msg){
try {
System.out.println("服务端返回消息:" + msg);
}finally {
ReferenceCountUtil.release(msg);
}
}
}
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
super.channelActive(ctx);
}
/***
* 异常处理
* @param ctx
* @param cause
* @throws Exception
*/
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
System.out.println("client exception is general");
/***
* 异常信息
* cause.printStackTrace();
*/
ctx.fireExceptionCaught(cause);
}
}
客户端向服务端,发数据服务端如果返回OK则表示数据发送成功,没有包含其他逻辑;一个简单的例子就成了。