Netty的简单应用
Netty可用于接收RTU的报文。 我们的项目主要需求是开发一款可用于PLC的上位机软件,PLC的报文格式是自定义的,所以我们需要使用Netty来接收PLC的报文,然后进行解析。 报文标准为:SL651-2014 水文监测数据通信规约 (基于Modbus协议) 这里主要实现了《SL651》中的遥测站定时报上行报文的解析。
1.引用插件
xml
<!-- https://mvnrepository.com/artifact/io.netty/netty-all -->
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-all</artifactId>
<version>4.1.107.Final</version>
</dependency>
2.创建一个Netty服务
java
package xxx.iot;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
@Component
public class PNettyServer {
@Autowired
/**
* 解码器
*/
SL651ModbusDecoder sL651ModbusDecoder;
@Autowired
/**
* 服务端口
*/
int port = 9995;
@PostConstruct
private void init(){
EventLoopGroup bossGroup = new NioEventLoopGroup();
EventLoopGroup workerGroup = new NioEventLoopGroup(4);
ServerBootstrap bootstrap = new ServerBootstrap();
bootstrap.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class)
.childOption(ChannelOption.SO_REUSEADDR, true)
.childHandler(new ChannelInitializer<SocketChannel>(){
@Override
public void initChannel(SocketChannel ch)throws Exception{
ch.pipeline()
.addLast(new SL651ModbusDecoder())
.addLast(new PChannelHandler());
}
});
bootstrap.bind(port).addListener((ChannelFutureListener) future -> {
System.out.println("rtu netty bind success in port: " + port);
});
System.out.println("rtu netty server started!");
}
}
3.创建一个适配器接收入站事件与数据
java
package xxx.iot;
import xxx.core.utils.DateUtils;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import java.util.Date;
public class PChannelHandler extends ChannelInboundHandlerAdapter {
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
super.channelActive(ctx);
}
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
System.out.println(DateUtils.dateTimeNow()+" RTU RADAR: "+msg);
super.channelRead(ctx, msg);
}
@Override
public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
super.channelReadComplete(ctx);
}
}
4.创建一个解码器
java
package xxx.iot;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.ByteToMessageDecoder;
import java.util.List;
public class SL651ModbusDecoder extends ByteToMessageDecoder {
public SL651ModbusDecoder(){}
@Override
protected void decode(ChannelHandlerContext channelHandlerContext, ByteBuf byteBuf, List<Object> list) throws Exception {
// 这里可以随心读取数据,常用读取方法有三个
// 1 标记当前读游标位置
byteBuf.markReaderIndex();
// 2 返回到最近标记的游标位置
butyfBuf.resetReaderIndex();
// 3 定长读取数据
byte[] bufferArray = new byte[1];
byteBuf.readBytes(bufferArray, 0, 1);
}
}