UDP 广播

面向连接的传输(如 TCP)管理两个网络端点之间的连接的建立,在连接的生命周期的有序和可靠的消息传输,以及最后,连接的有序终止。相比之下,类似 UDP 的无连接协议中则没有持久化连接的概念,此外,UDP 也没有 TCP 的纠错机制。但 UDP 的性能比 TCP 要好很多,适合那些能够处理或者忍受消息丢失的应用程序

目前为止,我们所有的例子都是采用一种叫作单播的传输模式,定义为发送消息给一个由唯一地址所标识的单一的网络目的地。面向连接的协议和无连接协议都支持这种模式

UDP 提供了向多个接收者发送消息的额外传输模式:

  • 多播:传输到一个预定义的主机组
  • 广播:传输到网络(子网)上的所有主机

本章示例将通过发送能够被同一个网络中的所有主机接收的消息来演示 UDP 广播的使用

UDP 示例应用程序

我们的程序将打开一个文件,随后通过 UDP 把每一行作为一个消息广播到一个指定的端口。而接收方只需简单地在指定端口上启动一个监听程序,便可以创建一个事件监视器来接受消息。本次示例以日志文件处理程序为例

1. 消息 POJO:LogEvent

在这个应用程序中,我们将会把消息作为事件处理,并且由于该数据来自于日志文件,所以我们将它称为 LogEvent

public class LogEvent {

    public static final byte SEPARATOR = (byte) ':';
private final InetSocketAddress source;
private final String logfile;
private final String msg;
private final long received; public LogEvent(String logfile, String msg) {
this(null, logfile, msg, -1);
} public LogEvent(InetSocketAddress source, String logfile, String msg, long received) {
this.source = source;
this.logfile = logfile;
this.msg = msg;
this.received = received;
} public InetSocketAddress getSource() {
return source;
} public String getLogfile() {
return logfile;
} public String getMsg() {
return msg;
} public long getReceived() {
return received;
}
}

2. 编写广播者

Netty 的 DatagramPacket 是一个简单的消息容器,DatagramChannel 实现和远程节点的通信,要将 LogEvent 消息转换为 DatagramPacket,我们需要一个编码器

下述是编码器的代码实现

public class LogEventEncoder extends MessageToMessageEncoder<LogEvent> {

    private final InetSocketAddress remoteAddress;

    public LogEventEncoder(InetSocketAddress remoteAddress) {
this.remoteAddress = remoteAddress;
} @Override
protected void encode(ChannelHandlerContext ctx, LogEvent logEvent, List<Object> out) throws Exception {
byte[] file = logEvent.getLogfile().getBytes(StandardCharsets.UTF_8);
byte[] msg = logEvent.getMsg().getBytes(StandardCharsets.UTF_8);
ByteBuf buf = ctx.alloc().buffer(file.length + msg.length + 1);
buf.writeBytes(file);
buf.writeByte(LogEvent.SEPARATOR);
buf.writeBytes(msg);
out.add(new DatagramPacket(buf, remoteAddress));
}
}

接下来准备引导该服务器,包括设置 ChannelOption,以及在 ChannelPipeline 中安装所需的 ChannelHandler,这部分通过主类 LogEventBroadcaster 完成

public class LogEventBroadcaster {

    private final EventLoopGroup group;
private final Bootstrap bootstrap;
private final File file; public LogEventBroadcaster(InetSocketAddress address, File file) {
group = new NioEventLoopGroup();
bootstrap = new Bootstrap();
bootstrap.group(group).channel(NioDatagramChannel.class)
.option(ChannelOption.SO_BROADCAST, true)
.handler(new LogEventEncoder(address));
this.file = file;
} public void run() throws Exception {
// 绑定 Channel
Channel ch = bootstrap.bind(0).sync().channel();
long pointer = 0;
for (; ; ) {
long len = file.length();
if (len < pointer) {
// 将文件指针指向文件的最后一个字节
pointer = len;
} else if (len > pointer) {
RandomAccessFile raf = new RandomAccessFile(file, "r");
// 设置当前文件指针
raf.seek(pointer);
String line;
while ((line = raf.readLine()) != null) {
ch.writeAndFlush(new LogEvent(null, line, file.getAbsolutePath(), -1));
}
pointer = raf.getFilePointer();
raf.close();
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
Thread.interrupted();
break;
}
}
} public void stop() {
group.shutdownGracefully();
} public static void main(String[] args) throws Exception {
if (args.length != 2) {
throw new InterruptedException();
}
LogEventBroadcaster broadcaster = new LogEventBroadcaster(new InetSocketAddress("255.255.255.255",
Integer.parseInt(args[0])), new File(args[1]));
try {
broadcaster.run();
}
finally {
broadcaster.stop();
}
}
}

