/* * Copyright 2018 netty.reactiveplatform.xyz * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package nia.chapter13 import io.netty.bootstrap.Bootstrap import io.netty.channel.{ ChannelOption, EventLoopGroup } import io.netty.channel.nio.NioEventLoopGroup import io.netty.channel.socket.nio.NioDatagramChannel import java.io.File import java.io.RandomAccessFile import java.net.InetSocketAddress import java.lang.{ Boolean ⇒ JBoolean } import java.util.Objects import scala.util.control.Breaks._ /** * 代码清单 13-3 LogEventBroadcaster * * @author <a href="mailto:[email protected]">Norman Maurer</a> */ object LogEventBroadcaster { @throws[Exception] def main(args: Array[String]): Unit = { if (args.length != 2) throw new IllegalArgumentException //创建并启动一个新的 LogEventBroadcaster 的实例 val broadcaster = new LogEventBroadcaster(new InetSocketAddress("255.255.255.255", args(0).toInt), new File(args(1))) try { broadcaster.run() } finally { broadcaster.stop() } } } class LogEventBroadcaster(address: InetSocketAddress, file: File) { val group: EventLoopGroup = new NioEventLoopGroup val bootstrap = new Bootstrap //引导该 NioDatagramChannel(无连接的) bootstrap .group(group) .channel(classOf[NioDatagramChannel]) //设置 SO_BROADCAST 套接字选项 .option[JBoolean](ChannelOption.SO_BROADCAST, true) .handler(new LogEventEncoder(address)) @throws[Exception] def run(): Unit = { //绑定 Channel val ch = bootstrap.bind(0).sync.channel var pointer: Long = 0 //启动主处理循环 breakable { while (true) { val len = file.length if (len < pointer) { // file was reset //如果有必要,将文件指针设置到该文件的最后一个字节 pointer = len } else if (len > pointer) { // Content was added val raf = new RandomAccessFile(file, "r") //设置当前的文件指针,以确保没有任何的旧日志被发送 raf.seek(pointer) Iterator.continually(raf.readLine()) .takeWhile(Objects.nonNull) .foreach { line ⇒ ch.writeAndFlush(LogEvent(file.getAbsolutePath, line)) } //存储其在文件中的当前位置 pointer = raf.getFilePointer raf.close() } try { //休眠 1 秒,如果被中断,则退出循环;否则重新处理它 Thread.sleep(1000) } catch { case e: InterruptedException ⇒ Thread.interrupted break } } } } def stop(): Unit = { group.shutdownGracefully() } }