简体   繁体   English

如何从InputStream读取二进制协议?

[英]How to read binary protocol from InputStream?

I try to read a binary protocol from my serial port in Java. 我尝试从Java的串行端口读取二进制协议。 I use the RXTX library to read from the serial port. 我使用RXTX库从串行端口读取。

The protocol is the SiRF binary protocol for GPS receivers. 该协议是GPS接收器的SiRF二进制协议。 The structure is: 结构为:

  • first two bytes are 0xA0 and 0xA2, which define the beginning of a data frame 前两个字节是0xA0和0xA2,它们定义数据帧的开头
  • next two bytes are the payload length n 接下来的两个字节是有效载荷长度n
  • next n bytes are the payload 接下来的n个字节是有效载荷
  • last two bytes are 0xB0 and 0xB3, which define the end of a data frame 最后两个字节是0xB0和0xB3,它们定义数据帧的结尾

Although this structure is quite simple I am not able to read the data correctly. 尽管此结构非常简单,但我无法正确读取数据。 I tried two different ways: 我尝试了两种不同的方法:

  1. I wrote a thread, which reads from the InputStream and compares the input with 0xA0. 我编写了一个线程,该线程从InputStream读取并将输入与0xA0进行比较。 If this matches, then the thread reads the next byte and compares to 0xA2. 如果匹配,则线程读取下一个字节并与0xA2比较。 If this matches again, it reads the next two bytes and computes the payload length. 如果再次匹配,它将读取接下来的两个字节并计算有效载荷长度。 After that, it reads the payload with length n bytes. 之后,它将读取长度为n个字节的有效负载。 At last, it reads the last two bytes and compares them to 0xB0 and 0xB3. 最后,它读取最后两个字节并将它们与0xB0和0xB3比较。 When everything matches, it creates a hex string from the input and saves it to a queue. 当所有内容匹配时,它将根据输入创建一个十六进制字符串,并将其保存到队列中。 Here is my code: 这是我的代码:

     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() just reads a byte from the InputStream . getInput()只是从InputStream读取一个字节。

  2. I used the SerialPortEventListener from the RXTX library. 我使用了RXTX库中的SerialPortEventListener Everytime something happens on the serial port, a SerialPortEvent is fired and I read the data in a similar way as in the first method. 每当串行端口上发生任何事情时,都会触发SerialPortEvent,并且我以与第一种方法类似的方式读取数据。

For debugging reasons I added several System.out.println() calls to show what exactly is read by the threads and it seems that I am missing nearly the half of all data frames sended via the InputStream . 出于调试原因,我添加了几个System.out.println()调用以显示线程确切读取的内容,并且似乎丢失了通过InputStream发送的所有数据帧的几乎一半。

My question now is: What is the best way to read such a protocol from an InputStream ? 我现在的问题是:从InputStream读取此类协议的最佳方法是什么? In my opinion I have to get my reading efforts synchronized with the data from the serial port, but I have no idea how to do that in Java. 我认为我的阅读工作必须与来自串行端口的数据同步,但是我不知道如何在Java中做到这一点。

If you are using RXTX you can set a listener which will notify you asyncronously when data is available: 如果您使用的是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();
    }
}

In general, when you read from the input stream you should abstract the concept of "packet of data" and not use the "available" function - just read enough from the packet to be able to determine its length, and then just use the "read(byte[])" to read a full packet into a buffer. 通常,当您从输入流中读取数据时,应该抽象化“数据包”的概念,而不要使用“可用”功能-只需从数据包中读取足够的数据即可确定其长度,然后使用“ read(byte [])“将完整的数据包读入缓冲区。 (on a more advanced note, the "read" can read fewer bytes than needed so it should be used in a loop). (在更高级的注释上,“读取”可以读取的字节数少于所需的字节数,因此应在循环中使用它)。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM