【问题标题】:Java socket blockingJava 套接字阻塞
【发布时间】:2021-03-03 16:15:09
【问题描述】:

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

但我的问题是,在执行一个命令后,我的程序会陷入死锁。我对其进行了解决方法,因此我的客户端发送命令并在服务器之后-说您可以发送查询-对象。如何避免这种解决方法?

这是我的服务器。

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

提前谢谢各位!

【问题讨论】:

  • 您正在发送和接收文本和二进制数据的组合。执行此操作时必须小心。

标签: java sockets server client


【解决方案1】:

您认为BufferedReader 中的“缓冲”代表什么?

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

解决方案是永远不要混合流。要“取消混合”此流,请定义一个协议。例如:

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

等等。

这导致了更多的实现:Java 内置的序列化机制几乎完全不适合这项工作;这是一个你无法描述的协议(除了:“获取这个类文件,找到一个 JVM,确保类文件在它的类路径上,创建一个 ObjectInputStream,然后在上面调用 readObject”),这是非常低效的,充斥着如果您只使用 java 会出现问题(在面对序列化对象时更改代码非常棘手),并且如果您向链中添加任何不是用 java 编写的工具,则无法阅读。我建议你用别的东西。

一些替代方案:

  • 使用例如将对象序列化为 JSON GSONJackson
  • ProtoBuf 用于整个协议(不仅是数据本身,还包括您发送的那些命令)。
  • 制作一个REST接口;而不是发送混合命令和二进制数据的长期 TCP/IP 连接,而是让客户端调用例如https://yourserver.com/api/listAll 如果在命令调用之间要记住有状态的数据,则在标头中带有某种身份验证令牌和/或会话令牌。然后在网上搜索如何在 java 中制作 REST API,您会发现大量选项,例如 JerseySpark 等 JAX-RS 实现。
  • 从相同的基础数据中抛弃多个过滤流的想法,转而使用原始的InputStream,仔细阅读您需要的内容,并在几乎所有内容前加上“大小”,以便了解要阅读的内容,不会不小心阅读太多。这相当复杂,这就是为什么我建议您不要采用此计划,而是选择上述其他 3 个选项之一。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2010-10-31
    • 2013-10-15
    • 1970-01-01
    • 1970-01-01
    • 2017-01-11
    • 2013-10-02
    • 1970-01-01
    相关资源
    最近更新 更多