1

I have a client/server application where two users write to each other and also can send large files to each other, i used ServerSocket and Socket classes to do this and everything was fine except of slow file transfer, this was the code:

Client side:

public void sendFileRequest(Path filePath) {

    try {

        DataOutputStream output = new DataOutputStream(socket.getOutputStream());

        output.writeUTF("FILE_REQUEST");
        output.writeUTF(gui.getFilePath().toFile().getName());
        output.writeLong(gui.getFilePath().toFile().length());

    } catch (IOException e) {
        e.printStackTrace();
    }

}

Server side:

DataInputStream codeInput = new DataInputStream(client1.getInputStream());
DataOutputStream codeOutput = new DataOutputStream(client2.getOutputStream());

String code = codeInput.readUTF();

                    switch (code) {

                    case "MESSAGE":

                        String message = codeInput.readUTF();
                        codeOutput.writeUTF(code);
                        codeOutput.writeUTF(message);

                        break;

                    case "FILE_REQUEST":

                        String fileName = codeInput.readUTF();
                        long fileSize = codeInput.readLong();

                        codeOutput.writeUTF(code);
                        codeOutput.writeUTF(fileName);
                        codeOutput.writeLong(fileSize);

                        break;

Switching from IO to NIO(the problem)

After "output.writeUTF("FILE_REQUEST");" at clients side, the client waited until "codeInput.readUTF();" was called on the server, then the client continued. The problem is that when i replaced ServerSocket with ServerSocketChannel, Socket with SocketChannel etc. and started using java NIO, it goes like this:

client:write - client:write - client:write - server:read

what i need is this

client:write - server:read - client:write - server:read - client:write - server:read

In case of a FILE_REQUEST, when i read the code on the server side, for some reason it reads only the first write operation of the client but when i try to read on the server side for the second time, it reads the bytes of the second message along with the bytes of the long value i'm sending

Here is the new code:

Client side:

//im using ByteBuffer
public void sendFileRequest(Path filePath) {

    try {

        writeString("FILE_REQUEST");
        //Here i need the code to wait until the string was read
        writeString(gui.getFilePath().toFile().getName());
        //Here i need the code to wait until the string was read
        writeLong(gui.getFilePath().toFile().length());

    } catch (IOException e) {
        e.printStackTrace();
    }

}

private void writeString(String s) throws IOException {

    buffer.put(s.getBytes());
    buffer.flip();
    channel.write(buffer);
    buffer.clear();

}

private void writeLong(long value) throws IOException {

    buffer.putLong(value);
    buffer.flip();
    channel.write(buffer);
    buffer.clear();

}

Server side:

public void run() {

    String code = readString();

    switch (code) {

                    case "MESSAGE":

                        String message = readString();
                        writeString(code);
                        writeString(message);

                        break;

                    case "FILE_REQUEST":

                        String fileName = readString();
                        long fileSize = readLong();

                        writeString(code);
                        writeString(fileName);
                        writeLong(fileSize);

                        break;

}

private String readString() throws IOException {

            firstChannel.read(firstBuffer);
            firstBuffer.flip();
            byte[] bytes = new byte[firstBuffer.remaining()];
            firstBuffer.get(bytes);
            firstBuffer.clear();

            return new String(bytes);

        }

        private long readLong() throws IOException {

            firstChannel.read(firstBuffer);
            firstBuffer.flip();
            long value = firstBuffer.getLong();
            firstBuffer.clear();

            return value;

        }

I want to use java NIO and either find a way how to send the data separately or send the bytes of the first string, second string and long value together and then check if all the bytes are there and somehow divide them, thanks for any help

Peter
  • 409
  • 2
  • 6
  • 14
  • I don't see how you are able to distinguish between the command name, file name, length, and payload? Shouldn't you send a header length? With an Object(In|Out)putStream the delimiters between native types/objects are taken care of, but when sending bytes, the server may read all bytes written so far by the client. – Kenney Feb 17 '16 at 19:45
  • If performance is important I suggest you do this without creating new objects, and you want to minimise every system call i.e. read/write. Each of these can limit the performance of your reading and writing. – Peter Lawrey Feb 17 '16 at 19:58
  • @Kenney When i was using DataOutputStream first i wrote writeUTF(code), then writeUTF(nameOfFile) and then writeLong(sizeOfFile), after each "write" operation there had to be "read" operation on the server side in order for other write operation to execute and they were in the same order, 1.writing code 2. reading code 3.writing name 4.reading name etc, with NIO i don't know how to do this – Peter Feb 17 '16 at 20:18
  • Your question is based on a fallacy. Switching to NIO won't speed up your file transfer. I would throw this all away and go back to blocking I/O. Two non-working implementations is one too many. Probably all you need to do is use buffered streams under your data streams and fix your copy loops. – user207421 Feb 17 '16 at 20:38
  • @Peter I know - the binary format of serialized objects allows DataObjectStreams to know the size of each object and block until that many bytes are read. You can do the same with NIO - wait for the buffer to be at least an `int`, read the int as the size of the block, then wait until the buffer is at least that size, and take size bytes out of the buffer and return that. – Kenney Feb 17 '16 at 20:42
  • You haven't posted your original file transfer code but it should strongly resemble [this](http://stackoverflow.com/a/10370497/207421). – user207421 Feb 17 '16 at 21:30

0 Answers0