一、基础知识

UDP 协议相较于 TCP 协议的特点:

1、无连接协议,没有持久化连接;
2、每个 UDP 数据报都是一个单独的传输单元;
3、一定的数据报丢失;
4、没有重传机制,也不管数据报是否可达;
5、速度比TCP快很多,可用来高效处理大量数据 —— 牺牲了握手以及消息管理机制。
6、常用于音频、视频场景,可以忍受一定的数据包丢失,追求速度上的提升。

TCP 协议采用的是一种叫做单播的传输形式,UDP 协议提供了向多个接收者发送消息的额外传输形式(多播、广播):

单播(TCP 和 UDP):发送消息给一个由唯一的地址所标识的单一的网络目的地。
多播(UDP):传输给一个预定义的主机组。
广播(UDP):传输到网络(或者子网)上的所有主机。

二、功能说明

广播方:打开一个文件,通过 UDP 使用特殊的受限广播地址或者零网络地址 255.255.255.255,把每一行作为一个消息广播到一个指定的端口。

接收方:通过 UDP 广播,只需简单地通过在指定的端口上启动一个监听程序,便可以创建一个事件监视器来接收日志消息。所有的在该 UDP 端口上监听的事件监听器都将会接收到广播信息。

三、实现

下图展示了怎么将我们的 文件数据 广播为 UDP消息:所有的将要被传输的数据都被封装在了 LogEvent 消息中。 LogEventBroadcaster 将把这些写入到 Channel 中,并通过 ChannelPipeline 发送它们,在那里它们将会被转换(编码)为 DatagramPacket 消息。最后,他们都将通过 UDP 被广播,并由远程节点(监视器)所捕获。

Netty 中支持 UDP 协议主要通过以下相关类:

DatagramPacket:使用 ByteBuf 作为数据源,是 UDP 协议传输的消息容器。

DatagramChannel:扩展了 Netty 的 Channel 抽象以支持 UDP 的多播组管理,它的实现类 NioDatagramChannnel 用来和远程节点通信。

Bootstrap:UDP 协议的引导类,使用 bind() 方法绑定 Channel。

public class LogEvent {
public static final byte SEPARATOR = ':';
/**
* IP套接字地址(IP地址+端口号)
*/
private final InetSocketAddress inetSocketAddress;
/**
* 文件名
*/
private final String logfile;
/**
* 消息内容
*/
private final String msg; private final long received; /**
* 用于传入消息的构造函数
*
* @param inetSocketAddress
* @param logfile
* @param msg
* @param received
*/
public LogEvent(InetSocketAddress inetSocketAddress, String logfile, String msg, long received) {
this.inetSocketAddress = inetSocketAddress;
this.logfile = logfile;
this.msg = msg;
this.received = received;
} /**
* 用于传出消息的构造函数
*
* @param logfile
* @param msg
*/
public LogEvent(String logfile, String msg) {
this(null, logfile, msg, -1);
} public InetSocketAddress getInetSocketAddress() {
return inetSocketAddress;
} public String getLogfile() {
return logfile;
} public String getMsg() {
return msg;
} public long getReceived() {
return received;
}
}

文件实体类 LogEvent.java

public class LogEventEncoder extends MessageToMessageEncoder<LogEvent> {
private final InetSocketAddress remoteAddress; public LogEventEncoder(InetSocketAddress remoteAddress) {
this.remoteAddress = remoteAddress;
} @Override
protected void encode(ChannelHandlerContext ctx, LogEvent msg, List<Object> out) throws Exception {
byte[] file = msg.getLogfile().getBytes(CharsetUtil.UTF_8);
byte[] content = msg.getMsg().getBytes(CharsetUtil.UTF_8);
ByteBuf byteBuf = ctx.alloc().buffer(file.length + content.length + 1);
byteBuf.writeBytes(file);
byteBuf.writeByte(LogEvent.SEPARATOR);
byteBuf.writeBytes(content);
out.add(new DatagramPacket(byteBuf, remoteAddress));
}
}

编码器 LogEventEncoder.java

