Android POS与ECR集成:如何用2字节二进制表示消息长度
符合协议的Kotlin实现方案
核心问题分析
你的问题根源在于:
- 错误计算了「消息大小」的取值逻辑(可能误将完整消息总长度包含了头部2字节,导致大消息时长度超出2字节的表示上限)
- 未严格按照协议要求将消息大小编码为固定2字节二进制(原代码可能用了字符串或变长字节表示长度)
协议规则再明确
根据你给出的协议,完整消息各部分的关系:
<消息大小(2字节二进制)><方向标识(3字节ASCII)><协议变体(2字节ASCII)><协议版本(2字节ASCII)><实际消息(消息大小-7字节)>
其中:
- 消息大小的取值 = 方向标识长度(3) + 协议变体长度(2) + 协议版本长度(2) + 实际消息长度 = 7 + 实际消息的字节数(ASCII编码下等于字符数)
- 2字节二进制的表示范围是
0~65535,因此实际消息最大长度为65535-7=65528字节 - 字节序通常采用大端(Big-Endian)(高位字节在前,多数POS/ECR设备遵循此规范)
具体实现代码
import java.nio.ByteBuffer import java.nio.ByteOrder import java.nio.charset.Charset // 定义协议固定参数(根据你的实际业务需求修改) const val DIRECTION_ID = "POS" // 3字节ASCII示例 const val PROTOCOL_VARIANT = "01" // 2字节ASCII示例 const val PROTOCOL_VERSION = "02" // 2字节ASCII示例 private val ASCII_CHARSET = Charset.forName("US-ASCII") /** * 构建符合协议要求的完整消息字节数组 * @param actualMessage 实际业务消息内容(ASCII编码字符串) * @return 完整的消息字节数组,可直接发送 * @throws IllegalArgumentException 当消息大小超过2字节表示范围时抛出 */ fun buildEcrMessage(actualMessage: String): ByteArray { // 1. 转换各固定字段为ASCII字节数组 val directionBytes = DIRECTION_ID.toByteArray(ASCII_CHARSET) val variantBytes = PROTOCOL_VARIANT.toByteArray(ASCII_CHARSET) val versionBytes = PROTOCOL_VERSION.toByteArray(ASCII_CHARSET) val actualMsgBytes = actualMessage.toByteArray(ASCII_CHARSET) // 2. 计算协议要求的消息大小(不含头部2字节长度字段) val messageSize = directionBytes.size + variantBytes.size + versionBytes.size + actualMsgBytes.size require(messageSize in 0..65535) { "消息大小超过2字节表示范围,最大支持65535字节(实际消息最大65528字节)" } // 3. 将消息大小编码为2字节大端二进制 val sizeBuffer = ByteBuffer.allocate(2).order(ByteOrder.BIG_ENDIAN) sizeBuffer.putShort(messageSize.toShort()) val sizeBytes = sizeBuffer.array() // 4. 拼接所有部分得到完整消息 return sizeBytes + directionBytes + variantBytes + versionBytes + actualMsgBytes } // 发送消息的示例方法 fun sendMessageToEcr(messageBytes: ByteArray) { // 替换为你的实际发送逻辑(比如串口/网络输出流写入) // 示例:serialPort.outputStream.write(messageBytes) } // 测试调用示例 fun testSmallMessage() { val actualMsg = "E/000" val fullMessage = buildEcrMessage(actualMsg) sendMessageToEcr(fullMessage) } fun testLargeMessage() { // 构造接近最大长度的测试消息 val largeMsg = "X".repeat(65528) val fullMessage = buildEcrMessage(largeMsg) sendMessageToEcr(fullMessage) }
关键注意事项
- 强制使用ASCII编码:所有字符串转字节时必须指定
US-ASCII,避免UTF-8编码产生多字节字符,导致长度计算错误 - 严格控制消息大小:如果业务需要发送超过65528字节的消息,需确认协议是否支持分片,否则需做截断或与设备方协商调整
- 字节序适配:如果设备要求小端(Little-Endian),只需将
ByteOrder.BIG_ENDIAN改为ByteOrder.LITTLE_ENDIAN即可 - 禁止字符串表示长度:绝对不能将消息大小转为十进制字符串再拼接,必须直接用二进制字节数组实现头部长度字段
内容的提问来源于stack exchange,提问作者Pointyhat
相关产品推荐
相关产品推荐

