【问题标题】:Client doesn't get response message until the server closes the connection (Java)在服务器关闭连接之前,客户端不会收到响应消息(Java)
【发布时间】:2017-01-05 18:28:31
【问题描述】:

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

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

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

客户端应用日志sn-p:

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服务的实现,有没有办法在服务发起响应时立即在客户端获取响应消息(然后由客户端关闭连接)?提前致谢。

【问题讨论】:

  • 您可能想尝试使用 DataFetcher,添加 FetcherListener 并侦听 gotMore(...) 问题可能是您的 BufferedReader.readLine() 正在等待新行并且没有通知你的传入数据。 sourceforge.net/p/tus/code/HEAD/tree/tjacobs/io/…
  • 究竟使用了什么协议?消息的结尾是如何分隔的?检测消息结束的客户端代码在哪里?您如何期望客户端代码知道它何时收到消息? (连接关闭时除外,因为这似乎不是您想要的。)

标签: java sockets tcp network-programming client-server


【解决方案1】:

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

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

【讨论】:

  • “客户端没有正确实现消息协议”——你是对的,协议不使用任何消息分隔符或 EOL 定义。相反,看起来消息在消息头中保存了(重要的)内容大小信息,因此我们使用该信息来指定要从输入流中读取的字节长度。谢谢
【解决方案2】:

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

【讨论】:

    【解决方案3】:

    正如@Yeroc 建议的那样,Web 服务器似乎正在缓冲其响应,并且在发生超时之前不会刷新缓冲区。 (这让我觉得服务器端的设计选择很糟糕,但如果你无法控制服务器,大概你会被它困住。)

    我会尝试在客户端执行shutdown(SHUT_WR)。这应该在服务器中产生一个“文件结束”指示,这可能会导致它刷新其缓冲区并关闭连接。由于这正是您希望发生的事情,因此您可能会更快地做出回应。 shutdownSHUT_WR 只关闭一个方向的连接——客户端到服务器的方向——所以你应该仍然能够接收到响应。

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

    【讨论】:

      猜你喜欢
      • 2013-01-18
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-12-22
      • 1970-01-01
      • 2016-07-26
      • 2018-11-04
      • 2019-11-09
      相关资源
      最近更新 更多