这两天做项目遇到奇怪的BUG,使用JDK socket编程进行服务器和客户端进行通信,读取输入输出流使用了ObjectInputStream
和ObjectOutputStream
,结果两边都卡住没反应,疑似客户端出现了点问题,后面发现在服务端的getInputStream()的一行代码处也产生了阻塞,当时的写法:
//服务端
ObjectInputStream objectInputStream = new ObjectInputStream(socket.getInputStream()); //在此处阻塞
ObjectOutputStream objectOutputStream = new ObjectOutputStream(socket.getOutputStream());
//客户端
ObjectInputStream in = new ObjectInputStream(socket.getInputStream()); //在此处阻塞
ObjectOutputStream out = new ObjectOutputStream((socket.getOutputStream()));
第一遍解决是通过调换服务端的输入输出流两行代码的位置,可以解决
后面发现只要调换客户端的输入输出流两行代码的位置,无论服务端两行代码的位置怎么换都可以。
//客户端修改如下
ObjectOutputStream out = new ObjectOutputStream((socket.getOutputStream()));
ObjectInputStream in = new ObjectInputStream(socket.getInputStream());
初步猜想是,当在客户端使用错误的方式(先打开输入流再打开输出流),客户端会在输入流这里产生阻塞,而服务端由于还没有收到客户端的请求,等待客户端发送请求,也在socket.getInputStream()
处产生了阻塞而无法处理消息。结果就是客户端也一直阻塞在socket.getInputStram()
方法上,两边都发生了阻塞。
查阅资料发现可能是ObjectOutputStream
和ObjectInputStream
的问题,使用原生字节流(InputStream
和OutputStream
)做了实验,发现确实与原生字节流代码顺序无关,那么大概率就是ObjectOutputStream
和ObjectInputStream
有关。在stackoverflow上看到有一篇问答,也提到了ObjectOutputStream
和ObjectInputStream
的问题getInputStream blocks?
yes, this question has been asked many times before. the object stream format has a header, and the ObjectInputStream reads that header on construction. therefore, it is blocking until it receives that header over the socket. assuming your client/server code looks similar, after you construct the ObjectOutputStream, flush it. this will force the header data over the wire.
对ObjectOutputStream
和ObjectInputStream
加上测试之后发现确实与客户端new出ObjectOutputStream
和ObjectInputStream
的顺序有关,先output再input则正常使用。
最后stackoverflow上找到解答 java - new ObjectInputStream() blocks - Stack Overflow
You need to create the
ObjectOutputStream
before theObjectInputStream
at both sides of the connection(!). When theObjectInputStream
is created, it tries to read the object stream header from theInputStream
. So if theObjectOutputStream
on the other side hasn't been created yet there is no object stream header to read, and it will block indefinitely.Or phrased differently: If both sides first construct the
ObjectInputStream
, both will block trying to read the object stream header, which won't be written until theObjectOutputStream
has been created (on the other side of the line); which will never happen because both sides are blocked in the constructor ofObjectInputStream
.This can be inferred from the Javadoc of ObjectInputStream(InputStream in):
java文档内容如下:
Creates an ObjectInputStream that reads from the specified InputStream. A serialization stream header is read from the stream and verified. This constructor will block until the corresponding ObjectOutputStream has written and flushed the header
总结:
当错误使用new ObjectOutputStream(OutputStream out)
和new ObjectInputStream(InputStream out)
的顺序时,会导致服务端和客户端都阻塞等待对方发消息从而产生死锁。