我目前正在编写一个与Chrome扩展程序通信的Java程序。我需要实现Chrome本机消息传递协议才能进行通信。谷歌Chrome文档说:
...每条消息都使用JSON,UTF-8编码进行序列化,并以本机字节顺序的32位消息长度开头。 (Source)
我试图用Java实现这个,但是当我的消息有一定长度时我遇到了问题,即使我的实现应该是正确的。这是我目前的实施,基于早期的SO-answers&问题(例如here):
// read the message size from Chrome. This part works correctly.
public static int getInt(char[] bytes) {
return (bytes[3]<<24) & 0xff000000|
(bytes[2]<<16) & 0x00ff0000|
(bytes[1]<< 8) & 0x0000ff00|
(bytes[0]<< 0) & 0x000000ff;
}
// transform the length into the 32-bit message length.
// This part works for small numbers, but does not work for length 2269 for example.
public static String getBytes(int length) {
return String.format("%c%c%c%c",
(char) ( length & 0xFF),
(char) ((length>>8) & 0xFF),
(char) ((length>>16) & 0xFF),
(char) ((length>>24) & 0xFF));
}
似乎问题在于java实现chars的方式。我期待正常的字符,就像在C中一样。在实践中,似乎Java有时会将这些字符转换为unicode-chars(至少,到目前为止,这是我的怀疑)。这反映在以下输出(通过管道传输到xxd以显示实际字节)中,来自java程序的长度为2269:
0000000: c39d 0800 00 .....
但是预期的输出(使用python):
import struct
struct.pack('I', 2269)
# outputs in interactive mode: '\xdd\x08\x00\x00'
这到底发生了什么?为什么Java将我的“0xDD”转换为“0xC39D”?如何让我的getBytes
函数代表Chrome Native Messaging的预期输入?使用其他语言不是一种选择。
答案 0 :(得分:4)
Java中的Chars会自动转换为unicode。此用例的正确类型为byte
,它不会自动转换并保留正确的值。因此,Chrome Native Messaging协议的正确实现如下:
public static byte[] getBytes(int length) {
byte[] bytes = new byte[4];
bytes[0] = (byte) ( length & 0xFF);
bytes[1] = (byte) ((length>>8) & 0xFF);
bytes[2] = (byte) ((length>>16) & 0xFF);
bytes[3] = (byte) ((length>>24) & 0xFF);
return bytes;
}
除了这种方法之外,还需要注意不要在计算长度字节和输出之间的任何地方使用String。输出到System.out
可以完成如下:
try {
System.out.write(getBytes(message.length()));
} catch (IOException ex) {
ex.printStackTrace();
}