Netty实践-使用POJO代替ByteBuf
精华
小牛编辑
186浏览
2023-03-14
到目前为止,我们上面几篇教程中的所有例子都使用ByteBuf作为协议消息的主要数据结构。 在本节中,我们将改进TIME协议的客户端和服务器示例,让它们使用POJO来代替原来的ByteBuf。
在ChannelHandler
中使用POJO的优点是显而易见的; 处理程序将从ByteBuf中提取信息的代码,将从处理程序中分离出来,变得更易维护和可重用。 在TIME客户端和服务器示例中,我们只读取一个32位整数,它不是直接使用ByteBuf来解码转换的。在实现真实世界协议时,这是必须要进行分离的。
首先,我们定义一个名为 UnixTime
的新类型(一个简单的Java类)。
package com.yiibai.netty.timepojo;
import java.util.Date;
public class UnixTime {
private final long value;
public UnixTime() {
this(System.currentTimeMillis() / 1000L + 2208988800L);
}
public UnixTime(long value) {
this.value = value;
}
public long value() {
return value;
}
@Override
public String toString() {
Date date = new Date((value() - 2208988800L) * 1000L);
SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String dateString = formatter.format(date);
return dateString;
}
}
我们现在来修改时间解码器(TimeDecoder
)来生成UnixTime
,而不是ByteBuf
。
@Override
protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) {
if (in.readableBytes() < 4) {
return;
}
out.add(new UnixTime(in.readUnsignedInt()));
}
使用更新的解码器,TimeClientHandler
不再使用ByteBuf:
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) {
UnixTime m = (UnixTime) msg;
System.out.println(m);
ctx.close();
}
怎么样?看起更简单和优雅,对吧? 相同地也可以应用在服务器端。现在我们首先更新TimeServerHandler
中的代码:
@Override
public void channelActive(ChannelHandlerContext ctx) {
ChannelFuture f = ctx.writeAndFlush(new UnixTime());
f.addListener(ChannelFutureListener.CLOSE);
}
现在,唯一缺少的是一个编码器,它是一个ChannelOutboundHandler
的实现,是将UnixTime
转换回ByteBuf
。 它比编写解码器简单得多,因为在编码消息时不需要处理数据包分段和组合。
package com.yiibai.netty.timepojo;
public class TimeEncoder extends ChannelOutboundHandlerAdapter {
@Override
public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) {
UnixTime m = (UnixTime) msg;
ByteBuf encoded = ctx.alloc().buffer(4);
encoded.writeInt((int)m.value());
ctx.write(encoded, promise); // (1)
}
}
- 在这一行有很多重要的东西。
首先,我们按原样传递原始的ChannelPromise
,以便Netty将编码数据实际写入时将其标记为成功或失败。
第二步,我们没有调用ctx.flush()
。 有一个单独的处理程序方法void flush(ChannelHandlerContext ctx)
,它用于覆盖flush()
操作。
要进一步简化,可以使用MessageToByteEncoder
:
public class TimeEncoder extends MessageToByteEncoder<UnixTime> {
@Override
protected void encode(ChannelHandlerContext ctx, UnixTime msg, ByteBuf out) {
out.writeInt((int)msg.value());
}
}
剩下的最后一个任务是在TimeServerHandler
之前将TimeEncoder
插入到服务器端的ChannelPipeline
中,这里将留作一个简单的练习吧。
参考代码: Netty->NettyTutorial->com.yiibai.netty.pojo
;