读取从C#客户端发送的ObjectInputStream时出现“StreamCorruptedException:invalid stream header”
问题
我正在尝试从C#客户端向此Java Server发送protobuf消息,但是我得到了以下exception:
java.io.StreamCorruptedException: invalid stream header: 0A290A08 java.io.StreamCorruptedException: invalid stream header: 0A290A08 at java.io.ObjectInputStream.readStreamHeader(Unknown Source) at java.io.ObjectInputStream.(Unknown Source)
说实话,我有点不知所措。 任何帮助表示赞赏。 谢谢!
- Java服务器
public ControllerThread(Socket s){ this.s = s; try { this.objectInputStream = new ObjectInputStream(s.getInputStream()); byte size = objectInputStream.readByte();System.out.println("Server: BYTES SIZE:" + size); byte[] bytes = new byte[size]; objectInputStream.readFully(bytes); AddressBook adb = AddressBook.parseFrom(bytes); System.out.println("Server: Addressbook:" + adb.getPersonCount()); } catch (IOException e) { System.out.println("Server: BufferedReader oder PrintWriter von ThermoClient konnte nicht erstellt werden"); e.printStackTrace(); } } }
C#代码
public AddressBook InitializeAdressBook() { Person newContact = new Person(); AddressBook addressBookBuilder = new AddressBook(); Person john = new Person(); //john.id=1234; john.name="John Doe"; john.email="jdoe@example.com"; Person.PhoneNumber nr = new Person.PhoneNumber(); nr.number="5554321"; john.phone.Add(nr); addressBookBuilder.person.Add(john); TextBox.Text += ("Client: Initialisiert? " + addressBookBuilder.ToString()) + "\t" + "\n"; TextBox.Text += " Erster Person " + addressBookBuilder.person.First().name + "\t" + "\n"; return addressBookBuilder; }
c#OutputStream
public void SendMessage(Stream ns, byte[] msg) { byte size = (byte)msg.Length; try { ns.WriteByte(size); ns.Write(msg, 0, msg.Length); ns.Flush(); ns.Close(); } catch (ArgumentNullException ane) { TextBox.Text += "ArgumentNullException : {0}" + ane.ToString(); } catch (Exception e) { TextBox.Text += ("Unexpected exception : {0}" + e.ToString()); } }
tldr; 问题是使用ObjectInputStream (Java)
, 它只适用于ObjectOutputStream (Java)
生成的数据。 在这种情况下,正在生成StreamCorruptedException
,因为流被赋予了非ObjectOutputStream (Java)
生成的无效数据。
而是使用DataInputStream (Java)
读取BinaryWriter (C#)
生成的数据。 这两者都只支持“原始”类型。 只要使用正确的字节顺序并根据需要执行符号填充 :整数,浮点数,双精度(但不是十进制数)和字节数组都可以通过这种方式安全地发送。
ObjectInputStream (Java)
:
ObjectInputStream反序列化以前使用ObjectOutputStream [在Java中]编写的原始数据和对象。
DataInputSteam (Java)
:
数据输入流允许应用程序从基础输入流中读取原语[..]类型 。
BinaryWriter (C#)
:
将二进制中的原始类型写入流并支持以特定编码写入字符串。
笔记:
- DataInputSteam(Java)是big-endian ,但BinaryWriter(C#)必须转换为big-endian 。
- 传递
char/character
,short
,int
,long
,float
和double
数据类型时没有问题(除了字节顺序),因为它们在C#和Java中具有相同的签名性质和按位表示。 -
byte (Java, signed)
和byte (C#, unsigned)
可能会出现签名问题。 值得庆幸的是,如果给定适当的byte[] (Java or C#)
, ProtocolBuffer将自动处理此问题 。 - 由于轻微的编码差异,字符串可以提供额外的乐趣。
所以用这个c#OutputStream方法和DataInputStream (Java)代替ObjectOutputSteam它没有任何问题
public void SendEndianBinaryMsg(Stream ns, byte[] msg) { byte size = (byte)msg.Length; try { EndianBinaryWriter writer = new EndianBinaryWriter(EndianBitConverter.Big, ns); writer.Write(size); writer.Write(msg); writer.Flush(); ns.Close(); } catch (ArgumentNullException ane) { TextBox.Text += "ArgumentNullException : {0}" + ane.ToString(); } catch (Exception e) { TextBox.Text += ("Unexpected exception : {0}" + e.ToString()); } }
笔记:
我从MiscUtil获得EndianBinaryWriter和EndianBitConverter 。