This is the 17th day of my participation in Gwen Challenge
theme: channing-cyan highlight: a11y-dark
All of the examples we’ve reviewed so far use a ByteBuf as the primary data structure for the protocol messages. In this section, we will improve the TIME protocol client and server examples to use POJO instead of ByteBuf.
The advantages of using POJos in your ChannelHandlers are obvious; By separating ByteBuf’s code to extract information from a handler, your handler becomes easier to maintain and reuse. In the TIME client and server example, we only read a 32-bit integer, and using ByteBuf directly is not a major problem. However, you will find that separation is necessary to implement the actual protocol.
First, let’s define a new type called UnixTime.
package io.netty.example.time; 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() { return new Date((value() - 2208988800L) * 1000L).toString(); }}Copy the code
We can now modify TimeDecoder to generate UnixTime instead of ByteBuf
@Override
protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) {
if (in.readableBytes() < 4) {
return;
}
out.add(new UnixTime(in.readUnsignedInt()));
}
Copy the code
With the newer decoder, TimeClientHandler no longer uses ByteBuf:
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) {
UnixTime m = (UnixTime) msg;
System.out.println(m);
ctx.close();
}
Copy the code
Simpler and more elegant, right? You can apply the same techniques on the server side. TimeServerHandler:
@Override
public void channelActive(ChannelHandlerContext ctx) {
ChannelFuture f = ctx.writeAndFlush(new UnixTime());
f.addListener(ChannelFutureListener.CLOSE);
}
Copy the code
Now, the only missing piece is the encoder, which is the ChannelOutboundHandler that converts aUnixTime to ByteBuf. It is much simpler than writing a decoder because there is no packet fragmentation and assembly to deal with when encoding a message.
package io.netty.example.time; 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)}}Copy the code
There are a lot of important things in this business.
- First, we pass the raw data ChannelPromise as-is so that Netty will mark it as a success or failure when the encoded data is actually written to the line.
- Second, we did not call ctx.flush(). There is a separate handler method void lush(ChannelHandlerContext CTX) to override the Flush () operation.
To further simplify, you can use MessageToByteEncoder:
public class TimeEncoder extends MessageToByteEncoder<UnixTime> { @Override protected void encode(ChannelHandlerContext ctx, UnixTime msg, ByteBuf out) { out.writeInt((int)msg.value()); }}Copy the code
The last remaining task is to insert the TimeEncoder into the server-side TimeServerHandler before the ChannelPipeline and leave it as an easy matter.
Disabling your Application Disabling a Netty application is normally like disabling EventLoopGroup where you pass ShutdownException.future When sEventLoopGroup has completely terminated and all S that channels belong to that group have been closed, It returns a notification.