Java Asynchroussocketchannel.read using future problem

53 Views Asked by At

I am trying to create an application where my client program reads the message from echo server. I'm trying to use Future to read the message from the server that will have a larger size than my allocated bytebuffer. My thought is to read into a outputstream until end-of-stream. However I think the code will stuck at readBytes = socket.read(buffer).get() at the last try becuase there will be nothing left to read from the socketchannel and Future will be blocked here. Please let me know how to fix this or another way around.

public String receiveMessage(){
    String message = "";
    if (socket.isOpen()) {
        try {
            ByteBuffer buffer = ByteBuffer.allocate(2);
            Future<Integer> readResult = socket.read(buffer);
            int readBytes = readResult.get();
            ByteArrayOutputStream outputStream = new ByteArrayOutputStream();

            while (readBytes != -1) {
                outputStream.write(buffer.array());
                buffer.clear();
                readBytes = socket.read(buffer).get();//stuck at here 
            }
            byte result[] = outputStream.toByteArray();
            System.out.println(result);
            message = new String(result, Charset.defaultCharset()).trim();
        } catch (InterruptedException | ExecutionException e) {
            e.printStackTrace();
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
    return message;
}

'''

1

There are 1 best solutions below

2
ReubenBeeler On

As this is an assignment, I believe I am not supposed to provide a completely functional answer, but here are some hints to guide you:

Oracle has many great Java tutorials including the one on sockets.

For asynchronous execution, I recommend creating a new java.lang.Thread object. Threads and Concurrency (unsurprisingly) also has a tutorial by Oracle. You may have something like the following, which I found useful when experimenting with Java sockets.

// write to server
Socket socket = //...
String message = //...
try (PrintWriter writer = new PrintWriter(socket.getOutputStream(), false)) {
    writer.println(message);
    // will auto-flush on '\n' (newline character) if 'false' in constructor is changed to
    // true or omitted (look at PrintWriter documentation)
    writer.flush();
} catch (IOException ioe) {
    ioe.printStackTrace();
}

// read from server
Socket socket = //...
try (BufferedReader reader = new BufferedReader(new InputReader(socket.getInputStream()))) {
    // TODO
} catch (IOException ioe) {
    ioe.printStackTrace();
}

// pipe input stream to output stream
// Perhaps you want what comes from the server to go directly into stdout
Socket socket = //...
new Thread() {
    @Override
    public void run() {
        try {
            socket.getInputStream().transferTo(System.out);
            // socket input stream is at end of stream, but not necessarily closed
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}.start();

Note that using InputStream#transferTo(OutputStream) will not terminate until the InputStream is closed, which is why you might want to execute it in its own thread.

Also, be careful about the above code segment: if you send a message through a socket using a PrintWriter then immediately close the PrintWriter, the PrintWriter will try to close the underlying OutputStream. Once that closes, it will generally try to close the Socket (whose OutputStream was being written to by the PrintWriter), and no more communication can be done through that socket (which will lead to a BrokenPipeException on attempted further use). So, perhaps try to send a message using newline characters as delimiters or something similar, which would be convenient for using a BufferedReader.