在服务器关闭连接之前,客户端不会收到响应消息 (Java)

Client doesn't get response message until the server closes the connection (Java)

我和我的同事正在开发一个客户端应用程序,该应用程序与 Web 服务通信以处理一些交易数据。这些是我们沟通的步骤:

  1. 客户端应用程序发起连接并发送请求消息(timestamp = T1)
  2. Web 服务接受连接并处理请求(timestamp ≈ T1)
  3. Web 服务 returns 响应消息 (timestamp = T1 + few_seconds)
  4. Web 服务关闭连接(timestamp = T1 + 3_minutes)
  5. 客户端应用程序获取响应消息并继续进行数据解析(timestamp = T1 + 3_minutes)

我们的问题在于时间戳引用: Web 服务几乎立即处理请求并检索响应,但客户端应用程序在服务器关闭连接之前不会收到响应消息。以下是日志文件:

客户端应用程序日志片段:

10:46:25,031 INFO  MessageHandler.java:115 Message sent.
10:49:25,071 INFO  MessageHandler.java:125 Message received.
10:49:25,103 DEBUG  MessageParser.java:67 Message parsed.

服务器应用程序日志:

10:46:25:153 <INFO>   Client connection accepted.
...
10:46:26:602 <INFO>   Response message sent.
...
10:49:25:069 <INFO>   Closing connection...

如您所见,客户端只有在服务器关闭连接时才会得到响应。

这是完成通信的方法的客户端应用程序代码:

public static String sendMsg(byte[] msg, String serverIp, int port) {
    try {
            InetAddress address = InetAddress.getByName(serverIp);
            Socket socket = new Socket(address, port);
            OutputStream os = socket.getOutputStream();

            logger.info("Message sent.");

            os.write(msg, 0, msg.length);
            os.flush();

            InputStream is = socket.getInputStream();
            InputStreamReader isr = new InputStreamReader(is);
            BufferedReader br = new BufferedReader(isr);
            String msgReceived = br.readLine();

            logger.info("Message received.");
            os.close();
            return msgReceived;
        } ...(catch blocks) ... 
}

假设我们无法更改web服务实现,有没有办法在服务发起响应时立即在客户端获取响应消息(然后由客户端关闭连接)?提前致谢。

client-side 听起来您对此无能为力。可能发生的情况是 Web 服务器正在缓冲来自 Web 服务的响应(因此它可以设置内容长度 headers 等),因此在 Web 服务关闭连接之前不会发送任何数据。您可以通过使用 br.read() 方法读取数据的单个字符而不是像现在这样一次读取整行来间接检验这个假设。

正如@Yeroc 所建议的那样,Web 服务器似乎正在缓冲其响应并且在发生超时之前不会刷新缓冲区。 (我觉得这在服务器端是一个糟糕的设计选择,但如果您无法控制服务器,那么您可能会坚持使用它。)

我会尝试在客户端做一个 shutdown(SHUT_WR)。这应该会在服务器中产生一个 "end of file" 指示,这可能会导致它刷新缓冲区并关闭连接。由于这正是您 想要 发生的情况,因此您可能会更快得到回复。 shutdownSHUT_WR 只会关闭一个方向的连接——客户端到服务器方向——所以你应该仍然能够收到响应。

(服务器可能会选择 close 而不刷新,但刷新缓冲区似乎是作为高级 close 调用的副作用完成的服务器端,因此绝对值得一试。)

客户端很可能没有正确实现消息协议。例如,客户端调用readLine。消息协议是否指定消息是 ?如果没有,readLine 将继续等待直到连接关闭,仍然尝试获取永远不会发送的那条线。

客户端将无法找到消息的结尾,除非它了解消息的定界方式。在您的协议中如何分隔消息?检测消息结束的客户端代码在哪里?如果你不写,就不会发生。