从Socket流中读取XML

时间:2014-06-07 13:45:07

标签: java multithreading sockets

我有一个套接字连接,我想发送XML消息,使用DOM类(DocumentBuilder )来构造和解析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();
        }
    }
}

我可以让服务器和客户端相互连接,然后开始等待消息,只需在每一侧使用相应的Connection实例化Socket即可。发送和接收消息也可以正常工作。

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

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

如何正确结束连接,而不会在阅读器线程中引起异常?

1 个答案:

答案 0 :(得分: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);

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

如果有更好的方法可以做到这一点,我很乐意接受并赞成任何描述它的答案。