3. 编写监视器

编写一个称为 LogEventMonitor 的消费者程序,它的作用包括:

  • 接收由 LogEventBroadcaster 广播的 UDP DatagramPacket
  • 解码为 LogEvent 消息
  • 处理 LogEvent 消息

和之前一样,解码器 LogEventDecoder 负责将传入的 DatagramPacket 解码为 LogEvent 消息

public class LogEventDecoder extends MessageToMessageDecoder<DatagramPacket> {

    @Override
protected void decode(ChannelHandlerContext ctx, DatagramPacket datagramPacket, List<Object> out) throws Exception {
ByteBuf data = datagramPacket.content();
int idx = data.indexOf(0, data.readableBytes(), LogEvent.SEPARATOR);
String filename = data.slice(0, idx).toString(CharsetUtil.UTF_8);
String logMsg = data.slice(idx + 1, data.readableBytes()).toString(CharsetUtil.UTF_8);
LogEvent event = new LogEvent(datagramPacket.sender(), logMsg, filename, System.currentTimeMillis());
out.add(event);
}
}

创建一个处理 LogEvent 的 ChannelHandler

public class LogEventHandler extends SimpleChannelInboundHandler<LogEvent> {

    @Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
cause.printStackTrace();
ctx.close();
} @Override
protected void messageReceived(ChannelHandlerContext ctx, LogEvent event) throws Exception {
StringBuilder builder = new StringBuilder();
builder.append(event.getReceived());
builder.append("[");
builder.append(event.getSource().toString());
builder.append("] [");
builder.append(event.getLogfile());
builder.append("] : ");
builder.append(event.getMsg());
System.out.println(builder.toString());
}
}

现在需要将 LogEventDecoder 和 LogEventHandler 安装到 ChannelPipeline 中,下述代码展示了如何通过 LogEventMonitor 主类来做到这一点

public class LogEventMonitor {

    private final EventLoopGroup group;
private final Bootstrap bootstrap; public LogEventMonitor(InetSocketAddress address) {
group = new NioEventLoopGroup();
bootstrap = new Bootstrap();
bootstrap.group(group)
.channel(NioDatagramChannel.class)
.option(ChannelOption.SO_BROADCAST, true)
.handler(new ChannelInitializer<Channel>() { @Override
protected void initChannel(Channel ch) throws Exception {
ChannelPipeline pipeline = ch.pipeline();
pipeline.addLast(new LogEventDecoder());
pipeline.addLast(new LogEventHandler());
}
})
.localAddress(address);
} public Channel bind() {
return bootstrap.bind().syncUninterruptibly().channel();
} public void stop() {
group.shutdownGracefully();
} public static void main(String[] args) throws Exception {
if (args.length != 1) {
throw new IllegalArgumentException("Usage: LogEventMonitor <port>");
}
LogEventMonitor monitor = new LogEventMonitor(new InetSocketAddress(Integer.parseInt(args[0])));
try {
Channel channel = monitor.bind();
channel.closeFuture().sync();
}
finally {
monitor.stop();
}
}
}

