【问题标题】:EOFException while constructing an ObjectInputStream构造 ObjectInputStream 时出现 EOFException
【发布时间】:2012-09-10 18:51:45
【问题描述】:

我正在尝试在服务器端(Tomcat 6.0.29)序列化 HashMap 并在客户端 android 应用程序上取消实现它。

我第一次尝试这个时遇到了 StreamCorruptedException,但是在我使用干净的 doGet/doPost-Method 创建了一个全新的 servlet 之后,我设法解决了这个问题,不幸的是我得到了一个 EOFException。

所以首先我发布我的代码,然后我将详细解释我的问题:

服务器端代码(这将在 doPost 或 doGet 被调用时执行):

    if ("object".equals(request.getParameter("format")))
        {

            trace.log("Before Serializing");
            ObjectOutputStream out = null;
            try
            {
                out = new ObjectOutputStream(response.getOutputStream());
                out.writeObject(outerTable);
                trace.log("after Serializing");
            }
            finally
            {
                if(out != null)
                {
                    try
                    {
                        out.flush();
                        out.close();
                    }
                    catch(Exception ex)
                    {
                        trace.log("Exception has been thrown "+ex.getMessage());
                    }
                }
            }
            trace.log("after closing stream");
        }
        else
        {
            PrintWriter out = response.getWriter();
            out.println(outerTable.toString());
        }

outerTable 是在此代码块之前创建的 HashMap,它始终具有正确的值。

在服务器端没有抛出任何错误。 当我尝试使用参数“?format = object”访问我的网络浏览器中的servlet时,我得到 整个序列化字符串。

Android-Application的脚本(该方法在特定按钮被按下后执行http请求):

    private synchronized void performNetworkAccess()
    {
  Runnable run = new Runnable()
  {
    @Override
    public void run()
    {
    System.out.println("in run()-Method");
    ObjectInputStream objInput = null;
    try
    {
        URL url = new URL(myWebsite);
        URLConnection con = url.openConnection();
        InputStream in = con.getInputStream(); 
        objInput = new ObjectInputStream(in);

        outerTable = (HashMap<String, HashMap<String, Object>>)              objInput.readObject();
        synchronized (monitor)
        {
        monitor.notifyAll();
        }
    }
    catch (Exception ex)
    {
        ex.printStackTrace();
    }
    finally
    {
        if (objInput != null)
        {
        try
        {
            objInput.close();
        }
        catch (IOException e)
        {
            e.printStackTrace();
        }
        }
    }

    }

};
Thread t1 = new Thread(run);
t1.start();
}

调用ObjectInputStream类的构造函数后立即发生异常。

堆栈跟踪:

  • W/System.err(1797): java.io.EOFException
  • W/System.err(1797):在 libcore.io.Streams.readFully(Streams.java:83)
  • W/System.err(1797):在 java.io.DataInputStream.readShort(DataInputStream.java:169)
  • W/System.err(1797):在 java.io.ObjectInputStream.readStreamHeader(ObjectInputStream.java:2102)
  • W/System.err(1797):在 java.io.ObjectInputStream.(ObjectInputStream.java:372)
  • W/System.err(1797):在 de.tesla.jtheseuscontactsync.ContactSync$4.run(ContactSync.java:602)
  • W/System.err(1797):在 java.lang.Thread.run(Thread.java:856)

我已经发现这个问题的出现次数很多,但是我没有看到其中一个在调用 readObject() 之前发生了这个错误。

我已经尝试过的事情:

  • 在构造 ObjectOutputStream 后不久添加 out.flush()
  • 删除 out.flush() 和/或 out.close() 方法
  • 使用 BufferedInput-/-OutputStream 包装 Input-/OutputStream
  • 将流作为文件检索并尝试取消实现该文件

奇怪的是,当我第一次通过 FileOutputStream 将对象写入文件并通过 Url.openStream() 从客户端访问该文件(在服务器上)时,ObjectOutputStream 可以完美地读取该对象,不幸的是,这意味着很多当多个用户尝试同时检索此对象时会出现麻烦...

我希望你能帮我解决这个问题:)

【问题讨论】:

  • 由 ObjectOutputStream 创建的序列化 Java 对象只能在使用相同实现的环境中反序列化。即使更改 JRE 版本也可以防止反序列化。因此使用序列化来传输复杂的非自写对象结构中的数据是一个非常糟糕的主意。
  • 确实我要回答这个问题,但是我看到从文件中完成反序列化是有效的......所以改变了我的帖子:)
  • @Robert 这完全不正确。 Java 序列化机制与所有 JRE 兼容,一直到 Java 1.1。
  • @EJP 机制定义明确,但不属于 JRE 的类。序列化包含 Java API 中从未描述过的私有字段的数据,因此序列化可能会失败,尤其是在使用不同的 Java 运行时环境(例如 Oracle JRE、Android 等)时。无论如何,Java 序列化会创建臃肿的数据并且非常不安全。

标签: java android jakarta-ee


【解决方案1】:

servlet 出现某种异常,甚至还没有构造 ObjectOutputStream。在客户端尝试构造 ObjectInputStream 之前,您需要检查 200 的响应代码。

【讨论】:

  • 感谢您的建议,这确实是一个值得在 %) 之前检查的值
【解决方案2】:

一些疯狂的想法...尝试在您的回复中添加Content-Length 标头。可能意味着将您的字节写入 ByteArrayOutputStream、测量大小、设置标头然后才写入响应...但可能是 URLConnection 过早切断连接的原因...

ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObejctOutputStream oos = new ObejctOutputStream(baos);
oos.write(...);

byte[] data = baos.toByteArray();

response.setHeader("Content-Length", "" + data.length);
response.getOutputStream().write(data);

Java 序列化

@Robert 的评论是对的。 Java 序列化并非旨在用于不同环境之间的通信。如果您在 JVM 和 Dalvik 之间工作,则更多。所以另一个框架会更合适(带有 XML 或 JSON 的东西可能会很好,甚至用 GZip[]Stream 包装它)。

【讨论】:

  • servlet 容器已经做到了这一点。没有必要再做一次。
  • @EJP servlet 容器添加内容长度标头?基于响应体实际大小?如果是这样,它应该缓冲整个响应......这似乎不是一个很好的默认行为......
  • 嗯……太好了!我有点惊讶,但很高兴!无论如何,如果事情是明确的(明确的内容长度),那就更好了。
猜你喜欢
  • 2014-02-11
  • 2013-02-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-09-22
  • 2016-04-10
  • 1970-01-01
相关资源
最近更新 更多