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很强大,但是使用 ...
随机推荐
- 使用springmvc报错Required int parameter 'age' is not present
仔细检查jsp代码 <a href="springmvc/testRequestParam?username=atguigu$age=11">Test RequestP ...
- 使用springmvc时报错JSPs only permit GET POST or HEAD
两个地方需要注意:第一处在web.xml文件中不要忘记配置 <filter> <filter-name>HiddenHttpMethodFilter</filter-na ...
- 浅谈 session 会话的原理
先谈 cookie 网络传输基于的Http协议,是无状态的协议,即每次连接断开后再去连接,服务器是无法判断此次连接的客户端是谁. 如果每次数据传输都需要进行连接和断开,那造成的开销是很巨大的. 为了解 ...
- 负载均衡之DNS域名解析
转载请说明出处:http://blog.csdn.net/cywosp/article/details/38017027 在上一篇文章(http://blog.csdn.net/cywosp/arti ...
- Angular路由的定义和使用
一.什么是routing(路由) Almost all non-trivial, non-demo Single Page App (SPA) require multiple pages. A se ...
- PetaPoco入门
(转自:http://www.cnblogs.com/tinyhu/archive/2013/06/02/3113652.html) 1. ORM概括 1.1. ORM简介 ORM 对象-关系映射(O ...
- MongoCola使用教程 1 - MongoDB的基本操作和聚合功能---Mongdb客户端软件操作说明
前言 在开始正文之前,感谢博客园的Nosql爱好者对于MongoCola工具的试用(使用).特别感谢 呆呆 这位朋友的Bug报告,让我纠正了一个很严重的Bug.同时也感谢以前的多个网友在博客留言中给我 ...
- MySQL Multi-Range Read
MySQL 5.6版本提供了很多性能优化的特性,其中之一就是 Multi-Range Read 多范围读(MRR) , 它的作用针对基于辅助/第二索引的查询,减少随机IO,并且将随机IO转化为顺序IO ...
- 记vue+leaflet的一次canvas渲染爆栈
背景: 在地图上绘制大量的circleMarker,leaflet能选择使用canvas来渲染,比起默认的svg渲染来说在大量绘制的情况下会更加流畅.但当触发其中某一个circleMarker的too ...
- Maven项目中java类报错-Cannot resolve symbol
电脑蓝屏了,强制重启之后再打开IDEA里面的项目,所有Java类文件都在报Cannot resolve symbo错误,可以确定所有依赖的包都有引用且jar包没有冲突. 经查询找到这个解决方法: 在I ...