Netty学习摘记 —— UDP广播事件
本文参考
本篇文章是对《Netty In Action》一书第十三章"使用UDP广播事件"的学习摘记,主要内容为广播应用程序的开发
消息POJO
我们将日志信息封装成名为LogEvent的POJO
public final 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, -1, logfile, msg);
}
//用于传入消息的构造函数
public LogEvent(InetSocketAddress source, long received, String logfile, String msg) {
this.source = source;
this.logfile = logfile;
this.msg = msg;
this.received = received;
}
//返回发送 LogEvent 的源的 InetSocketAddress
public InetSocketAddress getSource() {
return source;
}
//返回所发送的 LogEvent 的日志文件的名称
public String getLogfile() {
return logfile;
}
//返回消息内容
public String getMsg() {
return msg;
}
//返回接收 LogEvent 的时间
public long getReceivedTimestamp() {
return received;
}
}
编写广播者
Netty 提供了大量的类来支持 UDP 应用程序的编写

我们使用较多的是DatagramPacket和NioDatagramChannel
Netty 的DatagramPacket用于和远端的UDP通信,是一个简单的消息容器,可以包装消息,接收方地址和发送方地址,我们可以将LogEvent消息转换为DatagramPacket后进行发送(需要扩展 Netty 的 MessageToMessageEncoder)
The message container that is used for DatagramChannel to communicate with the remote peer.
Netty的NioDatagramChannel用来实现发送和接收UDP消息
An NIO datagram Channel that sends and receives an AddressedEnvelope .
下图展示了广播方向的LogEvent消息流向

下面是编码器的实现代码
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() + "\n").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));
}
}
最后对它进行引导,每秒从文件中读取一行数据,并且读完整个文件后又会从文件头开始重新读取数据
命令行参数的第一个参数是远程主机接收消息的端口号,第二个参数是广播者本地文件路径
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();
//引导该 NioDatagramChannel(无连接的)
bootstrap.group(group).channel(NioDatagramChannel.class)
//设置 SO_BROADCAST 套接字选项
.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;
long len = file.length();
//启动主处理循环
while (true){
if (len <= pointer) {
// file was reset
pointer = 0;
} else {
// Content was added
RandomAccessFile raf = new RandomAccessFile(file, "r");
//设置当前的文件指针,以确保没有任何的旧日志被发送
raf.seek(pointer);
String line;
while ((line = raf.readLine()) != null) {
//对于每个日志条目,写入一个 LogEvent 到 Channel 中
ch.writeAndFlush(new LogEvent(null, -1, file.getAbsolutePath(), line));
try {
//休眠 1 秒,如果被中断,则退出循环;否则重新处理它
Thread.sleep(1000);
} catch (InterruptedException e) {
Thread.interrupted();
break;
}
}
//存储其在文件中的当前位置
pointer = raf.getFilePointer();
raf.close();
}
}
}
public void stop() {
group.shutdownGracefully();
}
public static void main(String[] args) throws Exception {
if (args.length != 2) {
throw new IllegalArgumentException();
}
//创建并启动一个新的 LogEventBroadcaster 的实例
LogEventBroadcaster broadcaster = new LogEventBroadcaster(
new InetSocketAddress("255.255.255.255",
Integer.parseInt(args[0])), new File(args[1]));
try {
broadcaster.run();
}
finally {
broadcaster.stop();
}
}
}
我们注意到广播者绑定到了一个为0的端口号,因为UDP是无连接的协议,所以只需要为广播者分配一个临时的端口来发送消息,参数0代表绑定一个临时端口
A port number of zero will let the system pick up an ephemeral port in a bind operation.
使用netcat进行测试

