Java 套接字阻塞

Java socket blocking

我正在尝试建立一个具有多个客户端连接的服务器。客户端首先发送用户输入的命令。然后服务器读取所有参数并将其作为对象发送到服务器。

但我的问题是,执行一个命令后我的程序陷入死锁。我对它做了一个解决方法,所以我的客户端发送命令并在服务器之后 - 说你可以发送查询 - 对象。我怎样才能避免这种解决方法?

这是我的服务器。

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());
        }

这是我在客户端的功能。

 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;
    }

提前谢谢大家!

你认为 BufferedReader 中的 'buffered' 代表什么?

作为一项规则,您不能围绕同一个 'source' 输入流或 reader object 创建多个包装器并期望事情正常工作 - 过滤器流(那些是流它们本身不是源,但你将其包裹在另一个源中;BufferedReader 就是这样的一个例子,InputStreamReader 也是如此)被允许从底层输入源读取比严格要求更多的内容提供任何请求的数据;某些过滤器流,例如 BufferedReader,实际上是明确指定的(例如,BR 所做的 事情,因此得名)。

解决方案是永远不要混合流。为了 'unmix' 这个流,定义一个协议。例如:

首先,客户端发送1个字节,表示即将发送的命令的长度。然后,它发送那么多字节(注意:BYTES,不是字符,你不应该在这里制作任何类型的 Reader object),然后你使用 US_ASCII 编码转换为字符串,然后对于每个命令,您跳转到单独的代码片段。例如,'exit' 命令不需要发送更多数据,但是 'data' 命令将发送一个 big-endian 32 位值,其中包含以下数据的大小。然后使用 java 的内置序列化机制(即 ObjectInputStream)读取和解释该数据。

等等。

这导致了更多的认识:Java 的内置序列化机制几乎完全不适合这项工作;它是一个你无法描述的协议(除了:“拿这个 class 文件,找到一个 JVM,确保 class 文件在它的 class 路径上,创建一个 ObjectInputStream,然后调用readObject on it"),这是非常低效的,如果你坚持只使用 java 就会充满问题(面对序列化的 objects 改变你的代码是相当棘手的),如果你无法阅读永远向链中添加任何未在 java 中编写的工具。我建议你用别的东西。

一些备选方案:

  • 将 objects 序列化为 JSON 使用例如GSON or Jackson.
  • 对整个协议使用 ProtoBuf(不仅是数据本身,还包括您发送的那些命令)。
  • 制作REST界面;而不是 long-lived TCP/IP 连接,您可以在其中发送命令和二进制数据的混合,而是让客户端调用,例如https://yourserver.com/api/listAll 在 headers 中带有某种身份验证令牌 and/or session 令牌(如果在命令调用之间有状态数据要记住)。然后在网上搜索如何在 java 中制作 REST API,你会发现很多选项,例如 JAX-RS 实现,如 Jersey or Spark.
  • 摒弃相同基础数据的多个过滤器流的想法,而是使用原始 InputStream,仔细阅读您需要的内容,并按顺序在几乎所有内容前加上前缀 'size'知道要读多少,而不会不小心读太多。这相当复杂,这就是为什么我建议你不要采用这个计划,而是选择上面其他 3 个选项之一。