HID通信的长度受到USB设备速率的影响。
对于低速设备(LS),最大包长为8字节。
对于全速设备(FS),最大包长为64字节。
对于高速设备(HS),最大包长为1024字节。
如果使用的是全速设备,但需要传输的数据超过64字节,如256字节,可以通过拆包传输的方式处理,即将数据拆分成多个64字节的包进行传输。这种拆包传输的方式在HID通信中是一种常用的处理方法1。
项目需要Android客户端与PC控件传输数据,数据较长且长度不确定,由于HID通信是面向流的通信且每次最大的传输数据为1024长度的byte数组,面向流的通信是无消息保护边界的,所以如何确定接收的数据的长度是关键问题,在传输中容易出现乱码和粘包的问题。
在TCP数据传输中,解决流式数据传输的粘包/拆包问题的方法主要包括消息长度前置、消息定长、消息定界符。
使用HID通信每次只能传长度为1024的byte数组,如果传输的数据不够1024则用0补全,如果传输的数据超过了1024需要分割为多个数据包进行传输。接收时不够1024长度时需要补0,超过1024时需要接受多个数据包,最后将多个数据包进行累加,根据消息定界符计算数据的长度,截取界定符中间的数据为最终接受的数据。
第一种方式:接收方首先接收到传输数据的字符数组的长度,其次读取上次接收到的长度的byte数组的数据,即为需要处理的数据。
@Override
public void run() {
if ( hid == null )
return;
hid.open();
while (true) {
if (isStop) {
break;
}
try {
ByteArrayOutputStream bytearray = new ByteArrayOutputStream();
ByteArrayOutputStream outs_bytearray = new ByteArrayOutputStream();
int len = 0;
byte[] bytelen = new byte[1024];
byte[] buffer = null;
//接收待处理数据的长度
len = hid.read(bytelen);
if (len < 4) {
byte[] erroutbytes = "error".getBytes();
hid.write(intToByteArray(erroutbytes.length));
hid.write(erroutbytes);
logger.error("hid error");
} else {
//获取待处理数据的实际长度
int reallen = byteArrayToInt(bytelen, 0);
//读取制定长度的数据
byte[] revdata = new byte[reallen];
hid.read(revdata);
if (sendMessage != null) {
byte[] outbytes = sendMessage.SendByteData(handler, revdata);
if (outbytes == null) {
hid.write(intToByteArray(0));
logger.error("hid send error");
} else {
hid.write(intToByteArray(outbytes.length));
hid.write(outbytes);
}
}
}
bytearray.close();
outs_bytearray.close();
} catch (Exception e) {
logger.error("hid exception:", e);
}
}
}
第二种方式:
Android客户端和PC控件端约定数据以begin01XXXend的格式传输,即最后截取的时begin01和end之间的数据。这样将会存在一个问题,就是多个数据包中可能出现多个end的情况,就无法准确计算出数据的末尾。针对这个问题,我们将数据的结束位设计未byte形式的0,由于传输的数据是字符串,byte形式的0是不可能会出现的。最后 一个包的数据长度小于1024时,会补全多个0,所以我们取第一个0的位置减去end的byte长度的位置,就是最后数据结束的位置,具体查找起始位置和结束位置的代码如下所示:
// 查找并打印以 "begin" 开头和 "end" 结尾的中间数据
private void findAndPrintDataBetweenMarkers(byte[] data){
byte[] begin01Bytes = "begin01".getBytes();
byte[] endBytes = "end".getBytes();
int beginIndex = -1;
int endIndex = -1;
// 查找开始标记的位置
for (int i = 0; i <= data.length - begin01Bytes.length; i++) {
if (startsWith(data, i, begin01Bytes)) {
beginIndex = i + begin01Bytes.length;
break;
}
}
// 如果没有找到开始标记,则直接返回
if (beginIndex == -1) {
System.out.println("No 'begin' marker found.");
backToPC(1,"No 'begin' marker found.");
return;
}
// 从开始标记后面继续找结束标记的位置
for (int i = beginIndex; i < data.length; i++) {
if (data[i] == 0) {
endIndex = i-endBytes.length;
break;
}
}
// 如果没有找到结束标记,则直接返回
if (endIndex == -1) {
System.out.println("No 'end' marker found after 'begin'.");
backToPC(1,"No 'end' marker found after 'begin'.");
return;
}
// 提取并打印中间数据
byte[] middleData = new byte[endIndex - beginIndex];
System.arraycopy(data, beginIndex, middleData, 0, endIndex - beginIndex);
String middleDataString = new String(middleData);
System.out.println("Middle data between 'begin' and 'end': " + middleDataString);
handleWaitSignMsg(middleDataString);
backToPC(0,"receive PDF success");
}