MessageToByteEncoder
MessageToByteEncoder is an abstract encoder, and subclasses can override the encode method to encode objects into ByteBuf output.
MessageToByteEncoder inherits from ChannelOutboundHandlerAdapter, encode is called when outbound.
public class MyMessageEncoder extends MessageToByteEncoder<MessagePO> {
@Override
protected void encode(ChannelHandlerContext ctx, MessagePO msg, ByteBuf out) throws Exception {
System.out.println("MyMessageEncoder.encode,被调用");
String json = JSONObject.toJSONString(msg);
out.writeInt(json.getBytes(StandardCharsets.UTF_8).length);
out.writeBytes(json.getBytes(StandardCharsets.UTF_8));
}
}
ByteToMessageDecoder
ByteToMessageDecoder is a ChannelInboundHandler, which can be called a decoder, and is responsible for converting byte stream (ByteBuf) into a Message. Message is a Java object that the application can define itself.
ByteToMessageDecoder: Used to convert bytes to messages, it is necessary to check whether there are enough bytes in the buffer.
public class MyMessageDecoder extends ByteToMessageDecoder {
@Override
protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
System.out.println("MyMessageDecoder.decode,被调用");
while (in.readableBytes() >= 4){
int num = in.readInt();
System.out.println("解码出一个整数:"+num);
out.add(num);
}
}
}
ReplayingDecoder
ReplayingDecoder: Inherited from ByteToMessageDecoder, there is no need to check whether there are enough bytes in the buffer, but ReplayingDecoder is slightly slower than ByteToMessageDecoder, and not all ByteBufs support it.
Use ReplayingDecoder if the project complexity is high, otherwise use ByteToMessageDecoder.
public class MyMessageDecoder extends ReplayingDecoder<Void> {
@Override
protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
System.out.println("MyMessageDecoder.decode,被调用");
int length = in.readInt();
byte[] content = new byte[length];
in.readBytes(content);
String json = new String(content,StandardCharsets.UTF_8);
MessagePO po = JSONObject.parseObject(json,MessagePO.class);
out.add(po);
}
}
MessageToMessageEncoder
Used to encode from one message to another, such as from POJO to POJO, is a ChannelOutboundHandler
MessageToMessageDecoder
Decoding from one message to another, such as POJO to POJO, is a ChannelInboundHandler
MessageToMessageCodec
Integrated MessageToMessageEncoder and MessageToMessageDecoder
public class RequestMessageCodec extends MessageToMessageCodec<String, RequestData> {
@Override
protected void encode(ChannelHandlerContext ctx, RequestData msg, List<Object> out) throws Exception {
System.out.println("RequestMessageCodec.encode 被调用 " + msg);
String json = JSONObject.toJSONString(msg);
out.add(json);
}
@Override
protected void decode(ChannelHandlerContext ctx, String msg, List<Object> out) throws Exception {
System.out.println("RequestMessageCodec.decode 被调用 " + msg);
RequestData po = JSONObject.parseObject(msg, RequestData.class);
out.add(po);
}
}
ChannelInitializer
ChannelInitializer is a special ChannelInboundHandler that can initialize the Channel in a simple way (calling the initChannel method).
Usually set ChannelPipeline for Channel in Bootstrap.handler(ChannelHandler)
, andServerBootstrap.handler(ChannelHandler)
.ServerBootstrap.childHandler(ChannelHandler)
Note: When initChannel is executed, the current handler will be removed from the Pipeline.
Bootstrap bootstrap = new Bootstrap().group(group)//设置线程组
.channel(NioSocketChannel.class)//设置客户端通道的实现类
.handler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ch.pipeline().addLast(new NettyClientHandler());//加入自己的处理器
}
});
ServerBootstrap bootstrap = new ServerBootstrap().group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class)//使用NioServerSocketChannel作为服务器的通道实现
.option(ChannelOption.SO_BACKLOG, 128)//设置线程队列等待连接的个数
.childOption(ChannelOption.SO_KEEPALIVE, true)//设置保持活动连接状态
// .handler(null)//该Handler对应bossGroup
.childHandler(new ChannelInitializer<SocketChannel>() {
//给workerGroup的EventLoop对应的管道设置处理器
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ch.pipeline().addLast(new NettyServerHandler());
}
});
SimpleChannelInboundHandler
SimpleChannelInboundHandler inherits from ChannelInboundHandlerAdapter, and can specify the message type through generics.
To process incoming data we only need to implement the channelRead0 method.
SimpleChannelInboundHandler will automatically release the Bytebuffer resource occupied by the data after receiving the data, while ChannelInboundHandlerAdapter will not automatically release it.
public class MyClientHandler extends SimpleChannelInboundHandler<MessagePO> {
@Override
protected void channelRead0(ChannelHandlerContext ctx, MessagePO msg) throws Exception {
System.out.println("收到服务端消息:" + msg);
}
}
DefaultEventLoopGroup
When adding a ChannelHandler to the pipline, a new thread group can be provided, and the Handler business will be executed in this thread.
DefaultEventLoopGroup can come in handy when adding ChannelHandler to perform multi-threaded concurrent business.
If DefaultEventLoopGroup is not set, the default isEventLoopGroup workerGroup = new NioEventLoopGroup();
DefaultEventLoopGroup businessGroup = new DefaultEventLoopGroup(100);
...
addLast(businessGroup, new MyNettyServerHandler())
/**
* 读取客户端发送过来的消息
*/
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
ByteBuf byteBuf = (ByteBuf) msg;
System.out.println("收到客户信息:" + byteBuf.toString(CharsetUtil.UTF_8));
System.out.println("客户端地址:" + ctx.channel().remoteAddress());
System.out.println("处理线程:" + Thread.currentThread().getName());
ctx.executor().parent().execute(()->{
try {
System.out.println("parent().execute Thread = " + Thread.currentThread().getName());
TimeUnit.SECONDS.sleep(2L);
System.out.println("parent任务执行完成1");
} catch (InterruptedException e) {
e.printStackTrace();
}
});
ctx.executor().parent().execute(()->{
try {
System.out.println("parent().execute Thread = " + Thread.currentThread().getName());
TimeUnit.SECONDS.sleep(2L);
System.out.println("parent任务执行完成2");
} catch (InterruptedException e) {
e.printStackTrace();
}
});
ctx.executor().parent().execute(()->{
try {
System.out.println("parent().execute Thread = " + Thread.currentThread().getName());
TimeUnit.SECONDS.sleep(2L);
System.out.println("parent任务执行完成3");
} catch (InterruptedException e) {
e.printStackTrace();
}
});
}
The above code execution log is as follows:
收到客户信息:Hello 服务端
客户端地址:/127.0.0.1:60345
处理线程:defaultEventLoopGroup-4-1
parent().execute Thread = defaultEventLoopGroup-4-2
parent().execute Thread = defaultEventLoopGroup-4-3
程序继续~~ defaultEventLoopGroup-4-1
parent().execute Thread = defaultEventLoopGroup-4-4
parent任务执行完成1
parent任务执行完成3
parent任务执行完成2
EventLoop timed task
ctx.channel().eventLoop().schedule()
Timed tasks can be added through methods in Handler
ctx.channel().eventLoop().schedule(()->{
try {
System.out.println("Thread.currentThread().getName() = " + Thread.currentThread().getName());
TimeUnit.SECONDS.sleep(2L);
System.out.println("定时任务执行完成");
} catch (InterruptedException e) {
e.printStackTrace();
}
},10L,TimeUnit.SECONDS);