本文参考

本篇文章是对《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广播事件的更多相关文章

  1. 【Netty】UDP广播事件

    一.前言 前面学习了WebSocket协议,并且通过示例讲解了WebSocket的具体使用,接着学习如何使用无连接的UDP来广播事件. 二.UDP广播事件 2.1 UDP基础 面向连接的TCP协议管理 ...

  2. Netty学习摘记 —— 初步认识Netty核心组件

    本文参考 我在博客内关于"Netty学习摘记"的系列文章主要是对<Netty in action>一书的学习摘记,文章中的代码也大多来自此书的github仓库,加上了一 ...

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

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

  4. Netty学习摘记 —— 简单WEB聊天室开发

    本文参考 本篇文章是对<Netty In Action>一书第十二章"WebSocket"的学习摘记,主要内容为开发一个基于广播的WEB聊天室 聊天室工作过程 请求的 ...

  5. Netty学习摘记 —— 再谈引导

    本文参考 本篇文章是对<Netty In Action>一书第八章"引导"的学习摘记,主要内容为引导客户端和服务端.从channel内引导客户端.添加ChannelHa ...

  6. Netty学习摘记 —— 心跳机制 / 基于分隔符和长度的协议

    本文参考 本篇文章是对<Netty In Action>一书第十一章"预置的ChannelHandler和编解码器"的学习摘记,主要内容为通过 SSL/TLS 保护 N ...

  7. Netty学习摘记 —— 预置SSL / HTTP / WebSocket编解码器

    本文参考 本篇文章是对<Netty In Action>一书第十一章"预置的ChannelHandler和编解码器"的学习摘记,主要内容为通过 SSL/TLS 保护 N ...

  8. Netty学习摘记 —— 初识编解码器

    本文参考 本篇文章是对<Netty In Action>一书第十章"编解码器框架"的学习摘记,主要内容为解码器和编码器 编解码器实际上是一种特殊的ChannelHand ...

  9. Netty学习摘记 —— 再谈EventLoop 和线程模型

    本文参考 本篇文章是对<Netty In Action>一书第七章"EventLoop和线程模型"的学习摘记,主要内容为线程模型的概述.事件循环的概念和实现.任务调度和 ...

随机推荐

  1. springMVC整合mybatis,spring

    使用spring-mvc创建一个项目的过程 spring的配置十分复杂,很难记忆. 这篇博客用于记录springmvc整合创建过程,虽然步骤有点多,但是每一步都很容易理解,便于以后忘记后参考和记忆. ...

  2. 常用windows快捷键及cmd、dos命令

    Windows常用快捷键 #Alt+F4:关闭窗口.网页 #ctrl+C:复制 #ctrl+V:粘贴 #ctrl+X:剪切 #ctrl+Z:撤销 #ctrl+A:全选 #ctrl+S:保存 #shif ...

  3. 【C# IO 操作 】Span<T>类

    Span 原理探究 ref结构 ref struct是仅在堆栈上的值类型: 表现一个顺序结构的布局:(译注:可以理解为连续内存) 只能在堆栈上使用.即用作方法参数和局部变量: 不能是类或正常结构的静态 ...

  4. md5非对称密钥

    一.MD5加密概述 Message Digest Algorithm MD5(中文名为消息摘要算法第五版)为计算机安全领域广泛使用的一种散列函数,用以提供消息的完整性保护.该算法的文件号为RFC 13 ...

  5. parquet和orc选型以及压缩格式

    Hive表压缩功能 除了直接配置MapReduce压缩功能外,Hive的ORC表和Parquet表直接支持表的压缩属性. 但支持的压缩格式有限,ORC表支持None.Zlib.Snappy压缩,默认为 ...

  6. VT 入门篇——最小 VT 实现(上)

    写在前面   此系列是本人一个字一个字码出来的,包括示例和实验截图.由于系统内核的复杂性,故可能有错误或者不全面的地方,如有错误,欢迎批评指正,本教程将会长期更新. 如有好的建议,欢迎反馈.码字不易, ...

  7. Qt:Qt资源系统

    学习自 Qt 资源系统(Qt Resource System) - 知乎 1.什么是Qt 资源系统 Qt资源系统是一种将图片.数据存储于二进制文件中的一套系统.这些图片.数据会被我们的程序使用,它们称 ...

  8. Python 细聊可以媲美 PS 的 PIL 图片处理库

    1 . 前言 PIL 是 Python Image Library 的简称. PIL 库中提供了诸多用来处理图片的模块,可以对图片做类似于 PS(Photoshop) 的编辑.比如:改变图像大小.旋转 ...

  9. BeautifulSoup图片爬取

    ------------恢复内容开始------------ BeautifulSoup介绍: 简单来说,Beautiful Soup 是 python 的一个库,最主要的功能是从网页抓取数据.官方解 ...

  10. ArcMap操作随记(13)

    1.为地图册创建定位器地图 创建数据框→标注字段(PageName)→格网索引图层[导出数据]→图层右键,[属性]|[定义查询] 2.为地图册创建动态文本 导出→[文件]|[导出地图]→PDF 3.关 ...