Netty学习摘记 —— UDP广播事件的更多相关文章
- 【Netty】UDP广播事件
一.前言 前面学习了WebSocket协议,并且通过示例讲解了WebSocket的具体使用,接着学习如何使用无连接的UDP来广播事件. 二.UDP广播事件 2.1 UDP基础 面向连接的TCP协议管理 ...
- Netty学习摘记 —— 初步认识Netty核心组件
本文参考 我在博客内关于"Netty学习摘记"的系列文章主要是对<Netty in action>一书的学习摘记,文章中的代码也大多来自此书的github仓库,加上了一 ...
- Netty实战十三之使用UDP广播事件
1.UDP的基础知识 我们将会把重点放在一个无连接协议即用户数据报协议(UDP)上,它通常用在性能至关重要并且能够容忍一定的数据报丢失的情况下. 面向连接的传输(如TCP)管理了两个网络端点之间的连接 ...
- Netty学习摘记 —— 简单WEB聊天室开发
本文参考 本篇文章是对<Netty In Action>一书第十二章"WebSocket"的学习摘记,主要内容为开发一个基于广播的WEB聊天室 聊天室工作过程 请求的 ...
- Netty学习摘记 —— 再谈引导
本文参考 本篇文章是对<Netty In Action>一书第八章"引导"的学习摘记,主要内容为引导客户端和服务端.从channel内引导客户端.添加ChannelHa ...
- Netty学习摘记 —— 心跳机制 / 基于分隔符和长度的协议
本文参考 本篇文章是对<Netty In Action>一书第十一章"预置的ChannelHandler和编解码器"的学习摘记,主要内容为通过 SSL/TLS 保护 N ...
- Netty学习摘记 —— 预置SSL / HTTP / WebSocket编解码器
本文参考 本篇文章是对<Netty In Action>一书第十一章"预置的ChannelHandler和编解码器"的学习摘记,主要内容为通过 SSL/TLS 保护 N ...
- Netty学习摘记 —— 初识编解码器
本文参考 本篇文章是对<Netty In Action>一书第十章"编解码器框架"的学习摘记,主要内容为解码器和编码器 编解码器实际上是一种特殊的ChannelHand ...
- Netty学习摘记 —— 再谈EventLoop 和线程模型
本文参考 本篇文章是对<Netty In Action>一书第七章"EventLoop和线程模型"的学习摘记,主要内容为线程模型的概述.事件循环的概念和实现.任务调度和 ...
随机推荐
- springboot页面国际化
引入依赖pom.xml <dependency> <groupId>org.springframework.boot</groupId> <artifactI ...
- 数据平滑处理-均值|中值|Savitzky-Golay滤波器
均值滤波器 均值滤波器是一种使用频次较高的线性滤波器.它的实现原理很简单,就是指定一个长度大小为奇数的窗口,使用窗口中所有数据的平均值来替换中间位置的值,然后平移该窗口,平移步长为 1,继续重复上述操 ...
- Winfrom中关于toolStrip工具栏中按钮背景的设置
在ToolStrip中可以存放很多种控件,一般来说,主要使用的是Button和DropDownButton,通常情况下,Button需要设置图片和文字,网上找了很多个方法都不太靠谱,自己试验了下,下面 ...
- win7下安装docker toolbox
我的笔记本是win10系统,上面安装docker非常简单.只要在官网下载然后一路next就可以了.然而办公室的电脑装的是win7,我也懒得给它换win10.没想到,win7下安装docker这么费劲. ...
- selenium+python自动化103-一闪而过的dialog如何定位
前言 web页面操作的时候经常会遇到一闪而过的 dialog 消息,这些提示语一般只出现了几秒,过后元素节点就会在DOM中消失了. 本篇讲解下用chrome 浏览器如何定位一闪而过的 dialog 消 ...
- List分组和排序
1 //分组 2 var ll= lst.GroupBy(x => new { x.Id, x.Name }).Select(x => new 3 { 4 Key = x.Key, 5 I ...
- C#集合,字典的运用
三个题解释所有 using System;using System.Collections.Generic;using System.Linq;using System.Text;using Syst ...
- VirtualBox虚拟机--安装新系统
问题概述:在VirtualBox虚拟机中使用ISO镜像文件安装新系统. 继在本机系统上学习Python把系统搞坏掉以后,总结教训试新东西还是先在虚拟机上练手. 找同事拷了一个win10系统的ISO镜像 ...
- jquery, js轮播图插件Swiper3
轮播图插件Swiper3 HTML代码 如果只是简单的使用轮播图,直接复制我的html代码就可以. 如果想要高级一些,就自己去看文档吧 <!DOCTYPE html> <html l ...
- C++ STL vector扩容原理分析
扩容特点: 1)新增元素:vector通过一个连续的数组存放元素,如果集合已满,在新增数据的时候,就要分配一块更大的内存,将原来的数据复制过来,释放之前的内存,在插入新增的元素: 2)对vector的 ...