Netty 框架学习 —— UDP 广播的更多相关文章

  1. Netty 框架学习 —— 引导

    概述 前面我们学习了 ChannelPipeline.ChannelHandler 和 EventLoop 之后,接下来的问题是:如何将它们组织起来,成为一个可实际运行的应用程序呢?答案是使用引导(B ...

  2. Netty 框架学习 —— 编解码器框架

    编解码器 每个网络应用程序都必须定义如何解析在两个节点之间来回传输的原始字节,以及如何将其和目标应用程序的数据格式做相互转换.这种转换逻辑由编解码器处理,编解码器由编码器和解码器组成,它们每种都可以将 ...

  3. Netty 框架学习 —— 第一个 Netty 应用

    概述 在本文,我们将编写一个基于 Netty 实现的客户端和服务端应用程序,相信通过学习该示例,一定能更全面的理解 Netty API 该图展示的是多个客户端同时连接到一台服务器.客户端建立一个连接后 ...

  4. Netty 框架学习 —— Netty 组件与设计

    Channel.EventLoop 和 ChannelFuture 这一节将对 Channel.EventLoop 和 ChannelFuture 类进行讨论,它们组合在一起,可以被认为是 Netty ...

  5. Netty 框架学习 —— EventLoop 和线程模型

    EventLoop 接口 Netty 是基于 Java NIO 的,因此 Channel 也有其生命周期,处理一个连接在其生命周期内发生的事件是所有网络框架的基本功能.通常来说,我们使用一个线程来处理 ...

  6. Netty 框架学习 —— 预置的 ChannelHandler 和编解码器

    Netty 为许多提供了许多预置的编解码器和处理器,几乎可以开箱即用,减少了在烦琐事务上话费的时间和精力 空闲的连接和超时 检测空闲连接以及超时对于释放资源来说至关重要,Netty 特地为它提供了几个 ...

  7. Netty 框架学习 —— 添加 WebSocket 支持

    WebSocket 简介 WebSocket 协议是完全重新设计的协议,旨在为 Web 上的双向数据传输问题提供一个切实可行的解决方案,使得客户端和服务器之间可以在任意时刻传输消息 Netty 对于 ...

  8. Netty 框架学习 —— 传输

    概述 流经网络的数据总是具有相同的类型:字节,这些字节如何传输主要取决于我们所说的网络传输.用户并不关心传输的细节,只在乎字节是否被可靠地发送和接收 如果使用 Java 网络编程,你会发现,某些时候当 ...

  9. Netty 框架学习 —— ByteBuf

    概述 网络数据的基本单位总是字节,Java NIO 提供了 ByteBuffer 作为它的字节容器,但这个类的使用过于复杂.Netty 的 ByteBuf 具有卓越的功能性和灵活性,可以作为 Byte ...

随机推荐

  1. 网速测试利器-iperf3

    网速测试利器-iperf3 使用工具   简介 iperf3是一个网络速度测试工具,支持IPv4与IPv6,支持TCP.UDP.SCTP传输协议,可在Windows.Mac OS X.Linux.Fr ...

  2. ]# dmesg | grep ATAcentos下查看网卡,主板,CPU,显卡,硬盘型号等硬件信息

    centos下查看网卡,主板,CPU,显卡,硬盘型号等硬件信息 osc_4o5tc4xq 2019/10/11 15:03 阅读数 253 centos下查看网卡,主板,CPU,显卡,硬盘型号等硬件信 ...

  3. 数据库权限grant

    数据库权限grant 创建授权grant 权限类型(priv_type) 权限类型 代表什么? ALL 所有权限 SELECT 读取内容的权限 INSERT 插入内容的权限 UPDATE 更新内容的权 ...

  4. Pod无法删除 强制删除pod

    多次变更服务后,发现部分pod delete僵死无法删除,一直处于Terminating状态 kubectl delete pod $pod-name一直卡住或不生效 已经删除管理资源的情况下发现仍然 ...

  5. 西门子 S7200 以太网模块连接力控组态方法

    产品简介:北京华科远创科技有限研发的远创智控ETH-YC模块,以太网通讯模块型号有MPI-ETH-YC01和PPI-ETH-YC01,适用于西门子S7-200/S7-300/S7-400.SMART ...

  6. 【SpringBoot基础系列】手把手实现国际化支持实例开发

    [SpringBoot基础系列]手把手实现国际化支持实例开发 国际化的支持,对于app开发的小伙伴来说应该比价常见了:作为java后端的小伙伴,一般来讲接触国际化的机会不太多,毕竟业务开展到海外的企业 ...

  7. Go语言实现Snowflake雪花算法

    转载请声明出处哦~,本篇文章发布于luozhiyun的博客:https://www.luozhiyun.com/archives/527 每次放长假的在家里的时候,总想找点简单的例子来看看实现原理,这 ...

  8. Linux基础服务——Bind DNS服务 Part2

    Linux基础服务--Bind DNS服务 Part2 DNS反向解析与区域传送 实验环境延续Part1的实验环境. 反向区域配置 正向解析是域名到IP地址的映射,反向解析则是IP地址到域名的解析,在 ...

  9. 手把手带你快速入门jQuery(视频|资料,建议收藏!)

    jQuery是什么? jQuery是一个快速.简洁的JavaScript框架,是继Prototype之后又一个优秀的JavaScript代码库(或JavaScript框架). jQuery设计的宗旨是 ...

  10. 点云配准的端到端深度神经网络:ICCV2019论文解读

    点云配准的端到端深度神经网络:ICCV2019论文解读 DeepVCP: An End-to-End Deep Neural Network for Point Cloud Registration ...