该编码器实现了将 LogEvent 实体类内容转换为 DatagramPacket UDP数据报。

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)
//引导该 NioDatagramChannel(无连接的)
.channel(NioDatagramChannel.class)
// 设置 SO_BROADCAST 套接字选项
.option(ChannelOption.SO_BROADCAST, true)
.handler(new LogEventEncoder(address));
this.file = file;
} public void run() throws InterruptedException, IOException {
//绑定 Channel,UDP 协议的连接用 bind() 方法
Channel channel = bootstrap.bind(0).sync().channel();
long pointer = 0;
//长轮询 监听是否有新的日志文件生成
while (true) {
long length = file.length();
if (length < pointer) {
// 如果有必要,将文件指针设置到该文件的最后一个字节
pointer = length;
} else {
RandomAccessFile raf = new RandomAccessFile(file, "r");
// 确保当前的文件指针,以确保没有任何的旧数据被发送
raf.seek(pointer);
String line;
while ((line = raf.readLine()) != null) {
//对于每个日志条目,写入一个 LogEvent 到 Channel 中,最后加入一个换行符号
channel.writeAndFlush(new LogEvent(file.getAbsolutePath(), line + System.getProperty("line.separator")));
}
pointer = raf.getFilePointer();
raf.close();
}
try {
// 休眠一秒,如果被中断,则退出循环,否则重新处理它
Thread.sleep(1000);
} catch (InterruptedException e) {
while (!Thread.interrupted()) {
break;
}
}
}
} public void stop() {
group.shutdownGracefully();
} public static void main(String[] args) throws IOException, InterruptedException {
InetSocketAddress socketAddress = new InetSocketAddress("255.255.255.255", 8888);
File file = new File("E:\\2018-09-12.log");
LogEventBroadcaster logEventBroadcaster = new LogEventBroadcaster(socketAddress, file);
try {
logEventBroadcaster.run();
} finally {
logEventBroadcaster.stop();
}
}
}

现在,我们来测试一下这个 UDP 广播类,首先我们需要一个工具 nmap ,用它来监听 UDP 的 8888 端口,以接收我们广播的日志文件。下载地址: https://nmap.org/dist/nmap-7.70-win32.zip

下载完成后,命令行进入安装目录,执行命令:ncat.exe -l -u -p 8888 ,监听 UDP 端口。

当然,也可以自己写个测试类监听 UDP 端口,打印日志查看。这里我没有用 Netty 写监听类,直接用了 java 原生的 DatagramSocket 和 DatagramPacket 写的监听类,如下:

public class UDPServer {

    public static void main(String[] args) {
DatagramSocket server = null;
try {
server = new DatagramSocket(8888);
byte[] datas = new byte[1024];
//用一个字节数组接收UDP包,字节数组在传递给构造函数时是空的
while (true) {
DatagramPacket datagramPacket = new DatagramPacket(datas, datas.length);
server.receive(datagramPacket);
System.out.println(new String(datas));
}
} catch (SocketException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
server.close();
}
}
}

UDPServer.java

基于 Netty 的监听类实现可以参考我上传 GitHub 上的源代码。

参考资料:《Netty IN ACTION》

演示源代码:https://github.com/JMCuixy/NettyDemo/tree/master/src/main/java/org/netty/demo/udp

