【问题标题】:Reading XML from Socket streams从 Socket 流中读取 XML
【发布时间】:2014-06-07 13:45:06
【问题描述】:

我有一个套接字连接,我希望通过它发送 XML 消息,使用 DOM 类 (DocumentBuilder et al) 来构造和解析 XML 文档,但我不知道在等待消息时处理关闭连接的好方法。

消息可能以任何顺序到达(即我不知道哪一端将发送下一条消息)并且我想避免任何基于sleep() 的解决方案用于阻止 - 我想学习如何做到这一点比那更好。我尝试了以下设置的多种变体:

class XML {
    static Message readMessage(InputStream s) {
       // try/catch and setup of builder omitted for brevity

       Document doc = builder.parse(s);

       // Traverse the doc tree to built the Message, then return
    }

    static void writeMessage(Message m, OutputStream s) {
        // Build a Document from the provided message

        Transformer transformer = tf.newTransformer();
        transformer.transform(new DOMSource(doc), new StreamResult(output));
        output.write('\n');
    }
}

class Connection {
    Socket s;
    Thread reader;

    Connection(Socket s) {
        this.s = s;

        reader = new Thread(new Runnable() {
            void run() {
                while (true) {
                    Message m = XML.readMessage(s.getInputStream());

                    // Handle the message here, e.g. by dispatching UI events
                }
            }
        });
        reader.start();
    }

    void sendMessage(Message msg) {
        XML.writeMessage(msg, s.getOutputStream());
    }

    void close() {
        if (reader.isAlive()) {
            reader.interrupt();
        }
        if (!socket.isClosed()) {
            socket.close();
        }
    }
}

我可以让服务器和客户端正常连接并开始等待消息,只需在每一侧用各自的Socket 实例化一个Connection。发送和接收消息也可以正常工作。

但是,我想不出一种干净地关闭连接的方法。我不断遇到各种异常。确切的消息取决于上面骨架中的实现细节,但据我所知,它们都是在套接字关闭后读取线程尝试读取消息时出现的,或者在读取线程仍在读取时套接字关闭时出现。我总是使用上面的close() 方法关闭连接。

由于我只是将流传递给 DOM 库,因此我无法精确控制它的读取方式。

如何正确结束连接,而不会导致阅读器线程出现异常?

【问题讨论】:

  • 您是要打开一个连接并拥有一个请求/响应,还是要在同一个连接上拥有多个请求/响应?
  • @BrettOkken:我在“对话”期间保持连接打开,所以我期待双方发送多条消息,没有特定的顺序,直到一方发送一个特殊的“断开”消息并关闭连接。此时,双方都应关闭其套接字并退出。
  • 那么您可能必须定义一个包装 xml 的协议,以清楚地定义各个 xml 文档的开始和结束。然后,您需要在解析 xml 时包装套接字输入流并模拟文件结尾(即输入流读取返回 -1)。同样,发送方需要包装输出流并发送此特殊标记以指示消息结束。
  • 在你的close()方法中,中断reader线程后,调用reader.join()。这应该让它有机会干净地退出并停止所有操作,前提是您不只是忽略循环中的 IOExceptions。 (通常在 Java 中处理 I/O 循环的最好方法是让 try/catch 在循环之外,这样循环会在遇到错误时自动终止。)
  • 你也可以看看这个其他帖子,所以关注“优雅关机”:Java Sockets and Dropped Connections。也许这足以摆脱错误。

标签: java multithreading sockets


【解决方案1】:

我设法产生了一些真正有效的东西,即使我对这个解决方案有多丑陋感到畏缩。这就是我所做的(因为它有效,我将它作为答案发布):

InputStreamReader reader = new InputStreamReader(stream);
BufferedReader breader = new BufferedReader(reader);
String xml = breader.readLine();
InputStream newstream = new ByteArrayInputStream(xml.getBytes(Charset.forName("UTF-8")));

Document doc = builder.parse(newstream);

也就是说,我必须实例化四个额外的对象,最终得到一个与我开始使用的对象类型完全相同的对象,只是为了让解析器意识到它拥有整个文档。

如果有更好的方法来做到这一点,我很乐意接受并投票支持任何描述它的答案。

【讨论】:

  • 我已经回答了类似的问题here。我认为它提供了比这个答案更清洁的解决方案。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-07-19
  • 2017-07-15
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多