2

First of all, this is a homework problem. That being said, I'm stuck. Googling for java Properties over Sockets results in a lot of irrelevant things.

I'm trying to transfer a Properties object over a socket. The API says it can be done with a Stream or a Writer/Reader, but I can't get it to work. I can do it manually, that is, if I read the file line by line and pass it through a PrintWriter.

On the client side I've got roughly:

socket = new Socket(host, port);
outStream = socket.getOutputStream();
out = new PrintWriter(outStream, true);
in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
reader = new BufferedReader(new FileReader(file));
...
props.load(reader);
props.store(out, null);

On the server side the receiving bits look like:

out = new PrintWriter(sock.getOutputStream(), true);
inStream = sock.getInputStream();
in = new BufferedReader( new InputStreamReader(inStream));
...
props.load(in); // hangs
// doesn't get to code here...

In this case it hangs at the props.load(in). Instead of doing props.load(in), I read it in line by line to make sure props.store(out, null) was working, and the data looks like its being transferred.

Is there something about load/store I don't understand, or is it an issue with the Stream/Writer/Reader?

kjprice
  • 1,316
  • 10
  • 26
  • 2
    Did you close your output socket after you finished writing the Properties to it? – Jack Edmonds Jun 20 '12 at 22:07
  • 1
    You need to tell the server side of the socket that the message has finished. You need some mechanism for demarcation. When does a properties file finish? The server doesn't know, so it attempts to keep reading unless you close the socket from the client side or something else happens. – Martín Schonaker Jun 20 '12 at 22:10
  • So, I'd have to close it and reopen it? `sock.close(); sock.connect();` EDIT: just read Socket docs, that's not going to work. If I want to verify the transaction, I ought to do it the 'manual' way then? – kjprice Jun 20 '12 at 22:16

1 Answers1

2

I think this will answer this question as well as How do I recognize EOF in Java Sockets? and What can I send to an InputStream to signify EOF has been reached?

I had a similar problem; my dilemma was that I had a client/server request-response protocol where one of the requests included a stream sent from the client side using clientProps.store(). The corresponding serverProps.load() on the server side never returns because it needs to see the "end-of-file" - which in Java means the client has to close it's stream; resulting in the socket connection closing. The unwanted result was that, not only could I not keep the socket open for indefinite request-response exchanges, I couldn't even keep it open for the server to send its reply.

I hated Java for making me do that, even more because the documentation for Properties.load() says:

The specified stream remains open after this method returns.

That could never happen if it's detecting end-of-file by seeing the stream close!! Anyway, now, I still love Java because it allowed me to use this solution (might not be useful if you have any special encoding or localization of the data you are streaming):

I used this on the client side:

    PrintWriter toServer;
    Properties clientProps = new Properties();

//  ... code to populate the properties and to 
//      construct toServer from the socket ...

    clientProps.store(toServer, null);
    toServer.write('\u001A'); // this is an old-school ASCII end-of-file
    toServer.flush();

On the server side I extended Reader to detect the 1A and return -1 (so that the serverProps.load() learns about the end-of-file in the normal way (by seeing -1 returned from a call to read()), but below that, the stream and the socket stay open.

    BufferedReader fromClient;
    Properties serverProps = new Properties();


// ... code to construct fromClient from the socket ...

    serverProps.load (new PropReader (fromClient));

/////

    private static class PropReader extends Reader {

    BufferedReader src;
    boolean eof=false;

    private PropReader(BufferedReader fromClient) {
        super();
        src=fromClient;
    }


    @Override
    public int read(char[] cbuf, int off, int len) throws IOException {
        int inCount;

        if (!eof) {
            inCount = src.read(cbuf, off, len);

            if (inCount > 0) {
                // we read a buffer... look at the end for the EOF that the client used to mark the end of file
                if (cbuf[off+inCount-1] == '\u001A') {
                    --inCount; // don't send eof with the data
                    eof = true; // next time... we'll return -1
                }
            }
        } else {
            inCount = -1;
        }

        return inCount;
    }

    @Override
    public void close() throws IOException {
        src.close();
    }
Community
  • 1
  • 1
PMorganCA
  • 730
  • 6
  • 24
  • since writing this I have added this after `toServer.flush()` on the client side: `synchronized (props) { try { props.wait(10); } catch (InterruptedException ex) { } }` ... seems like the client was sending its next request so soon that it was buffered on the server side and caught in a overlapping read. The result was that the **eof** was _not_ the last char in the bufffer in the server-side read. – PMorganCA Mar 15 '13 at 15:03