为什么java.io.InputStream的read()与read(byte[] b)方法返回值存在差异?
不需要用int[]存储帧数据,你遇到的负数问题是Java byte类型的有符号特性导致的,不是读取错误。
- Java 中
byte是8位有符号类型,取值范围为-128 ~ 127,当读取到的字节值大于127时,存为byte就会显示为负数。只需要做一次无符号转换byteValue & 0xFF,就能得到0~255区间的对应int值,完全符合RFC6455的帧解析要求。
比如你读到的-127做-127 & 0xFF运算后结果就是129,-122 & 0xFF运算后结果就是134,和预期一致。 - 你提到的
read(byte[] b)方法的问题:注意该方法不保证一次读完整个WebSocket帧,也不保证读满你传入的数组长度,它的返回值是本次实际读取到的字节数,你必须根据返回值累计读取,直到拿到你需要的完整数据,这是TCP流协议的特性,和存储用byte[]还是int[]无关。 - 你写的循环逻辑存在两处问题:
- 只适配了payload长度≤125的场景,RFC6455规定如果第二个字节的低7位是126,后续还要读2字节作为实际payload长度;如果是127,后续还要读8字节作为实际payload长度,你的逻辑没有处理这两种情况。
- 循环终止条件错误:客户端发往服务端的帧必须带掩码,你还要额外读4字节的掩码key,payload读取完成的位置是
帧头长度 + 掩码长度 + payload长度,不是等于payload长度就停止。
以下是简化的正确实现示例:
// 完全可以用byte[]存储帧数据 byte[] frame = new byte[1024]; // 先读2字节基础帧头 int readLen = inputStream.read(frame, 0, 2); // 实际使用时要判断readLen是否为2,处理读不够的边界情况,示例省略校验 int firstByte = frame[0] & 0xFF; // 得到129这类无符号值 int secondByte = frame[1] & 0xFF; // 得到134这类无符号值 boolean hasMask = (secondByte & 0x80) != 0; int payloadLen = secondByte & 0x7F; int realPayloadLen = payloadLen; int headLen = 2; // 处理扩展长度 if (payloadLen == 126) { inputStream.read(frame, 2, 2); realPayloadLen = ((frame[2] & 0xFF) << 8) | (frame[3] & 0xFF); headLen += 2; } else if (payloadLen == 127) { // 处理8字节扩展长度逻辑,示例省略 headLen +=8; } // 读4字节掩码 byte[] mask = new byte[4]; if (hasMask) { inputStream.read(frame, headLen, 4); mask[0] = frame[headLen]; mask[1] = frame[headLen+1]; mask[2] = frame[headLen+2]; mask[3] = frame[headLen+3]; headLen +=4; } // 读payload内容 inputStream.read(frame, headLen, realPayloadLen); // 用掩码异或解密得到原始数据 byte[] payload = new byte[realPayloadLen]; for (int i = 0; i < realPayloadLen; i++) { payload[i] = (byte) (frame[headLen + i] ^ mask[i %4]); } // 此时payload就是客户端发送的原始数据,比如"abcdef"对应的字节数组
内容的提问来源于stack exchange,提问作者Jason Rich Darmawan
相关产品推荐
相关产品推荐

