UDP 不需要连接,直接向服务器发送连接请求,而不会考虑是否连接成功
首先项目目的是读取本地日志文件,然后一行一行打印出消息
首先设置消息对象 LogEvent
package UDP;
import java.net.InetSocketAddress;
public final class LogEvent {
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;
}
}
LogEvent消息类型需要进行转换,才能够被Netty框架处理
因此如何转换消息类型时关键,这里采用DatagramPacket 进行消息的接受,并在Channel中流动
package UDP;
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;
import java.net.InetSocketAddress;
import java.util.List;
public class LogEventEncoder extends MessageToMessageEncoder<LogEvent> {
private final InetSocketAddress remoteAddress;
LogEventEncoder(InetSocketAddress remoteAddress) {
this.remoteAddress = remoteAddress;
}
@Override
protected void encode(ChannelHandlerContext ctx, LogEvent msg, List<Object> out) throws Exception {
// 将消息内容转为byte类型存储,以待传入byteBuf
byte[] file = msg.getLogfile().getBytes(CharsetUtil.UTF_8);
byte[] message = msg.getMsg().getBytes(CharsetUtil.UTF_8);
ByteBuf byteBuf = ctx.alloc().buffer(file.length+message.length+1);// 分配byteBuf的内存
byteBuf.writeBytes(file);// 将file内容写入byteBuf
byteBuf.writeByte(LogEvent.SEPARATOR);// 写入指定的分隔符
byteBuf.writeBytes(message);
out.add(new DatagramPacket(byteBuf, remoteAddress));// 将datagrampacket写入out列,进行处理
}
}
DatagramPacket 是一个指明消息地址和消息内容的信息包裹,因此表现为
new DatagramPacket(ByteBuf buf, InetSocketAddress remoteAddress)
广播器从文件中读取日志,并解码后传入Channel进行发送
广播器要有地址和传输的文件,才能进行读取并转为LogEvent类型,然后发送给地址内的各个接收点
package UDP;
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;
import java.io.File;
import java.io.RandomAccessFile;
import java.net.InetSocketAddress;
public class LogEventBroadcaster {
private File file;// 日志文件地址
private InetSocketAddress address;// 目标地址
// 构造函数指定传输地址和文件地址,才能进行下一步操作
public LogEventBroadcaster(InetSocketAddress address, File file){
this.address = address;
this.file = file;
}
public static void main(String[] args) throws Exception {
File file = new File("C:\\Users\\root\\IdeaProjects\\SimpleNetty\\src\\main\\java\\Demo\\file.txt");
InetSocketAddress address = new InetSocketAddress("255.255.255.255", 9999);
new LogEventBroadcaster(address, file).run();
}
public void run() throws Exception{
EventLoopGroup group = new NioEventLoopGroup();
Bootstrap b = new Bootstrap();
b.group(group)
.channel(NioDatagramChannel.class) // 使用非阻塞DatagramChannel
.option(ChannelOption.SO_BROADCAST, true)// 设置BROADCAST广播
.handler(new LogEventEncoder(address));// 添加处理器
// 获取引导bootstrap对应的Channel
Channel ch = b.bind(0).syncUninterruptibly().channel();
System.out.println("LogEventBroadcaster running!");
long pointer = 0;// 文件指针
for (;;){
long len = file.length();
if (len < pointer){
pointer = len;
} else if (len > pointer) {// 从0开始读取文件
RandomAccessFile raf = new RandomAccessFile(this.file, "r");
raf.seek(pointer);// 跳到指针的位置
String line;
while ((line = raf.readLine())!=null){// 逐行读取文件内容,指针随之移动,直到文件末尾
// 将logevent消息传输给Channel
ch.writeAndFlush(new LogEvent(null, -1, file.getAbsolutePath(), line));
}
pointer = raf.getFilePointer();// 获取当前指针位置,如果循环被中断,则重启时不会读取旧文件
raf.close();// 关闭文件
}
try {
Thread.sleep(1000);
} catch (InterruptedException e){
Thread.interrupted();
break;
}
}
group.shutdownGracefully();
}
}
接收者将收到的DatagramPacket进行解码为String类型,转换为byte内容进行处理
package UDP;
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;
import java.nio.charset.StandardCharsets;
import java.util.List;
public class LogEventDecoder extends MessageToMessageDecoder<DatagramPacket> {
@Override
protected void decode(ChannelHandlerContext ctx, DatagramPacket packet, List<Object> out) throws Exception {
ByteBuf byteBuf = packet.content();// 消息包的内容存入byteBuf中
// 获取分隔符的索引,便于分割bytebuf为两部分
int idx = byteBuf.indexOf(0, byteBuf.readableBytes(), LogEvent.SEPARATOR);
// 将bytebuf转化为两部分的String类型
String logfile = byteBuf.slice(0, idx).toString(StandardCharsets.UTF_8);// 提取文件名
String message = byteBuf.slice(idx+1, byteBuf.readableBytes()).toString(CharsetUtil.UTF_8);
// 转化为LogEvent类型
LogEvent event = new LogEvent(packet.sender(), System.currentTimeMillis(), logfile, message);
out.add(event);
}
}
对LogEvent进行处理,将其转化为能够阅读的格式,打印到屏幕
package UDP;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
public class LogEventHandler extends SimpleChannelInboundHandler<LogEvent> {
@Override
protected void channelRead0(ChannelHandlerContext ctx, LogEvent event) throws Exception {
String builder = event.getReceivedTimestamp() +
" [" +
event.getSource().toString() +
"] [" +
event.getLogfile() +
"] : " +
event.getMsg();//3
System.out.println(builder); //4
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
cause.printStackTrace();
ctx.close();
}
}
package UDP;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioDatagramChannel;
import java.net.InetSocketAddress;
public class LogEventMonitor {
private final Bootstrap bootstrap;
private final EventLoopGroup group;
public LogEventMonitor(InetSocketAddress address) {
group = new NioEventLoopGroup();
bootstrap = new Bootstrap();
bootstrap.group(group) //1
.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()); // 先将packet数据解码为LogEvent
pipeline.addLast(new LogEventHandler()); // 添加处理LogEvent的Handler
}
}).localAddress(address);
}
public Channel bind() {
return bootstrap.bind().syncUninterruptibly().channel(); //3
}
public void stop() {
group.shutdownGracefully();
}
public static void main(String[] args) throws Exception {
LogEventMonitor monitor = new LogEventMonitor(new InetSocketAddress(9999)); //4
try {
Channel channel = monitor.bind();
System.out.println("LogEventMonitor running");
channel.closeFuture().await();
} finally {
monitor.stop();
}
}
}
先运行监听器,再运行广播器,否则广播器会直接发送消息,监听器就接受不到了