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. 攻防世界(七)ics-06

    攻防世界系列:ics-06 1.打开题目 很LiuPi的样子,根据题目提示点开报表中心(其他的点开没什么变化) 注意到URL中有?id=1,测试其他数值无果 3.抓包对id爆破 设置1-10000,爆 ...

  2. linux中级之HAProxy基础配置

    一.haproxy简介 HAProxy是一款提供高可用性.负载均衡以及基于TCP(第四层)和HTTP(第七层)应用的代理软件,HAProxy是完全免费的.借助HAProxy可以快速并且可靠的提供基于T ...

  3. CentOS 7 网络配置详解

    今天在一台PC上安装了CentOS 7,当时选择了最小安装模式,安装完成后马上用ifconfig查看本机的ip地址(局域网已经有DHCP),发现报错,提示ifconfig命令没找到. ? 1 2 3 ...

  4. jsp表单传值后在页面中文显示乱码

    主要通过添加以下代码来实现(前提是其他处的编码都设置成了utf-8): response.setContentType("text/html;charset=utf-8"); re ...

  5. kotlin中的嵌套类与内部类

    Java中的内部类和静态内部类在Java中内部类简言之就是在一个类的内部定义的另一个类.当然在如果这个内部类被static修饰符修饰,那就是一个静态内部类.关于内部类 和静态内部类除了修饰符的区别之外 ...

  6. 在Go语言项目中使用Zap日志库

    在Go语言项目中使用Zap日志库 本文先介绍了Go语言原生的日志库的使用,然后详细介绍了非常流行的Uber开源的zap日志库,同时介绍了如何搭配Lumberjack实现日志的切割和归档. 在Go语言项 ...

  7. 三维点云去噪无监督学习:ICCV2019论文分析

    三维点云去噪无监督学习:ICCV2019论文分析 Total Denoising: Unsupervised Learning of 3D Point Cloud Cleaning 论文链接: htt ...

  8. 在OpenShift平台上验证NVIDIA DGX系统的分布式多节点自动驾驶AI训练

    在OpenShift平台上验证NVIDIA DGX系统的分布式多节点自动驾驶AI训练 自动驾驶汽车的深度神经网络(DNN)开发是一项艰巨的工作.本文验证了DGX多节点,多GPU,分布式训练在DXC机器 ...

  9. CEVA引入新的可配置传感器集线器DSP架构

    CEVA引入新的可配置传感器集线器DSP架构 CEVA introduces new configurable sensor hub DSP architecture 在一个将多个传感器设计成几乎所有 ...

  10. springmvc——自定义类型转换器

    一.什么是springmvc类型转换器? 在我们的ssm框架中,前端传递过来的参数都是字符串,在controller层接收参数的时候springmvc能够帮我们将大部分字符串类型的参数自动转换为我们指 ...