一、前言

  前面学习了WebSocket协议,并且通过示例讲解了WebSocket的具体使用,接着学习如何使用无连接的UDP来广播事件。

二、UDP广播事件

  2.1 UDP基础

  面向连接的TCP协议管理端到端的连接,在连接生命周期中,发送的消息会有序并且可靠地进行传输,最后连接有序地终止。然而,在无连接协议(如UDP)中,没有持久连接的概念,每个消息(UDP数据报)都是独立的传输,此外,UDP没有TCP的纠错机制(即每个对等体会确认其接收到的分组,并且发送者会重传未确认的分组)。

  UDP的限制比TCP多,但是比TCP快很多,这是因为消除了握手和消息管理的所有开销,UDP非常适合处理或容忍消息丢失的应用。

  2.2 UDP广播

  迄今为止所有的示例都使用了单播的传输模式,其被定义为将消息发送到由唯一地址标识的单个网络目的地,有连接和无连接的协议都支持这种模式,UDP为多个收件人发送消息提供了额外的传输模式:

    · 组播--传输到定义的主机组。

    · 广播--传输到网络(或子网)上的所有主机。

  本章中的示例将通过发送在同一网络上的所有主机接收的消息来使用UDP广播。

  2.3 UDP简单示例

  示例将打开一个文件,并通过UDP将每一行广播为指定端口。下图展示了应用的结构图。

  

  2.4 LogEvent POJO

  在消息应用中,消息经常以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;
}
public InetSocketAddress getSource() {
return source;
}
public String getLogfile() {
return logfile;
}
public String getMsg() {
return msg;
}
public long getReceivedTimestamp() {
return received;
}
}

  2.5 编写broadcaster

  Netty提供了许多类来支持UDP应用程序,如Netty的DatagramPacket是DatagramChannel实现与远程对等体进行通信的简单消息容器,我们需要一个编码器将EventLog消息转换为DatagramPackets,可以扩展Netty的MessageToMessageEncoder,LogEventEncoder的代码如下。  

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));
}
}

  完成编码器后,即可以开始启动服务端,其中服务端LogEventBroadcaster的代码如下。  

public class LogEventBroadcaster {
private final Bootstrap bootstrap;
private final File file;
private final EventLoopGroup group; 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 IOException {
Channel ch = bootstrap.bind(0).syncUninterruptibly().channel();
System.out.println("LogEventBroadcaster running");
long pointer = 0;
for (;;) {
long len = file.length();
if (len < pointer) {
// file was reset
pointer = len;
} else if (len > 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();
} LogEventBroadcaster broadcaster = new LogEventBroadcaster(new InetSocketAddress("255.255.255.255",
Integer.parseInt(args[0])), new File(args[1]));
try {
broadcaster.run();
} finally {
broadcaster.stop();
}
}
}

  2.6 编写monitor

  在应用中

    · 接收由LogEventBroadcaster广播的UDP DatagramPackets。

    · 将其解码为LogEvent。

    · 将LogEvent写入输出流System.out。

  下图展示LogEvent的流动。

  

  LogEventDecoder负责将传入的DatagramPackets解码为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 i = data.indexOf(0, data.readableBytes(), LogEvent.SEPARATOR);
String filename = data.slice(0, i).toString(CharsetUtil.UTF_8);
String logMsg = data.slice(i + 1, data.readableBytes()).toString(CharsetUtil.UTF_8); LogEvent event = new LogEvent(datagramPacket.recipient(), System.currentTimeMillis(),
filename,logMsg);
out.add(event);
}
}

  而LogEventHandler用于处理LogEvent,其代码如下。  

public class LogEventHandler extends SimpleChannelInboundHandler<LogEvent> {

    @Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
cause.printStackTrace();
ctx.close();
} @Override
public void channelRead0(ChannelHandlerContext channelHandlerContext, 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());
}
}

  LogEventMonitor用于将处理器添加至管道中,其代码如下。  

public class LogEventMonitor {

    private final Bootstrap bootstrap;
private final EventLoopGroup group;
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>");
}
LogEventMonitor monitor = new LogEventMonitor(new InetSocketAddress(Integer.parseInt(args[0])));
try {
Channel channel = monitor.bind();
System.out.println("LogEventMonitor running"); channel.closeFuture().await();
} finally {
monitor.stop();
}
}
}

  运行LogEventBroadcaster和LogEventMonitor

三、总结

  本篇博文讲解了UDP协议,以及其示例,在实际应用中需要根据不同的应用场景选择不同的协议,谢谢各位园友的观看~

