【问题标题】:ObjectInputStream.readObject() hangs forever during the process of socket communicationObjectInputStream.readObject() 在套接字通信过程中永远挂起
【发布时间】:2012-01-10 05:33:01
【问题描述】:

我在linux系统上遇到过socket通信的问题,通信过程如下:客户端发送消息请求服务器做计算任务,任务完成后等待服务器返回结果消息。

但是如果任务花费了很长时间,比如大约40分钟,客户端会挂机等待结果消息,即使从服务器端,结果消息已经写入套接字以响应客户端,但是如果任务花费的时间很少,例如一分钟,它通常可以收到结果消息。此外,此问题仅发生在客户环境中,通信过程在我们的测试环境中表现正常。

我怀疑这个问题的原因是客户环境和测试环境之间的socket默认超时值不同,但是这两个环境,客户端和服务器上的以下值是相同的。

getSoTimeout:0
getReceiveBufferSize:43690
getSendBufferSize:8192
getSoLinger:-1
getTrafficClass:0
getKeepAlive:false
getTcpNoDelay:false

客户端上的代码如下:

Message msg = null;
ObjectInputStream in = client.getClient().getInputStream();
//if no message readObject() will hang here
while ( true ) {
  try {
   Object recObject = in.readObject();
   System.out.println("Client received msg.");
   msg = (Message)recObject; 
   return msg;
       }catch (Exception e) {
    e.printStackTrace();
    return null;
   }
}

服务器上的代码是这样的,

ObjectOutputStream socketOutStream = getSocketOutputStream();
try {
  MessageJobComplete msgJobComplete = new MessageJobComplete(reportFile, outputFile );
  socketOutStream.writeObject(msgJobComplete);
  }catch(Exception e) {
    e.printStackTrace();
  }

为了解决这个问题,我添加了flush和reset方法,但是问题依旧存在:

ObjectOutputStream socketOutStream = getSocketOutputStream();
try {
   MessageJobComplete msgJobComplete = new MessageJobComplete(reportFile, outputFile );
   socketOutStream.flush();
   logger.debug("AbstractJob#reply to the socket");
   socketOutStream.writeObject(msgJobComplete);
   socketOutStream.reset();
   socketOutStream.flush();
   logger.debug("AbstractJob#after Flush Reply");
 }catch(Exception e) {
    e.printStackTrace();
            logger.error("Exception when sending MessageJobComplete."+e.getMessage());
 }

所以有谁知道我应该采取什么下一步措施来解决这个问题。 我猜是环境设置的原因,但不知道是什么环境因素会影响socket通信?

而socket使用Tcp/Ip协议进行通信,问题与任务时间长有关,那么tcp的哪些值会影响socket通信的超时呢?

在对日志进行分析后,我发现在将消息写入套接字后,没有抛出/捕获异常。但总是在 15 分钟后,用于接受来自客户端的请求的服务器端的 objectInputStream.readObject() 代码 sn-p 中出现异常。但是socket.getSoTimeout的值是0,所以抛出了一个Timed out Exception就很奇怪了。

{2012-01-09  17:44:13,908} ERROR java.net.SocketException: Connection timed out
   at java.net.SocketInputStream.socketRead0(Native Method)
   at java.net.SocketInputStream.read(SocketInputStream.java:146)
   at sun.security.ssl.InputRecord.readFully(InputRecord.java:312)
   at sun.security.ssl.InputRecord.read(InputRecord.java:350)
   at sun.security.ssl.SSLSocketImpl.readRecord(SSLSocketImpl.java:809)
   at sun.security.ssl.SSLSocketImpl.readDataRecord(SSLSocketImpl.java:766)
   at sun.security.ssl.AppInputStream.read(AppInputStream.java:94)
   at sun.security.ssl.AppInputStream.read(AppInputStream.java:69)
   at java.io.ObjectInputStream$PeekInputStream.peek(ObjectInputStream.java:2265)
   at java.io.ObjectInputStream$BlockDataInputStream.peek(ObjectInputStream.java:2558)
   at  java.io.ObjectInputStream$BlockDataInputStream.peekByte(ObjectInputStream.java:2568)
   at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1314)
   at java.io.ObjectInputStream.readObject(ObjectInputStream.java:368)

那么为什么会抛出 Connection Timed out 异常呢?

【问题讨论】:

  • 如果客户端没有收到,很可能是服务器没有发送。我会确认您会看到客户端未收到的已发送对象的日志消息。
  • 谢谢,但是日志显示对象已经发送,并没有检测到任何异常,只是客户端挂了,你知道如何检查吗? {2012-01-09 19:28:16,416} 调试 AbstractJob#reply 到套接字 {2012-01-09 19:28:16,422} 调试 AbstractJob#after Flush 回复
  • printStackTrace 不会出现在日志中,它们会被写入控制台。如果您没有将控制台写入文件,我会确保将异常写入日志。
  • 官方代码中有异常抛出时的日志动作,这里没有记录错误,但是对象写入socket后,在ObjectInputStream.readObject()处抛出异常在服务器端也等待客户端请求,请查看我修改的帖子。

标签: java linux sockets objectoutputstream objectinputstream


【解决方案1】:

这个问题解决了。使用tcpdump 捕获消息流。我发现在应用程序级别,ObjectOutputStream.writeObject() 方法被调用,在 tcp 级别,多次找到[TCP ReTransmission]。

所以,我断定连接可能已经死了,尽管使用netstat -an 命令,tcp 连接状态仍然是ESTABLISHED。

所以我编写了一个测试应用程序来定期发送测试消息作为来自服务器的心跳消息。然后这个问题就消失了。

【讨论】:

    【解决方案2】:

    java.io.InputStream 的 read() 方法是阻塞调用。这意味着如果在流中没有数据可供读取时调用它们,它们会“永远”等待。

    如果服务器没有响应,这完全是预期的行为,并且根据 javadoc 中发布的合同。

    如果您想要非阻塞读取,请使用 java.nio.* 类。

    【讨论】:

    • 感谢您的回复,但实际上服务器已将对象写入套接字以响应客户端。所以我不知道为什么客户端收不到。
    • 如果客户端没有收到,很可能是服务器没有发送。我会确认您会看到客户端未收到的已发送对象的日志消息。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-09-20
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多