views:

1487

answers:

2

Hi,

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 Socket and I must keep alive this connection.

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

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

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

+1  A: 

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));
rob
Don't work. Server wait on: request = docBuilder.parse(connection.getInputStream());
THeK3nger
I updated my answer right after you posted your comment, but I see you found a different solution. :)
rob
A: 

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();
 }
}

public class XMLSender {

2)

 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;
 }
}
THeK3nger