2

I'm trying to write a client-server application in Java with an XML-based protocol. But I have a great problem!

See this part of client code:

InputStream incoming = skt.getInputStream(); //I get Stream from Socket.
OutputStream out = skt.getOutputStream();

[...]

XMLSerializer serializer = new XMLSerializer();
//This create an XML document.
tosend = WUTPClientWriter.createMessage100(projectid, cpuclock, cpunumber);
serializer.setOutputByteStream(out);
serializer.serialize(tosend);

At this point server fall in deadlock. It wait for EOF but I can't send it because if I use

out.close();

or

skt.shutdownOutput();

I close the Socket and I must keep this connection alive.

I can't send '\0' becouse I get Parse Error in the server.

How can I do it? Can I "close" the output stream without closing the socket?

RESOLVED I've created new class XMLStreamOutput and XMLStreamInput with advanced Stream gesture.

2
  • 3
    How can you do what? You haven't really described what's going wrong. You also haven't mentioned parsing, other than in the title... Commented Sep 15, 2009 at 16:02
  • Oh sorry :( however the problem is that server go in deadlock. Wait for client inputstream closing but i cant close it. Commented Sep 15, 2009 at 16:18

2 Answers 2

6

I've resolved with this four class:

1)

public class XMLOutputStream extends  ByteArrayOutputStream {

 private DataOutputStream outchannel;

 public XMLOutputStream(OutputStream outchannel) {
     super();
     this.outchannel = new DataOutputStream(outchannel);
 }

 public void send() throws IOException {
     byte[] data = toByteArray();
     outchannel.writeInt(data.length);
     outchannel.write(data);
     reset();
 }
}

2)

public class XMLSender {

 public static void send(Document tosend, OutputStream channel) throws   TransformerConfigurationException, IOException {
     XMLOutputStream out = new XMLOutputStream(channel);

     StreamResult sr = new StreamResult(out);
     DOMSource ds = new DOMSource(tosend);
     Transformer tf = TransformerFactory.newInstance().newTransformer();

     try {
         tf.transform(ds, sr);
     } catch (TransformerException ex) {
         Logger.getLogger(XMLSender.class.getName()).log(Level.SEVERE, null, ex);
     }

     out.send();
 }
}

3)

public class XMLInputStream extends ByteArrayInputStream {

 private DataInputStream inchannel;

 public XMLInputStream(InputStream inchannel) {
     super(new byte[2]); 
     this.inchannel = new DataInputStream(inchannel);
 }

 public void recive() throws IOException {
     int i = inchannel.readInt(); 
     byte[] data = new byte[i];
     inchannel.read(data, 0, i); 
     this.buf = data; 
     this.count = i;
     this.mark = 0;
     this.pos = 0;
 }

}

4)

public class XMLReceiver {

 public static Document receive(InputStream channel) throws ParserConfigurationException, TransformerConfigurationException, IOException, SAXException {

     DocumentBuilderFactory docBuilderFact = DocumentBuilderFactory.newInstance();
     DocumentBuilder docBuilder = docBuilderFact.newDocumentBuilder();
     Document request = null;


     XMLInputStream xmlin = new XMLInputStream(channel);

     xmlin.recive();

     request = docBuilder.parse(xmlin);

     return request;
 }
}
Sign up to request clarification or add additional context in comments.

Comments

1

You don't want to close the socket's OutputStream, because the socket only has one OutputStream.

It looks like you just need to flush your OutputStream after writing to it.

out.flush();

EDIT: Thanks for the extra info. If you're reading the socket like this, receiver needs to know when you're done writing. An InputStream only knows you're done writing if you close the socket.

But since you have already stated you can't close the socket, you need another way of telling the receiving side that you're done. You either need to use a special type of stream which knows about the data being sent, or you need to set up a contract for writing/reading the appropriate amount of data.

It would probably be easiest to send the data as an Object (using ObjectOutputStream/ObjectInputStream--maybe you don't even need to convert to XML).

If you don't want the overhead associated with Object streams, the simple solution is to compute the length of the data being sent, and send that number just prior to sending the actual data. In this case, you can use a DataOutputStream/DataInputStream. Send the number of bytes to read, followed by the data. On the receiving side, read the number, then read the given number of bytes into a temporary variable and feed that to DocumentBuilder.parse(InputStream).

On the sending end, you would do this:

DataOutputStream out = new DataOutputStream(s.getOutputStream());
ByteArrayOutputStream baos = new ByteArrayOutputStream();

XMLSerializer serializer = new XMLSerializer();
serializer.setOutputByteStream(baos);
tosend = WUTPClientWriter.createMessage100(projectid, cpuclock, cpunumber);
serializer.serialize(tosend);

out.writeInt(baos.size());
out.write(baos.toByteArray());

out.flush();

Then on the receiving end, you do something like the following:

DataInputStream in = new DataInputStream(s.getInputStream());

int len = in.readInt();
byte[] xml = new byte[len];
in.read(xml, 0, len);

Document doc = builder.parse(new ByteArrayInputStream(xml));

2 Comments

Don't work. Server wait on: request = docBuilder.parse(connection.getInputStream());
I updated my answer right after you posted your comment, but I see you found a different solution. :)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.