简体   繁体   中英

Java socket blocking

I'm trying to make a sever with multiple client connections. The Client sends first the command, which the user tipped in. Then the server reads all arguments and send this as an object to the server.

But my issue is, that after one command my program runs into a deadlock. I made a workaround to it so my client sends the command and after the server - says you can send the query - the object. How can I avoid this workaround?

This is my server.

try {
            out = new PrintWriter(clientSocket.getOutputStream(), true);
            in = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
            objectOut = new ObjectOutputStream(clientSocket.getOutputStream());
            objectIn = new ObjectInputStream(clientSocket.getInputStream());
            // communicate with client
            if ("Hello Server".equals(in.readLine())) {
                out.println("Connection with Server established");
            }
            else {
                out.println("You picked the wrong house, fool! (wrong greeting)");
            }
            String inputLine;
            while ((inputLine = in.readLine()) != null) {
                if ("exit".equals(inputLine)) {
                    break;
                }
                else if ("ls".equals(inputLine)) {
                    execute_ls();
                }
                else if ("data".equals(inputLine)) {
                    out.println("o");
                    var query = (Query) objectIn.readObject();
                    exceute_data(query);
                }
            }

            in.close();
            out.close();
            clientSocket.close();
        } catch (Exception e) {
            Logger.err(e.toString());
        }

And this is my function on the client side.

 public CompletableFuture<Data> queryData(param) {
        Data data_from_server;
        CompletableFuture<Data> data_series = new CompletableFuture<>();
        try {
            outputData.println("data");
            inputData.readLine();
            objectOutputData.writeObject(param);

            data_from_server = (Data) objectInputData.readObject();
            data_series.complete(data_from_server);

            if (data_from_server.getErrorMessage() != null) {
                throw new Exception(data_from_server.getErrorMessage());
            }
        } catch (Exception e) {
            System.out.println(e.getMessage());
        }
        return data_series;
    }

Thanks in advance guys!

What did you think 'buffered' in BufferedReader stands for?

As a rule you cannot create more than one wrapper around the same 'source' inputstream or reader object and expect things to work properly - filterstreams (those are treams that aren't themselves the source, but which you wrap around another one; BufferedReader is one example of such a thing, as is InputStreamReader) are permitted to read more from the underlying input source than was strictly required to serve up any requested data; certain filterstreams, such as BufferedReader, are in fact explicitly specified to do that (that's the only thing BR does, for example, hence the name).

The solution is to never have a mixed stream. To 'unmix' this stream, define a protocol. For example:

First, the client sends 1 byte which indicates the length of the command it is about to send. Then, it sends that many bytes (note: BYTES , not characters, you should not be making any Reader object of any sort here), which you then turn into a string using the US_ASCII encoding, and then for each command you jump to a separate slice of code. For example, the 'exit' command requires no further data to be sent, but the 'data' command will then send a big-endian 32-bit value containing the size of the following data. That data is then read and interpreted using java's built in serialization mechanism (ie ObjectInputStream).

and so on.

This leads to a few more realizations: Java's built in serialization mechanism is almost entirely inappropriate for this job; it is a protocol that you cannot describe (except as: "Take this class file, find a JVM, ensure the class file is on its classpath, create an ObjectInputStream, and call readObject on it"), that is quite inefficient, rife with issues if you stick with just java (changing your code is quite tricky in the face of serialized objects), and impossible to read if you ever add any tooling to the chain that isn't written in java. I suggest you use something else.

Some alternatives:

  • Serialize objects into JSON using eg GSON or Jackson .
  • Use ProtoBuf for the entire protocol (not just the data itself, but also those commands you are sending).
  • Make a REST interface; instead of a long-lived TCP/IP connection where you send a mix of commands and binary data, instead, have the client call eg https://yourserver.com/api/listAll with, in the headers, some sort of authentication token and/or session token if there is stateful data to be remembered between command invocations. Then search the web for how to make a REST API in java, and you'll find a ton of options, such as JAX-RS implementations like Jersey or Spark .
  • Ditch the idea of multiple filterstreams off of the same underlying data, and instead go with the raw InputStream , carefully reading exactly what you need, and prefixing almost everything with a 'size' in order to know how much TO read, without accidentally reading too much. This is rather complicated, which is why I suggest you don't go with this plan and instead pick one of the other 3 options above.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM