【问题标题】:How to read binary protocol from InputStream?如何从 InputStream 中读取二进制协议?
【发布时间】:2012-11-03 13:11:09
【问题描述】:

我尝试从我的 Java 串行端口读取二进制协议。我使用RXTX library 从串口读取。

该协议是用于 GPS 接收器的 SiRF 二进制协议。结构是:

  • 前两个字节是 0xA0 和 0xA2,它们定义了数据帧的开始
  • 接下来的两个字节是有效载荷长度 n
  • 接下来的 n 个字节是有效负载
  • 最后两个字节是 0xB0 和 0xB3,它们定义了一个数据帧的结束

虽然这个结构很简单,但我无法正确读取数据。我尝试了两种不同的方法:

  1. 我写了一个线程,它从InputStream 中读取数据并将输入与0xA0 进行比较。如果匹配,则线程读取下一个字节并与 0xA2 进行比较。如果再次匹配,它将读取接下来的两个字节并计算有效负载长度。之后,它读取长度为 n 字节的有效负载。最后,它读取最后两个字节并将它们与 0xB0 和 0xB3 进行比较。当一切都匹配时,它会从输入中创建一个十六进制字符串并将其保存到队列中。这是我的代码:

    try {
        byte[] size = new byte[2];
        byte[] checkSum = new byte[2];
        byte[] packet;
        int bytesRead = -1;
        while (in.available() > 0) {
            if (getInput() == 0xA0) {
                if (getInput() == 0xA2) {
                    System.out.print("160,162,");
                    bytesRead = in.read(size);
                    int payLoadLength = (size[0] << 8) | (size[1] & 0xFF);
                    byte[] payload = new byte[payLoadLength];
                    bytesRead = in.read(payload);
                    bytesRead = in.read(checkSum);
                    if (getInput() == 0xB0) {
                        if (getInput() == 0xB3) {
                    System.out.println("out");
                    packet = new byte[2 + 2 + payLoadLength + 2 + 2];
                    packet[0] = (byte) START_1;
                    packet[1] = (byte) START_2;
                    packet[2] = size[0];
                    packet[3] = size[1];
                    for (int i = 0; i < payload.length; i++) {
                        packet[i + 4] = payload[i];
                    }
                    packet[packet.length - 4] = checkSum[0];
                    packet[packet.length - 3] = checkSum[1];
                    packet[packet.length - 2] = (byte) END_1;
                    packet[packet.length - 1] = (byte) END_2;
                    StringBuffer hexString = new StringBuffer();
                    int[] output = new int[packet.length];
                    for (int i = 0; i < packet.length; i++) {
                        output[i] = 0xFF & packet[i];
                        hexString.append(Integer.toHexString(output[i]));
                    }
                    TransportMessage tm = new TransportMessage(hexString.toString());
                    boolean b = queue.offer(tm);
                    System.out.println(hexString.toString().toUpperCase());
                    if(!b)
                        System.err.println("TransportMessageQueue voll!");
                }
            } else {
                System.out.println();
            }
        }
    } 
        }
    } catch (IOException e) {
        System.out.println(e);
    }
    

    getInput() 只是从InputStream 中读取一个字节。

  2. 我使用了来自 RXTX 库的 SerialPortEventListener。每当串行端口上发生某些事情时,就会触发一个 SerialPortEvent 并且我以与第一种方法类似的方式读取数据。

出于调试原因,我添加了几个 System.out.println() 调用以显示线程读取的确切内容,似乎我丢失了通过 InputStream 发送的所有数据帧的近一半。

我现在的问题是:从InputStream 读取此类协议的最佳方法是什么?在我看来,我必须让我的阅读工作与来自串行端口的数据同步,但我不知道如何在 Java 中做到这一点。

【问题讨论】:

  • java.io.DataInputStream 用于此类任务。

标签: java serial-port protocols inputstream binary-data


【解决方案1】:

如果你使用 RXTX,你可以设置一个监听器,当数据可用时异步通知你:

serial.notifyOnDataAvailable(true);
serial.addEventListener(this);

public void serialEvent(final SerialPortEvent arg0) {
    try {
        if (arg0.getEventType() == SerialPortEvent.DATA_AVAILABLE) {
            //read from the input stream;
        }
    } catch (final Exception e) {
        e.printStackTrace();
    }
}

一般来说,当您从输入流中读取数据时,您应该抽象出“数据包”的概念,而不是使用“可用”函数——只需从数据包中读取足够的数据以确定其长度,然后只需使用“read(byte[])”将完整的数据包读入缓冲区。 (在更高级的说明中,“读取”可以读取比需要更少的字节,因此应该在循环中使用)。

【讨论】:

  • 我已经试过了。应该更清楚地说明它。这样一来,我还是漏掉了一些数据框。
  • 我发现了问题:这是围绕阅读过程的附加循环while (in.available() &gt; 0) {}。你写了“当数据可用时异步通知你”,这让我很怀疑......在删除这个循环之后,它似乎按预期工作。谢谢!
  • 是的,我从来没有很好地利用“可用”功能。
猜你喜欢
  • 2012-10-07
  • 2015-08-16
  • 1970-01-01
  • 2011-09-25
  • 2012-01-21
  • 1970-01-01
  • 1970-01-01
  • 2023-04-11
  • 1970-01-01
相关资源
最近更新 更多