0

I am trying to figure out NIO in Java doing some simple client-server project.

The case is I have to concurrent clients in cached thread pool executor, who are communicating with single-threaded server using non-blocking NIO channels.

The problem is that last client cannot receive last server's sent message. It locks in infinite loop waiting for upcoming data.

ClientTask class:

public class ClientTask extends FutureTask<String> {

    private Client client;
    private List<String> reqList;   // requests list (without last and first)
    private boolean showRes;        // print request results

    public ClientTask(Client client, List<String> reqList, boolean showRes) {
        super(() -> ClientTask.getLogWhenArrives(client, reqList, showRes));
        this.client = client;
        this.reqList = reqList;
        this.showRes = showRes;
    }

    public static ClientTask create(Client c, List<String> reqList, boolean showRes) {
        return new ClientTask(c, reqList, showRes);
    }

    private static String getLogWhenArrives(Client client, List<String> reqList, boolean showRes) {
        client.connect();
        String response = client.send("login " + client.getId());
        if (showRes) System.out.println(response);

        for (String req : reqList) {
            response = client.send(req);
            if (showRes) System.out.println(response);
        }

        String responseLog = client.send("bye and log transfer");

        client.close();
        return responseLog;
    }
}

Client send():

    public String send(String req) {
        ByteBuffer reqBuffer = ByteBuffer.wrap((req + END).getBytes());
        try {
            channel.write(reqBuffer);
        } catch (IOException e) {
            e.printStackTrace();
        }

        return receive();
    }

Client receive()

 public String receive() {
        StringBuilder result = new StringBuilder();
        try {
            inBuff.clear();
            readLoop:
            while (true) { // THIS LOOP WON'T END
                int n = channel.read(inBuff);

                if (n == -1) {
                    break;
                }
                if (n > 0) {
                    inBuff.flip();
                    CharBuffer cb = charset.decode(inBuff);

                    while (cb.hasRemaining()) {
                        char c = cb.get();
                        if (c == END.charAt(0)) {
                            break readLoop;
                        }
                        result.append(c);
                    }
                }
            }

        } catch (IOException e) {
            e.printStackTrace();
        }
        return result.toString();
    }

Main:

public class Main {

  public static void main(String[] args) throws Exception {
    String fileName = System.getProperty("user.home") + "/PassTimeServerOptions.yaml";
    Options opts = Tools.createOptionsFromYaml(fileName);
    String host = opts.getHost();
    int port = opts.getPort();
    boolean concur =  opts.isConcurMode();
    boolean showRes = opts.isShowSendRes();
    Map<String, List<String>> clRequests = opts.getClientsMap();
    ExecutorService es = Executors.newCachedThreadPool();
    List<ClientTask> ctasks = new ArrayList<>();
    List<String> clogs = new ArrayList<>(); 

    Server s = new Server(host, port);
    s.startServer();

    // start clients
    clRequests.forEach( (id, reqList) -> {
      Client c = new Client(host, port, id);
      if (concur) {
        ClientTask ctask = ClientTask.create(c, reqList, showRes);
        ctasks.add(ctask);
        es.execute(ctask);
      }
    });

    if (concur) {
      ctasks.forEach( task -> {
        try {
          String log = task.get();
          clogs.add(log);
        } catch (InterruptedException | ExecutionException exc) {
          System.out.println(exc);
        }
      });
      clogs.forEach( System.out::println);
      es.shutdown();
    }
    s.stopServer();
    System.out.println("\n=== Server log ===");
    System.out.println(s.getServerLog());
  }

}

Server is sending all the info and channels are open and connected.

Mark Rotteveel
  • 100,966
  • 191
  • 140
  • 197
magladko
  • 3
  • 3
  • It isn't blocking. It is spinning on `read()` returning zero, either because there is nothing to read or there is no room in the buffer, which in turn is because you are never calling `compact()`. I wouldn't bother with non-blocking I/O in a client, but if you must use it, you must also use a Selector to tell you when to read. Merely spin-looping is not a correct way to write non-blocking code. Actually you are half-way to asynchronous mode, what with the `FutureTask`. I would either go the whole hog and use that, or else use blocking mode. This code needs major surgery, and it isn't worth it. – user207421 Apr 19 '22 at 04:26
  • `FutureTask` is for Main thread, which I cannot modify and it needs to wait until client collects final answer from server. I know that for whatever reason bytes from Server are not delievered to client channel, but are sent correctly. I will look into implementing selector in the client. Thanks for the answer! – magladko Apr 19 '22 at 08:49
  • Also using `compact()` throws `java.lang.IllegalArgumentException: newPosition > limit: (2114 > 66)` or `java.nio.BufferUnderflowException` – magladko Apr 19 '22 at 09:28
  • Using `compact()` how? You need to call it at the end of your read loop. – user207421 Apr 19 '22 at 10:30
  • Even when I call it at the end some requests are lost somewhere and I get BufferUnderflow for whatever reason. But I guess this code is just fundamentally defective as mentioned earlier, so I cannot really say why would that happen. – magladko Apr 20 '22 at 14:59

0 Answers0