1、配置依赖
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-all</artifactId>
<version>5.0.0.Alpha2</version>
</dependency>
2、服务端
- ChannelInitializer 中,也是一个 Handler,重写了 channelRegistered 方法,在 channel 注册的时候,调用 initChannel 方法后将自己从 pipeline 中移除
- ChannelHandlerAdapter 中,实现了简单的将事件传递给下一个 ChannelHandler 方法的实现,使用的类似 fire 开头的方法,其中 In 和 Out 的继承类,没有重写任何方法
- ChannelHandlerContext 主要用于写出站数据,两种写的方式:直接写到 Channel 和 写到 ChannelHandlerContext 对象中
package com.sample.modules.netty;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import java.net.InetSocketAddress;
public class NettyServer {
/**
* 监听服务器ip和端口
*/
private String host;
private int port;
public NettyServer(String host, int port){
this.host = host;
this.port = port;
}
public void listen() throws Exception {
EventLoopGroup bossGroup = new NioEventLoopGroup();
EventLoopGroup workerGroup = new NioEventLoopGroup();
try {
ServerBootstrap b = new ServerBootstrap();
b.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class)
.childOption(ChannelOption.SO_KEEPALIVE,true)
.option(ChannelOption.SO_BACKLOG, 1024)
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ChannelPipeline pipeline = ch.pipeline();
//添加业务逻辑处理单元
}
});
ChannelFuture f = b.bind(new InetSocketAddress(host, port)).sync();
f.channel().closeFuture().sync();
} finally {
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
}
}
public static void main(String[] args) throws Exception{
new NettyServer("localhost", 8000).listen();
}
}
3、客户端
package com.sample.modules.netty;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
public class NettyClient {
private String host;
private int port;
public NettyClient(String host, int port) {
this.host = host;
this.port = port;
}
public void start() {
EventLoopGroup group = new NioEventLoopGroup();
try {
Bootstrap b = new Bootstrap();
b.group(group)
.option(ChannelOption.SO_KEEPALIVE,true)
.channel(NioSocketChannel.class)
.handler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ChannelPipeline pipeline = ch.pipeline();
//添加业务逻辑处理单元
}
});
ChannelFuture f = b.connect(host,port);
f.channel().closeFuture().sync();
}catch (Exception e){
e.printStackTrace();
}
}
public static void main(String[] args) {
new NettyClient("localhost", 8000).start();
}
}
4、业务逻辑处理单元
// 出站10
pipeline.addLast("server10", new ChannelOutboundHandlerAdapter(){
@Override
public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
System.out.println("out 10");
// 最终将数据发送出去
ctx.writeAndFlush(msg);
}
});
// 出站11
pipeline.addLast("server11", new ChannelOutboundHandlerAdapter(){
@Override
public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
System.out.println("out 11");
// 出站数据继续传递
ctx.writeAndFlush(msg);
}
});
// 入站1
pipeline.addLast("server1", new SimpleChannelInboundHandler<ByteBuf>(){
@Override
protected void messageReceived(ChannelHandlerContext ctx, ByteBuf msg) throws Exception {
System.out.println("in 1: "+msg.toString(CharsetUtil.UTF_8));
// 入站数据继续传递
ReferenceCountUtil.retain(msg);
ctx.fireChannelRead(msg);
// 从当前 handler 往前传递,经过出站11和出站10
ctx.writeAndFlush(Unpooled.copiedBuffer("aa".getBytes()));
// 从尾部 handler 往前传递,经过出站12、出站11和出站10
ctx.channel().writeAndFlush(Unpooled.copiedBuffer("aa".getBytes()));
}
});
// 出站12
pipeline.addLast("server12", new ChannelOutboundHandlerAdapter(){
@Override
public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
System.out.println("out 12");
// 出站数据继续传递
ctx.writeAndFlush(msg);
}
});
// 入站2
pipeline.addLast("server2", new SimpleChannelInboundHandler<ByteBuf>(){
@Override
protected void messageReceived(ChannelHandlerContext ctx, ByteBuf msg) throws Exception {
System.out.println("in 2: "+msg.toString(CharsetUtil.UTF_8));
// 入站数据继续传递
ReferenceCountUtil.retain(msg);
ctx.fireChannelRead(msg);
}
});
// 入站3
pipeline.addLast("server3", new SimpleChannelInboundHandler<ByteBuf>(){
@Override
protected void messageReceived(ChannelHandlerContext ctx, ByteBuf msg) throws Exception {
System.out.println("in 3: "+msg.toString(CharsetUtil.UTF_8));
}
});
5、注意事项
- 如果一个消息被消费或丢弃了,并且没有传递给 pipeline 中的下一个 handler,那么需要调用 ReferenceCountUtil.release(msg) 进行释放
- 引用计数: 一旦消息被编码或解码,就会被 ReferenceCountUtil.release(message) 调用自动释放,如果需要保留引用以便稍后使用,那么可以调用 ReferenceCountUtil.retail(message) 增加该引用计数,防止该消息被释放
6、解码器 -- ByteToMessageDecoder
- 将字节解码为消息
- 由于不知道远程节点是否会一次性的发完一个完整的数据,所有需要对入站数据进行缓冲,直到数据准备好
package com.vim.test.netty;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.ByteToMessageDecoder;
import java.util.List;
public class ToIntegerDecoder extends ByteToMessageDecoder{
@Override
protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> list) throws Exception {
if(in.readableBytes() >= 4){
list.add(in.readInt());
}
}
}