【问题标题】:How can you force a flush on an OutputStream object without closing it?如何在不关闭 OutputStream 对象的情况下强制刷新?
【发布时间】:2012-04-27 08:16:23
【问题描述】:

我的问题在于以下假设,我希望这些假设是正确的,因为当我在谷歌搜索我的问题时阅读它们时我相信这些假设:

  1. 关闭 Socket 的 OutputStream 也会关闭套接字
  2. OutputStream 的 flush() 方法什么都不做

所以我基本上需要从我的 OutputStream 对象中刷新数据才能让我的应用程序正常工作。

如果您对详细信息感兴趣,请查看以下两个链接:

Weird behavior : sending image from Android phone to Java server (code working)

通过关闭 OutputStream 解决了这个问题。这样做会将所有数据刷新到套接字的另一端,并使我的应用程序进一步工作,但这个修复很快引发了问题 2 - 相应的套接字也被关闭:

SocketException - 'Socket is closed' even when isConnected() returns true

【问题讨论】:

  • 如果您投反对票,请礼貌地解释原因。

标签: java android sockets outputstream


【解决方案1】:

关闭 Socket 的 OutputStream 也会关闭套接字

是的。

OutputStream 的 flush() 方法什么都不做

错误。有覆盖。请参阅FilterOutputStream.flush()BufferedOutputStream.flush()ObjectOutputStream.flush() 等的 Javadoc。

所以你最初的问题是不存在的,所以你不需要导致问题 #2 的“解决方案”。

【讨论】:

  • 那么你对问题#​​1 有什么建议呢?最初我确实在 OutputStream 上使用了 flush() 而不是 close() 但图像不会被传输..
  • 问题#1的解决方案是不要关闭流。如果对等方需要流结束才能知道消息何时完成,则您使用的应用程序协议只允许一条消息,因此您需要更改它。 TCP 中没有消息,只有一个字节流。消息由您决定。
【解决方案2】:

你可以调用OutputStream的flush方法而不是close。继承自 OutputStream 的具体类将覆盖 flush() 以执行其他操作(将数据写入文件或通过网络发送)。

【讨论】:

  • 但是,如果您看到我发布的第一个问题,则通过套接字传输的图像在 OutputStream 关​​闭之前不会被传输。并且使用 flush() 在那里没有帮助。那你有什么建议呢?
  • 在您发布的链接中,服务器正在等待流上的 EOF(文件结尾)。通过关闭流和 TCP 连接来发出 EOF 信号 - 因此服务器在循环中运行,直到您关闭客户端的输出流。如果您不希望在传输图像后关闭流,则必须设计另一种方法来在图像传输完成时发出信号。最简单的方法是在发送图像之前将图像大小作为整数(4 个字节)发送。然后服务器可以在退出循环之前读取最多该数量的字节。
【解决方案3】:

OutputStream 的 flush() 方法什么都不做。

这是不正确的。

OutputStream 类提供的flush() 的基本实现确实没有任何作用。但是,您的应用将调用您正在使用的 actual 流类提供的该方法的版本。如果流类没有直接写入语义,它将覆盖flush() 以执行所需的操作。

简而言之,如果需要刷新(并且 是 Socket 输出流所必需的),那么调用 flush() 将做正确的事情。 (如果某些互联网来源告诉您否则它是错误的或您误解了它。)


仅供参考,基础OutputStreamflush() 实现为无操作的原因是:

  • 某些输出流类在刷新时不需要做任何事情;例如ByteArrayOutputStream,和
  • 对于flush() 不是空操作的流类,无法在基类级别实现操作。

他们可以(理论上)设计流 API,以便 OutputStream 是一个抽象类(而 flush() 是一个抽象方法)或一个接口。然而,这个 API 在 Java 1.0 之前就被有效地冻结了,当时没有足够的实际 Java 编程经验来意识到这个 API 设计是次优的。

【讨论】:

    【解决方案4】:

    你真的需要冲洗吗?我还有一个问题,c# 服务器上的监听器无法接收从 android 发送的数据(我试图同步获取数据)。

    我确定这是因为在 Android 端,以下代码没有刷新。

    OutputStream str = btSocket.getOutputStream();
    str.write(data_byte);
    // "This implementation does nothing"
    str.flush();
    

    事实证明,如果我在服务器的侦听器上使用异步数据检索 - 它会获取数据,并且不需要客户端的 flush()!

    【讨论】:

      【解决方案5】:

      我会试一试。我遇到了同样的问题。关闭输出流是我可以“刷新”数据的唯一方法。但因为我仍然需要不是一个选项的输出流。所以第一次我发送字节数组长度,out.writeInt,然后是数组本身。当所有字节都被读取时,即 buffer.length == in.readInt() 我打破循环

          ByteArrayOutputStream dataBuffer = new ByteArrayOutputStream();
          byte[] buffer = new byte[1024];
          byte[] fileBytes;
          int n;
          int length;
      
          try
          {
              size = in.readInt();
      
              while((n = in.read(buffer)) != -1)
              {
                  dataBuffer.write(buffer, 0, n);
      
                  if(dataBuffer.toByteArray().length == length)
                  {
                      fileBytes = dataBuffer.toByteArray(); break;
                  }
              }
          }
      

      【讨论】:

        【解决方案6】:

        我遇到了同样的问题。 在流的末尾添加“\n”。刷新有效,但命运不知道消息是否结束

        【讨论】:

          【解决方案7】:

          YES on Android flush() 什么都不做(基于 api 23 的示例)

          public Socket() {
              this.impl = factory != null ? factory.createSocketImpl() : new PlainSocketImpl();
              this.proxy = null;
          }
          
          public class PlainSocketImpl extends SocketImpl {
          
              @Override protected synchronized OutputStream getOutputStream() throws IOException {
                  checkNotClosed();
                  return new PlainSocketOutputStream(this);
              }
          
          
          }
          
          private static class PlainSocketOutputStream extends OutputStream {
                  // doesn't override base class flush();
          }
          

          要在不关闭套接字的情况下刷新输出流,您可以关闭输出:

          protected void shutdownOutput() throws IOException
          

          -- 这将关闭 WRITE 文件描述符。

          您可以直接写入文件描述符,而不是使用输出流,或者通过使用 OutputStream 创建自己的 Socket 实现,这将覆盖刷新方法(例如在 c 中使用 Berkeley socket 实现(通过本机调用) )。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 2011-10-01
            • 2013-10-04
            • 1970-01-01
            • 1970-01-01
            • 2015-04-13
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多