UDP广播事件(二)编写广播者和监视器
一、编写广播者
Netty 提供了大量的类来支持 UDP 应用程序的编写。下面我们列出一些要用到的类型:
名 称 | 描 述 |
---|---|
interface AddressedEnvelope<M, A extends SocketAddress>extends ReferenceCounted | 定义一个消息,其包装了另一个消息并带有发送者和接收者地址。其中 M 是消息类型;A 是地址类型 |
class DefaultAddressedEnvelope<M, A extends SocketAddress>implements AddressedEnvelope<M,A> | 提供了 interface AddressedEnvelope的默认实现 |
class DatagramPacketextends DefaultAddressedEnvelope<ByteBuf, InetSocketAddress> | 扩展了 DefaultAddressedEnvelope 以使用 ByteBuf 作为消息数据容器 |
implements ByteBufHolder | 扩展了 DefaultAddressedEnvelope 以使用 ByteBuf 作为消息数据容器 |
interface DatagramChannelextends Channel | 扩展了 Netty 的 Channel 抽象以支持 UDP 的多播组管理 |
class NioDatagramChannnelextends AbstractNioMessageChannelimplements DatagramChannel | 定义了一个能够发送和接收 AddressedEnvelope 消息的 Channel 类型 |
Netty 的 DatagramPacket 是一个简单的消息容器,DatagramChannel 实现用它来和远程节点通信。类似于在我们先前的类比中的明信片,它包含了接收者(和可选的发送者)的地址以及消息的有效负载本身。
要将 LogEvent 消息转换为 DatagramPacket,我们将需要一个编码器。但是没有必要从头开始编写我们自己的。我们将扩展 Netty 的 MessageToMessageEncoder,这个我们之前使用过。
下面我们看一下将要广播的信息:我们将展示正在广播的 3 个日志条目,每一个都将通过一个专门的 DatagramPacket进行广播。
这是该 LogEventBroadcaster 的 ChannelPipeline 的一个高级别视图,展示了 LogEvent 消息是如何流经它的。
我们会将所有的将要被传输的数据都被封装在了 LogEvent 消息中。LogEventBroadcaster 将把这些写入到 Channel 中,并通过 ChannelPipeline 发送它们,在那里它们将会被转换(编码)为 DatagramPacket 消息。最后,他们都将通过 UDP 被广播,并由远程节点(监视器)所捕获。
下面我们自定义一个MessageToMessageEncoder来执行上面所说的转换:
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.socket.DatagramPacket;
import io.netty.handler.codec.MessageToMessageEncoder;
import io.netty.util.CharsetUtil;
import java.net.InetSocketAddress;
import java.util.List;
/**
* Author: lhd
* Data: 2023/6/13
* Annotate:
*/
public class LogEventEncoder extends MessageToMessageEncoder<LogEvent> {
private final InetSocketAddress remoteAddress;
//LogEventEncoder 创建了即将被发送到指定的InetSocketAddress 的 DatagramPacket 消息
public LogEventEncoder(InetSocketAddress remoteAddress) {
this.remoteAddress = remoteAddress;
}
@Override
protected void encode(ChannelHandlerContext channelHandlerContext, LogEvent logEvent, List<Object> out) throws Exception {
byte[] file = logEvent.getLogfile().getBytes(CharsetUtil.UTF_8);
byte[] msg = logEvent.getMsg().getBytes(CharsetUtil.UTF_8);
ByteBuf buf = channelHandlerContext.alloc()
.buffer(file.length + msg.length + 1);
//将文件名写入到 ByteBuf 中
buf.writeBytes(file);
//添加一个SEPARATOR
buf.writeByte(LogEvent.SEPARATOR);
//将日志消息写入ByteBuf 中
buf.writeBytes(msg);
//将一个拥有数据和目的地地址的新 DatagramPacket 添加到出站的消息列表中
out.add(new DatagramPacket(buf, remoteAddress));
}
}
在 LogEventEncoder 被实现之后,我们就该准备引导该服务器,其包括设置各种各样的 ChannelOption,以及在 ChannelPipeline 中安装所需要的 ChannelHandler,这些都要通过主类 LogEventBroadcaster 完成。
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.Channel;
import io