1

I'm trying to create a simple client where at first I comunicate to a server:

  1. A filename
  2. The sequence of chunks which compose the file

So for the first one I thought to use to a BufferedWriter: this choice was made since I can't use on the server a InputStreamReader from the moment that the readLine() method is deprecated. However, for the second one I used a OutputStreamWriter since it is the better (only?) one choice to write a byte array on a socket.

So, this is the first version of my client code:

public class Client
{
    private static final int PART_SIZE = 1000000; // 1MB

    public static void main(String[] args) throws IOException
    {
        final Path file = Paths.get(args[0]);
        final String filenameBase = file.getFileName().toString();
        final byte[] buf = new byte[PART_SIZE];    
        Socket socket = new Socket(InetAddress.getLocalHost(),8080);
        System.out.println("Socket created");
        int partNumber = 0;
        Path part;
        int bytesRead;
        byte[] toWrite;

        try (
            final InputStream in = Files.newInputStream(file);
            final BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
            final DataOutputStream dos = new DataOutputStream(socket.getOutputStream());
        ) {
            System.out.println("closed="+socket.isClosed());
            bw.write(filenameBase,0,filenameBase.length());
            //other stuff for the chunk creation and spedition
        }
    }
}

However, if I run this code, this exception occours:

Exception in thread "main" java.net.SocketException: Socket closed
    at java.net.SocketOutputStream.socketWrite(SocketOutputStream.java:121)
    at java.net.SocketOutputStream.write(SocketOutputStream.java:159)
    at sun.nio.cs.StreamEncoder.writeBytes(StreamEncoder.java:221)
    at sun.nio.cs.StreamEncoder.implClose(StreamEncoder.java:316)
    at sun.nio.cs.StreamEncoder.close(StreamEncoder.java:149)
    at java.io.OutputStreamWriter.close(OutputStreamWriter.java:233)
    at java.io.BufferedWriter.close(BufferedWriter.java:266)
    at PAD.Charlie.Client.App.main(App.java:50)

The strange thing is that if I change the order between the BufferedWriter and the DataOutputStream inside the try everthing works fine!

Actually the idea has come because I remembered something about it from the java course, but I can't really remember the details! Can you help me about this doubt that I have? Thanks a lot! :)

TwistAndShutter
  • 239
  • 2
  • 15

2 Answers2

1

First of all, what you are doing is borderline crazy. You appear to be intending to write both text and binary data the same stream:

  • It is going to be difficult to control the interleaving of the two kinds of data since you are using a buffered writer at that point in the stack.

  • Even if you get the interleaving right, the "other end" has the problem of unpicking it to separate the text and binary.


You attempt to justify your decision use two stream stacks on the output stream as follows:

So for the first one I thought to use to a BufferedWriter: this choice was made since I can't use on the server a InputStreamReader from the moment that the readLine() method is deprecated. However, for the second one I used a OutputStreamWriter since it is the better (only?) one choice to write a byte array on a socket.

I don't follow your logic. But the fact that one approach doesn't work does not necessarily mean that (any) other one will.

If you want a solution that will work, then I can think of a few. The simplest is to use DataOutputStream only on the client side, and use writeUTF to write the file name and writeInt + write to write the chunks. Indicate the end of file by sending a chunk size of zero.

(You could also send the file as one big chunk if you know beforehand how many bytes you will be sending.)

The server-side code should mirror the client-side in its calls on a DataInputStream.


But the reason for difference in behaviour that you are seeing is that the order of the declarations in the try initializations determines the order in which the streams are closed at the end of the try block.

  • If the writer is closed first then:

    BufferedWriter.close() 
        -> BufferedWriter.flush() -> OutputStreamWriter.write()
        -> OutputStreamWriter.close() -> SocketOutputStream.close()
    DataOutputStream.close() -> SocketOutputStream.close()
    

    This is OK because the second set of closes does not need to write any data.

  • If the writer is closed second then:

    DataOutputStream.close() -> SocketOutputStream.close()
    BufferedWriter.close() 
        -> BufferedWriter.flush() -> OutputStreamWriter.write()  // FAIL
    

    The failure happens because the flush cannot write data to the socket because you have already (implicitly) closed it.

Stephen C
  • 698,415
  • 94
  • 811
  • 1,216
  • Well could you then suggest me a method to write: 1. a string only once 2. a number which is the bytearray size 3. a bytearray 4. repeat 2. until the whole file wast sent – TwistAndShutter Jan 11 '15 at 00:19
  • For the filename use `writeUTF()` and `readUTF()`, and get rid of the readers and writers altogether. For the size, `writeInt()` or `writeLong()` etc. – user207421 Jan 11 '15 at 00:20
0

Because closing the BufferedWriter flushes it, and if you create the writer first it will be closed last, after the stream, and closing either of them closes the socket. See the stack trace. DataOutputStream isn't buffered, so flushing it does nothing.

NB:

... since I can't use on the server a InputStreamReader from the moment that the readLine() method is deprecated. However, for the second one I used a OutputStreamWriter since it is the better (only?) one choice to write a byte array on a socket.

None of this makes sense. InputStreamReader doesn't have a readLine() method, let alone one that is deprecated; and OutputStreamWriter writes chars, not bytes.

user207421
  • 305,947
  • 44
  • 307
  • 483