netty中的UDP
UDP 提供了向多个接收者发送消息的额外传输模式:
- 多播——传输到一个预定义的主机组;
- 广播——传输到网络(或者子网)上的所有主机。
本示例应用程序将通过发送能够被同一个网络中的所有主机所接收的消息来演示 UDP 广播的使用。为此,我们将使用特殊的受限广播地址或者零网络地址 255.255.255.255。 发送到这个地址的消息都将会被定向给本地网络(0.0.0.0)上的所有主机,而不会被路由器 转发给其他的网络。所有的在该 UDP 端口上监听的事件监视器都将会接收到广播消息。
代码:
1、消息 POJO: LogEvent
package com.dxz.nettydemo.chapter13; import java.net.InetSocketAddress; 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;
} public static byte getSeparator() {
return SEPARATOR;
} public InetSocketAddress getSource() {
return source;
} public String getLogfile() {
return logfile;
} public String getMsg() {
return msg;
} public long getReceived() {
return received;
} public Object getReceivedTimestamp() {
return System.currentTimeMillis();
} }
2、编写广播者
Netty 的 DatagramPacket 是一个简单的消息容器,DatagramChannel 实现用它来和远程节点通信。类似于在我们先前的类比中的明信片,它包含了接收者(和可选的发送者)的地址 以及消息的有效负载本身。 要将 LogEvent 消息转换为 DatagramPacket,我们将需要一个编码器。但是没有必要从 头开始编写我们自己的。我们将扩展 Netty 的 MessageToMessageEncoder:
编码:
package com.dxz.nettydemo.chapter13; import java.net.InetSocketAddress;
import java.util.List; 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; public class LogEventEncoder extends MessageToMessageEncoder<LogEvent> {
private final InetSocketAddress remoteAddress; 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);
buf.writeBytes(file);
buf.writeByte(LogEvent.SEPARATOR);
buf.writeBytes(msg);
out.add(new DatagramPacket(buf, remoteAddress));
}
}
在 LogEventEncoder 被实现之后,我们已经准备好了引导该服务器,其包括设置各种各 样的 ChannelOption,以及在 ChannelPipeline 中安装所需要的 ChannelHandler。这将通过主类 LogEventBroadcaster 完成,如下代码清单:
package com.dxz.nettydemo.chapter13; import java.io.File;
import java.io.RandomAccessFile;
import java.net.InetSocketAddress; import io.netty.bootstrap.Bootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioDatagramChannel; 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 ch = bootstrap.bind(0).sync().channel();
long pointer = 0;
System.out.println("run() ch:" + ch);
for (;;) {
long len = file.length();
if (len < pointer) {
// file was reset
pointer = len;
} else if (len > pointer) {
System.out.println("read begin " + pointer);
// Content was added
RandomAccessFile raf = new RandomAccessFile(file, "r");
raf.seek(pointer);
String line;
while ((line = raf.readLine()) != null) {
ch.writeAndFlush(new LogEvent(null, -1, file.getAbsolutePath(), line));
}
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 IllegalArgumentException();
}*/
String port = "8888";
String path = "E:\\myspace\\netty-demo\\src\\main\\java\\com\\dxz\\nettydemo\\chapter13\\log.txt";
LogEventBroadcaster broadcaster = new LogEventBroadcaster(
//new InetSocketAddress("192.168.5.12",
//new InetSocketAddress("localhost",
new InetSocketAddress("255.255.255.255",
Integer.parseInt(port)), new File(path));
try {
broadcaster.run();
} finally {
broadcaster.stop();
}
}
}
图 13-3 呈现了该 LogEventBroadcaster 的 ChannelPipeline 的一个高级别视图,展 示了 LogEvent 消息是如何流经它的。
正如你所看到的,所有的将要被传输的数据都被封装在了 LogEvent 消息中。LogEventBroadcaster 将把这些写入到 Channel 中,并通过 ChannelPipeline 发送它们,在那里它 们将会被转换(编码)为 DatagramPacket 消息。最后,他们都将通过 UDP 被广播,并由远 程节点(监视器)所捕获。
通过nc监控指定的8888端口如下:
然后运行LogEventBroadcaster类,再观察nc的日志如下:
编写监视器
我们的目标是将 netcat 替换为一个更加完整的事件消费者,我们称之为 LogEventMonitor。 这个程序将:
(1)接收由 LogEventBroadcaster 广播的 UDP DatagramPacket;
(2)将它们解码为 LogEvent 消息;
(3)将 LogEvent 消息写出到 System.out。
解码
ChannelPipeline 中的第一个解码器LogEventDecoder 负责将传入的DatagramPacket 解码为 LogEvent 消息ChannelPipeline 中的第一个解码器LogEventDecoder 负责将传入的DatagramPacket 解码为 LogEvent 消息:
package com.dxz.nettydemo.chapter13; import java.util.List; import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.socket.DatagramPacket;
import io.netty.handler.codec.MessageToMessageDecoder;
import io.netty.util.CharsetUtil; 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(), System.currentTimeMillis(), filename, logMsg);
out.add(event);
}
}
第二个 ChannelHandler 的工作是对第一个 ChannelHandler 所创建的 LogEvent 消息执行一些处理:
LogEventHandler 将以一种简单易读的格式打印 LogEvent 消息,包括以下的各项:
以毫秒为单位的被接收的时间戳;
发送方的 InetSocketAddress,其由 IP 地址和端口组成;
生成 LogEvent 消息的日志文件的绝对路径名;
实际上的日志消息,其代表日志文件中的一行。
package com.dxz.nettydemo.chapter13; import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler; public class LogEventHandler extends SimpleChannelInboundHandler<LogEvent> {
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
cause.printStackTrace();
ctx.close();
} @Override
public void messageReceived(ChannelHandlerContext ctx, LogEvent event) throws Exception {
StringBuilder builder = new StringBuilder();
builder.append(event.getReceivedTimestamp());
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 主类来做到这一点:
package com.dxz.nettydemo.chapter13; import java.net.InetSocketAddress; import io.netty.bootstrap.Bootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioDatagramChannel; 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 channel) throws Exception {
ChannelPipeline pipeline = channel.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>");
}*/
String port = "8888";
LogEventMonitor monitor = new LogEventMonitor(new InetSocketAddress(Integer.parseInt(port)));
try {
Channel channel = monitor.bind();
System.out.println("LogEventMonitor running");
channel.closeFuture().sync();
} finally {
monitor.stop();
}
}
}
测试:
新修改下广播的地址为localhost,启动发送端和监听端程序,往log.txt文件中增加“fffffffffffff”保存后,如下图:
netty中的UDP的更多相关文章
- Netty中的连接管理
连接管理是我们首先需要关注的,检测空闲连接以及超时对于及时释放资源来说是至关重要的.由于这是一项常见的任务,Netty特地为它提供了几个ChannelHandler实现. 用于空闲连接以及超时的Cha ...
- Linux下Netty实现高性能UDP服务(SO_REUSEPORT)
参考: https://www.jianshu.com/p/61df929aa98b SO_REUSEPORT学习笔记:http://www.blogjava.net/yongboy/archive/ ...
- Netty中的基本组件及关系
原文:https://blog.csdn.net/summerZBH123/article/details/79344226--------------------- 概述 这篇文章主要是用来 ...
- Netty(六):Netty中的连接管理(心跳机制和定时断线重连)
何为心跳 顾名思义, 所谓心跳, 即在TCP长连接中, 客户端和服务器之间定期发送的一种特殊的数据包, 通知对方自己还在线, 以确保 TCP 连接的有效性. 为什么需要心跳 因为网络的不可靠性, 有可 ...
- netty中的引导Bootstrap服务端
引导一个应用程序是指对它进行配置,并使它运行起来的过程. 一.Bootstrap 类 引导类的层次结构包括一个抽象的父类和两个具体的引导子类,如图 8-1 所示 服务器致力于使用一个父 Channel ...
- netty中的引导Bootstrap客户端
一.Bootstrap Bootstrap 是 Netty 提供的一个便利的工厂类, 我们可以通过它来完成 Netty 的客户端或服务器端的 Netty 初始化.下面我以 Netty 源码例子中的 E ...
- netty中的发动机--EventLoop及其实现类NioEventLoop的源码分析
EventLoop 在之前介绍Bootstrap的初始化以及启动过程时,我们多次接触了NioEventLoopGroup这个类,关于这个类的理解,还需要了解netty的线程模型.NioEventLoo ...
- Netty 中的心跳机制
在TCP长连接或者WebSocket长连接中一般我们都会使用心跳机制–即发送特殊的数据包来通告对方自己的业务还没有办完,不要关闭链接. 网络的传输是不可靠的,当我们发起一个链接请求的过程之中会发生什么 ...
- netty系列之:Bootstrap,ServerBootstrap和netty中的实现
目录 简介 Bootstrap和ServerBootstrap的联系 AbstractBootstrap Bootstrap和ServerBootstrap 总结 简介 虽然netty很强大,但是使用 ...
随机推荐
- Maven的生命周期阶段
一.Maven的生命周期 Maven的生命周期就是对所有的构建过程进行抽象和统一.包含了项目的清理.初始化.编译.测试.打包.集成测试.验证.部署和站点生成等几乎所有的构建步骤. Maven的生命周期 ...
- shell awk命令
语法: awk '{command}' filename 多个命令以分号分隔. awk 'BEGIN {command1} {command2} END{command3}' 注意:BEGIN , ...
- base64编码的 文件 图片
//图片 转为 base64编码的文本 private void button1_Click(object sender, EventArgs e) { OpenFileDialog dlg = ne ...
- C++纯虚函数和抽象类的一些要点
1. 纯虚函数是在其被声明的类中不被实现的函数. 2. 定义了纯虚函数的类是抽象类,可以用来生命变量,但不能用来构造实例. 3. 基类中定义了纯虚函数,派生类要么定义这个纯虚函数,要么重复声明一次这个 ...
- react 总结
1.React 里直接修改 this.state 和调用 setState() 修改 state 的值有什么区别? 使用对this.state赋值并没有什么作用,官方提醒,应该把this.state当 ...
- 更新增加一个门店ID字段的值
MYSQL因为不能查询一张表时同时更新一张表,同时又会有子查询大于等于一条的情况出现. 分两种情况: 1 直接JOIN 得到一张表. 然后导出做筛选 CREATE TABLE TEST SELECT ...
- Ajax中的XMLHttpRequest对象详解(转)
XMLHttpRequest对象是Ajax技术的核心.在Internet Explorer 5中,XMLHttpRequest对象以ActiveX对象引入,被称之为XMLHTTP,它是一种支持异步请求 ...
- LeetCode OJ:Add Two Numbers (相加链表之数)
You are given two linked lists representing two non-negative numbers. The digits are stored in rever ...
- mac root
我在mac下,用su登录root用户 但是提醒 su:Sorry 然后按照网上的方法弄了好多次,都没成功,说是要修改/etc/group 文件中,wheel后添加自己的用户名,用逗号分隔多个用户, ...
- 你所不知道的,Java 中操作符的秘密?
在 Java 编程的过程中,我们对数据的处理,都是通过操作符来实现的.例如,用于赋值的赋值操作符.用于运算的运算操作符等.用于比较的比较操作符,还包括逻辑操作符.按位操作符.移位操作符.三元操作符等等 ...