我正在尝试在NIO SocketChannel和阻塞IO套接字之间序列化Object。由于我不能在NIO上使用Serializable / writeObject,我想编写代码将对象序列化为ByteArrayOutputStream然后发送数组长度后跟数组。
发件人功能
public void writeObject(Object obj) throws IOException{
ByteArrayOutputStream serializedObj = new ByteArrayOutputStream();
ObjectOutputStream writer = new ObjectOutputStream(serializedObj);
writer.writeUnshared(obj);
ByteBuffer size = ByteBuffer.allocate(4).putInt(serializedObj.toByteArray().length);
this.getSocket().write(size);
this.getSocket().write(ByteBuffer.wrap(serializedObj.toByteArray()));
}
和接收者是:
public Object readObject(){
try {
//Leggi dimensione totale pacchetto
byte[] dimension = new byte[4];
int byteRead = 0;
while(byteRead < 4) {
byteRead += this.getInputStream().read(dimension, byteRead, 4 - byteRead);
}
int size = ByteBuffer.wrap(dimension).getInt(); /* (*) */
System.out.println(size);
byte[] object = new byte[size];
while(size > 0){
size -= this.getInputStream().read(object);
}
InputStream in = new ByteArrayInputStream(object, 0, object.length);
ObjectInputStream ois = new ObjectInputStream(in);
Object res = ois.readUnshared();
ois.close();
return res;
} catch (IOException | ClassNotFoundException e) {
return null;
}
}
问题是当serializedObj.toByteArray()时,size(*)总是等于-1393754107。我的测试中的长度是316。 我不明白为什么投射不能正常工作。
答案 0 :(得分:1)
this.getSocket().write(size);
this.getSocket().write(ByteBuffer.wrap(serializedObj.toByteArray()));
如果getSocket()
的结果在非阻止模式下为SocketChannel
,则问题出在此处。您没有检查write()
的结果。在非阻塞模式下,它可以写入少于ByteBuffer
中剩余的字节数;实际上它可以写零个字节。
所以你没有写出你认为自己正在写的所有数据,所以另一端超出并读取下一个长度字作为正在写入的数据的一部分,并将下一个数据的一部分读取为下一个长度的单词,得到错误的答案。我很惊讶它早些时候没有barf。事实上它可能会这样做,但是你忽视IOExceptions
的令人遗憾的做法掩盖了它。不要这样做。 记录他们。
所以你需要循环,直到所有请求的数据都被写入,如果任何write()
返回零,你需要在OP_WRITE上选择直到它被触发,这会给你的代码带来相当大的复杂性,因为你必须返回选择循环,同时记住有一个未完成的ByteBuffer
,其中还有待写入的数据。当你获得OP_WRITE并完成写入时,你必须注销对OP_WRITE的兴趣,因为在write()
返回零之后它只是感兴趣。
注意您的代码中没有强制转换。
答案 1 :(得分:0)
问题是write()总是返回0。发生这种情况是因为在write()之前没有翻转缓冲区。