Netty 系列九(支持UDP协议).的更多相关文章

  1. netty系列之:使用UDP协议

    目录 简介 UDP协议 String和ByteBuf的转换 构建DatagramPacket 启动客户端和服务器 总结 简介 在之前的系列文章中,我们到了使用netty做聊天服务器,聊天服务器使用的S ...

  2. netty 3.9.2 UDP协议服务器和客户端DEMO

    说明:基于netty 3.9.2的udp协议实现的(如果你使用的版本是4.X或5.X,请参考其他方法):程序的逻辑结构是,客户端发送给服务端一串数据,服务器端返回给客户端“A”.在进行游戏开发时需要对 ...

  3. netty系列之:kequeue传输协议详解

    目录 简介 KQueueEventLoopGroup KQueueEventLoop KQueueServerSocketChannel和KQueueSocketChannel 总结 简介 在前面的章 ...

  4. netty系列之:protobuf在UDP协议中的使用

    目录 简介 UDP在netty中的表示 DatagramPacketEncoder DatagramPacketDecoder 总结 简介 netty中提供的protobuf编码解码器可以让我们直接在 ...

  5. Netty系列之源码解析(一)

    本文首发于微信公众号[猿灯塔],转载引用请说明出处 接下来的时间灯塔君持续更新Netty系列一共九篇 当前:Netty 源码解析(一)开始 Netty 源码解析(二): Netty 的 Channel ...

  6. 采用UDP协议实现PIC18F97J60 ethernet bootloader

    了解更多关于bootloader 的C语言实现,请加我QQ: 1273623966 (验证信息请填 bootloader),欢迎咨询或定制bootloader(在线升级程序). TCP/IP Stac ...

  7. 没错,请求DNS服务器还可以使用UDP协议

    目录 简介 搭建netty客户端 在netty中发送DNS查询请求 DNS消息的处理 总结 简介 之前我们讲到了如何在netty中构建client向DNS服务器进行域名解析请求.使用的是最常见的TCP ...

  8. netty系列之:netty对SOCKS协议的支持

    目录 简介 SocksMessage Socks4Message Socks5Message 总结 简介 SOCKS是一个优秀的网络协议,主要被用来做代理,它的两个主要版本是SOCKS4和SOCKS5 ...

  9. netty支持的协议

    流经网络的数据总是具有相同的类型:字节.这些字节是如何流动的主要取决于我们所说的 网络传输--一个帮助我们抽象底层数据传输机制的概念.用户并不关心这些细节:他们只想确保他们的字节被可靠地发送和接收. ...

随机推荐

  1. 新FSM的一些思路

    好久之前写过一篇关于状态机的小例子,可以看这里http://www.cnblogs.com/mawanli/p/5966080.html,这篇博客首先感谢需要感谢当时看到凉鞋的笔记博客, 凉鞋的博客地 ...

  2. [转载]你所不了解的DevOps

    DevOps开发运维训练营 一旦建立了创新的文化,即使那些并非科学家或者工程师的人——诗人.演员.记者——也能以团体的形式,接受科学文化的意义.他们信奉创新文化的概念.他们以促进这种文化的方式投票.他 ...

  3. QQ的成功,远没有你想象的那么顺利和轻松

    本文来自公众号“傅老师”(ID:fustory)的原创分享,感谢作者. 1.引言 如果QQ是一个人,看似风光,其实从出生到成长,过程饱经错荡,堪算坎坷.它的人生历程确实也够励志的了.   学习交流: ...

  4. 1.TabActivity、视图树、动画

    整个页面为TabActivity, 其中对TabWidget进行了一些改变,当切换页签时页签后面红色背景会以Translate动画形式移动到相对应的页签后. 布局 )); lastPosition = ...

  5. 机器学习入门11 - 逻辑回归 (Logistic Regression)

    原文链接:https://developers.google.com/machine-learning/crash-course/logistic-regression/ 逻辑回归会生成一个介于 0 ...

  6. sql server 索引阐述系列六 碎片查看与解决方案

    一 . dm_db_index_physical_stats 重要字段说明 1.1 内部碎片:是avg_page_space_used_in_percent字段.是指页的填充度,为了使磁盘使用状况达到 ...

  7. MySQL联合索引VS单列索引

    MySQL联合索引VS单列索引 以一个一千万数据量的表格为例 1. 建表建索引 USE foo; DROP TABLE IF EXISTS tmp; CREATE TABLE tmp ( id INT ...

  8. 基于Github&Hexo的个人博客搭建过程

    大家好,这里是「 从零开始学 Web 系列教程 」,并在下列地址同步更新...... github:https://github.com/Daotin/Web 微信公众号:Web前端之巅 博客园:ht ...

  9. 了解Java内存模型,看完这一篇就够了

    前言(此文草稿是年前写的,但由于杂事甚多一直未完善好.清明假无事,便收收尾发布了) 年关将近,个人工作学习怠惰了不少.两年前刚做开发的时候,信心满满想看看一个人通过自己的努力,最终能达到一个什么样的高 ...

  10. 【EF6学习笔记】(十二)EF高级应用场景

    本篇原文链接:Advanced Entity Framework Scenarios 本篇主要讲一些使用Code First建立ASP.NET WEB应用的时候除了基础的方式以外的一些扩展方式方法: ...