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. 攻防世界(二)Training-WWW-Robots

    攻防世界系列:Training-WWW-Robots 1.查看robots.txt的要求  补充: 什么是robots.txt协议? Robots.txt是放在网站根目录下的一个文件,也是搜索引擎在网 ...

  2. C++的构造函数为何不能为虚函数

    1. 存储空间角度:虚函数对应一个vtable,vtable存储于对象的内存空间 若构造函数是虚的,则需要通过 vtable来调用,若对象还未实例化,即内存空间还没有,无法找到vtable 2. 使用 ...

  3. AJAX 原理与使用

    AJAX 是什么 AJAX ( Asynchronous JavaScript and XML,异步 JavaScript 和 XML,中文读音:"阿贾克斯 ") 是一种 Web ...

  4. Centos7.4 docker安装包下载以及离线安装

    docker安装包下载地址:https://download.docker.com/linux/centos/7/x86_64/stable/Packages/ 需要下载一个selinux包:dock ...

  5. ubuntu 14.04安装mysql-python

    网上看到的是想安装mysql-python都得安装mysql本身,可是我就不想安装这个数据库,而是用于连接到别的服务器上的mysql,所以下面就是安装过程: 1. 直接运行: pip install ...

  6. 回顾Servlet

    1.新建一个Maven工程当做父工程!pom依赖! <!-- 依赖 --> <dependencies> <dependency> <groupId>j ...

  7. MindSpore Lite整体架构介绍

    MindSpore Lite整体架构介绍 MindSpore Lite框架的总体架构如下所示: 前端(Frontend): 负责模型生成,用户可以通过模型构建接口构建模型,将第三方模型和MindSpo ...

  8. NVIDIA空中导航SDK改造5G通信

    NVIDIA空中导航SDK改造5G通信 Transforming Next-Generation Wireless with 5T for 5G and the NVIDIA Aerial SDK N ...

  9. java后端知识点梳理——Spring

    开篇:感谢我是祖国的花朵,java3y,三太子敖丙等优秀博主!他们的文章为我学习java提供了莫大的帮助,膜拜大神! Spring的优点有哪些呢? Spring的依赖注入将对象之间的依赖关系交给了框架 ...

  10. springboot实现自定义拦截器

    为了更容易理解,我们通过一个代码例子来演示. 例子: 我们现在要访问http://localhost:8080/main.html页面,这个页面需要登录之后才能够浏览,没登录不能浏览. 那么现在问题来 ...