【Netty】UDP广播事件的更多相关文章

  1. Netty学习摘记 —— UDP广播事件

    本文参考 本篇文章是对<Netty In Action>一书第十三章"使用UDP广播事件"的学习摘记,主要内容为广播应用程序的开发 消息POJO 我们将日志信息封装成名 ...

  2. Netty实战十三之使用UDP广播事件

    1.UDP的基础知识 我们将会把重点放在一个无连接协议即用户数据报协议(UDP)上,它通常用在性能至关重要并且能够容忍一定的数据报丢失的情况下. 面向连接的传输(如TCP)管理了两个网络端点之间的连接 ...

  3. Netty 框架学习 —— UDP 广播

    UDP 广播 面向连接的传输(如 TCP)管理两个网络端点之间的连接的建立,在连接的生命周期的有序和可靠的消息传输,以及最后,连接的有序终止.相比之下,类似 UDP 的无连接协议中则没有持久化连接的概 ...

  4. 利用Docker技术实现UDP广播效果(网络编程python版)

    docker的安装见官方文档 我使用的系统为Ubuntu16.04 Ubuntu系统安装docker文档地址:https://docs.docker.com/engine/installation/l ...

  5. 给Hi3518e的Uboot添加UDP广播收发功能

    基于个人兴趣,决定实现一个和方案公司提供的uboot收发广播的功能.记录笔记如下. SDK版本:Hi3518E_V100R001C01SPC081 1. 由于我手头的板子的Phy是RMII模式,因此先 ...

  6. [蓝牙] 2、蓝牙BLE协议及架构浅析&&基于广播超时待机说广播事件

    第一章 BLE基本概念了解 一.蓝牙4.0和BLE区别   蓝牙4.0是一种应用非常广泛.基于2.4G射频的低功耗无线通讯技术.蓝牙低功耗(Bluetooth Low Energy ),人们又常称之为 ...

  7. NetworkComms V3 之发送UDP广播消息

    NetworkComms网络通信框架序言 NetworkComms通信框架,是一款来自英国的c#语言编写的通信框架,历时6年研发,成熟稳定,性能可靠. NetworkComms v3目前只支持基本的U ...

  8. Android杂谈--HTC等手机接收不到UDP广播报文的解决方案

    最近遇到个问题,在android手机上发送UDP报文的时候,HTC等机型(测试用HTC new one)接收不到广播报文,而其他的samsung, huawei, xiaomi, nexus等等均没有 ...

  9. [C# 网络编程系列]专题七:UDP编程补充——UDP广播程序的实现

    转自:http://www.cnblogs.com/zhili/archive/2012/09/03/2666974.html 上次因为时间的关系,所以把上一个专题遗留下的一个问题在本专题中和大家分享 ...

随机推荐

  1. mysql常用技能分享

    一,MySQL查询的五种子句: 1,where(条件查询),常用的运算符: ①比较运算符 > , < , = , != , >= , <= , in( ) , between ...

  2. 网络语音视频技术浅议(附多个demo源码下载)

    我们在开发实践中常常会涉及到网络语音视频技术.诸如即时通讯.视频会议.远程医疗.远程教育.网络监控等等,这些网络多媒体应用系统都离不开网络语音视频技术.本人才疏学浅,对于网络语音视频技术也仅仅是略知皮 ...

  3. 使用Maven构建SSH

    本人自己进行的SSH整合,中间遇到不少问题,特此做些总结,仅供参考. 项目环境: struts-2.3.31 + spring-4.3.7 + hibernate-4.2.21 + maven-3.3 ...

  4. Google Code Jam 2016 Round 1B Problem C. Technobabble

    题目链接:https://code.google.com/codejam/contest/11254486/dashboard#s=p2 大意是教授的学生每个人在纸条上写一个自己的topic,每个to ...

  5. js日期转化(计算一周的日期)

    之前做项目的时候遇到过一个日期转化的问题,一个日期控件和近一天,近七天和近一月的的联动效果.发现自己不会,后来就百度了一下解决了这个问题. 现在抽空又写了一个时间转化的案例(计算一周的日期),因为之前 ...

  6. 尚学堂Java第一课

    今天是北京尚学堂郑州分校开课的第一天,小班面授教学. 我很兴奋,是一个小白对IT大神渴求传道授业解惑的兴奋. 杨老师还是一贯耐心详细的手敲了第一堂课的完整大纲,必须给杨老师赞赞赞!!! 从幽默的制定班 ...

  7. 在linux系统中跟踪高IO等待

    原文作者:Jon Buys 原文地址:http://ostatic.com/blog/tracking-down-high-io-wait-in-linux 译者:Younger Liu,本作品采用知 ...

  8. 【lucene系列学习三】用socke把lucene做成一个web服务并实现多线程

    首先,参考http://www.cnblogs.com/itlqs/p/6104672.html和http://www.cnblogs.com/itlqs/p/6079301.html 然后,Loca ...

  9. 移动前端常用meta标签

    移动前端常用meta标签 移动前端head常用meta标签 原文链接:http://caibaojian.com/mobile-meta.html //code from http://caibaoj ...

  10. python3 selenium 随机选择同一类型下的某一个元素

    使用场景: 如上图所示,有时候,我们测试的时候,不会每个方向都选择一遍,也不能每次都选择一个方向,这个时候就需要每次运行用例的时候,随机选择一个方向来测试 使用方法: random.randint() ...