首先dis一下某些物联网设备的通信协议。
您用TCP Socket协议也就罢了,可是您还通信协议只有开始符,没有结束符是怎么回事。
您还用不可见字符,二进制的,不便于调试。请问这都什么年代了,您的MCU(单片机)性能有那么弱吗?
来看一下通信协议:
通信帧格式:
名称 长度 说明
帧头
开始标识 2Byte 0xEF3A (高字节在前)
数据长度 2 Byte 长度是指信道、协议版本、命令、内容、CRC 五者的字节之和。(高字节在前)
信道 1 Byte 0x01 服务发出 4G/2G
0x02 设备发出
协议版本 1 Byte 当前版本 0x01
帧数据
命令 1 Byte
数据 N Byte
校验 CRC16 2 Byte 将开始标识、长度、信道、协议版本、命令和内容这六部分
内容的所有字节进行 CRC 计算(高字节在前)
别欺负我不懂MCU, 我以前也是干硬件出身,PCB也画过,MCU软件也写过,机器也量产过。
以上叨B叨那么多,硬件改不了,还是要对接啊。以下开始:
- 来人,上netty
其它的也就不説了,各种文章太多了,
来看看核心部分:
public class ServerChannelInitializer extends ChannelInitializer<SocketChannel> {
@Override
protected void initChannel(SocketChannel socketChannel) throws Exception {
//这是用最简单的分割符来分割
//ByteBuf delimiter = Unpooled.copiedBuffer("#".getBytes());
//添加编解码
//socketChannel.pipeline().addLast("decoder", new StringDecoder(CharsetUtil.UTF_8));
//socketChannel.pipeline().addLast("encoder", new StringEncoder(CharsetUtil.UTF_8));
//前置,处理了普通分隔符作为报文结束符的分包和粘包问题,不过不适合此例子
//socketChannel.pipeline().addLast(new MyDelimiterBasedFrameDecoder(1024, delimiter));
socketChannel.pipeline().addLast("decoder", new MyBinDecoder(CharsetUtil.UTF_8));
socketChannel.pipeline().addLast("encoder", new MyBinEncoder(CharsetUtil.UTF_8));
socketChannel.pipeline().addLast(new NettyServerHandler());
}
}
- 怎么写具体的协议解析
然后,就是我们定义的MyBinDecoder 最重要:
public class MyBinDecoder extends MessageToMessageDecoder<ByteBuf> {
private final Charset charset;
//上次的半包. 用于处理分包情况
private String lastHalfPacket = "";
public MyBinDecoder() {
this(Charset.defaultCharset());
}
public MyBinDecoder(Charset charset) {
if (charset == null) {
throw new NullPointerException("charset");
} else {
this.charset = charset;
}
}
protected void decode(ChannelHandlerContext ctx, ByteBuf msg, List<Object> out) throws Exception {
List<String> cmdList = parseCmdList(msg);
if(cmdList!=null && !cmdList.isEmpty()) {
log.info("添加list");
out.addAll(cmdList);
}else{
System.out.println("暂未发现指令");
}
}
byte[] starter = new byte[]{ (byte)0xEF, (byte)0x3A};
final int CMD_HEADER_LENGTH = 6;
private List<String> parseCmdList(ByteBuf originBuf){
ByteBuf buf = Unpooled.buffer(1024);
buf.writeBytes(ByteBufUtil.decodeHexDump(lastHalfPacket));
buf.writeBytes(originBuf);
List<String> result = new ArrayList<>();
log.info("开始遍历 buf readableBytes=" + buf.readableBytes());
try {
while(buf.readableBytes() >= CMD_HEADER_LENGTH) {
byte[] bStart = new byte[2];
//String hex = ByteBufUtil.hexDump(b);
buf.getBytes(buf.readerIndex(), bStart);
log.info("读starter:" + ByteBufUtil.hexDump(bStart));
if (compareBytes(bStart, starter)) {
log.info("找到了start");
//判断这次长度够不够读,如果不够读直接退出
//如果够读,读进来,插进cmd list
byte[] bHeader = new byte[CMD_HEADER_LENGTH];
//buf.markReaderIndex();
//buf.readBytes(bHeader);
buf.getBytes(buf.readerIndex(), bHeader);
int dataLength = bHeader[2] * 256 + bHeader[3];
//buf.resetReaderIndex();
int THEORYLENGTH = 4 + dataLength;
log.info("命令总长度: " + THEORYLENGTH);
if(buf.readableBytes() >= THEORYLENGTH) {
log.info("正在读全长");
byte[] bCmdFrame = new byte[THEORYLENGTH];
buf.readBytes(bCmdFrame);
//readerIndex += THEORYLENGTH;
String hexCmd = ByteBufUtil.hexDump(bCmdFrame);
log.info("找到一个指令啦:" + hexCmd);
result.add(hexCmd);
log.info("读缓冲区还有:" + buf.readableBytes());
continue;
}else{
log.info("命令总长未达预期,下次再读");
int halfPacketNum = buf.readableBytes();
byte[] bHalfPacket = new byte[halfPacketNum];
buf.readBytes(bHalfPacket);
if(halfPacketNum>=1) {
String halfPacketStr = ByteBufUtil.hexDump(bHalfPacket);
log.info("剩下半包"+halfPacketStr);
lastHalfPacket = halfPacketStr==null? "": halfPacketStr;
}
return result;
}
} else {
//读指针向后移动一个
log.info("没找到starter,读指针向后移动一个");
buf.readBytes(1);
continue;
}
}
int halfPacketNum = buf.readableBytes();
if(halfPacketNum > 0) {
log.info("HEADER命令头未达预期,下次再读");
byte[] bHalfPacket = new byte[halfPacketNum];
buf.readBytes(bHalfPacket);
String halfPacketStr = ByteBufUtil.hexDump(bHalfPacket);
log.info("剩下半包"+halfPacketStr);
lastHalfPacket = halfPacketStr==null? "": halfPacketStr;
}else{
log.info("余下字节恰好为0,本轮读完了");
}
}catch (Exception e){
e.printStackTrace();
log.warn(e.toString());
return result;
}
return result;
}
private boolean compareBytes(byte[] b1, byte[] b2){
if(b1==null && b2==null){
return true;
}
if( (b1==null && b2!=null) || (b1!=null && b2==null)){
return false;
}
if(b1.length != b2.length){
return false;
}
for(int i = 0; i<b1.length; i++){
if( b1[i]!=b2[i]){
return false;
}
}
return true;
}
}
- 后记
这是个半成品,只是打印出来了十六进制转成了HEX STRING的结果。后续对指令的处理,自己再加吧。
伸手党可去CSDN下载源码
https://download.csdn.net/download/stephenzhu/16593276
网友评论