如何从Java发送四字节头并在Python中读取?
我正在尝试通过TCP套接字从一个Java应用程序发送消息,并在Python 2.7中读取它。我希望前4个字节用来指定消息的长度,所以我可以在Python端这样做:
header = socket.recv(4)
message_length = struct.unpack(">L",header)
message = socket.recv(message_length)
在Java端:
out = new PrintWriter(new BufferedWriter(new StreamWriter(socket.getOutputStream())),true);
byte[] bytes = ByteBuffer.allocate(4).putInt(message_length).array();
String header = new String(bytes, Charset.forName("UTF-8"));
String message_w_header = header.concat(message);
out.print(message_w_header);
这个方法在某些消息长度(比如10个字符和102个字符)下是有效的,但在其他情况下就失败了(例如1017个字符)。在失败的情况下,如果我输出每个字节的值,我得到:
Java:
Bytes 0 0 3 -7
Length 1017
Hex string 3f9
Python:
Bytes 0 0 3 -17
Length 1007
Hex string \x00\x00\x03\xef
我觉得这可能和Java中的有符号字节以及Python中的无符号字节有关,但我不知道该怎么做才能让它正常工作。
1 个回答
1
问题出在Java这边——b'\x03\xf9'
不是有效的utf-8字节序列:
>>> b'\x03\xf9'.decode('utf-8')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
UnicodeDecodeError: 'utf-8' codec can't decode byte 0xf9 in position 1: invalid start byte
看起来 new String(bytes, Charset.forName("UTF-8"));
使用了 'replace'
错误处理方式,b'\xef'
是三个字节中第一个字节,代表的是utf-8编码中的 '\ufffd'
Unicode替代字符:
>>> b'\x03\xf9'.decode('utf-8', 'replace').encode('utf-8')
b'\x03\xef\xbf\xbd'
这就是为什么你在Python中收到的是 b'\x03\xef'
而不是 b'\x03\xf9'
。
要解决这个问题,建议在Java中发送字节而不是Unicode文本。
另外,sock.recv(n)
可能会返回少于 n
个字节。如果套接字是阻塞的,你可以使用 file = sock.makefile('rb')
创建一个类似文件的对象,然后调用 file.read(n)
来准确读取 n
个字节。