【问题标题】:Is an OutputStream in Java blocking? (Sockets)Java 中的 OutputStream 是否阻塞? (插座)
【发布时间】:2012-05-21 10:07:27
【问题描述】:

我目前正在为一个项目编写简单的网络代码,一位伙伴向我暗示,当我以迭代方式从服务器向所有客户端发送一包信息时,当其中一个客户端运行时,我可能会出现严重的延迟。没有正确响应。

他以拖钓而闻名,所以我在实现一个辅助线程时有点怀疑,该线程现在负责将数据发送到客户端,有一个队列,服务器只需将包添加到该队列上,然后由线程读取到发送数据。

我现在思考后的问题是天气与否Java Socket的OutputStream实际上将他想要发送的东西排队,从而预先消除了排队的需要。只有当服务器阻塞时,只要他没有从客户端收到发送对象已收到的响应,就会出现严重问题的可能性。

谢谢。

【问题讨论】:

  • 如果我尝试在线程的运行函数中获取输出或输入流,我遇到了类似的问题。套接字阻塞(对于getInputStream和getOutputStream)的问题是因为它在run函数中......解决方案似乎是将它放在构造函数中,保存变量然后在run中引用变量。

标签: java sockets implementation blocking outputstream


【解决方案1】:

您的朋友是对的,但更多的是与 协议的工作方式有关。大大简化了发送到客户端的数据包需要确认。如果客户端没有响应(无法读取传入的数据,计算机负载过重等),服务器将不会收到确认并停止发送数据。 TCP/IP 中内置的这一机制可防止通信的一端发送大量数据,而无需确保另一端接收到这些数据。这反过来又避免了重新发送大量数据的要求。

在 Java 中,这表现为阻塞写入 OutputStream。在客户端准备好接收数据之前,底层 TCP/IP 堆栈/操作系统不允许发送更多数据。

您可以轻松地对此进行测试!我实现了接受连接但无法读取传入数据的简单服务器:

new Thread(new Runnable() {
    @Override
    public void run() {
        try {
            final ServerSocket serverSocket = new ServerSocket(4444);
            final Socket clientSocket = serverSocket.accept();
            final InputStream inputStream = clientSocket.getInputStream();
        } catch (IOException e) {
            e.printStackTrace();
        }

    }
}).start();

还有一个简单的客户端,它可以以 4K 批次发送尽可能多的数据:

final Socket client = new Socket("localhost", 4444);
final OutputStream outputStream = client.getOutputStream();
int packet = 0;
while(true) {
    System.out.println(++packet);
    outputStream.write(new byte[1024 * 4]);
}

客户端循环在 95 次迭代后挂在我的计算机上(您的里程可能会有所不同)。但是,如果我在服务器线程中从 inputStream 读取 - 循环会继续进行。

【讨论】:

  • 如果我尝试在线程的运行函数中获取输出或输入流,我遇到了类似的问题。套接字阻塞(对于getInputStream和getOutputStream)的问题是因为它在run函数中......解决方案似乎是将它放在构造函数中,保存变量然后在run中引用变量。
【解决方案2】:

OutputStream 阻塞。它可能有一些缓冲,但如果服务器从不消耗字节(任何固定缓冲区最终会填满),这对您没有多大帮助。所以你的朋友是对的,你需要在单独的线程中编写,或者使用更高级的东西,比如 nio。

在阅读方面,您可以使用 available() 来避免阻塞。写入端不存在匹配的调用。我希望有。

【讨论】:

    【解决方案3】:

    当然,当您写入套接字时,此写入会被缓冲。 Socket 对象有一个setSendBufferSize() 方法来设置这个缓冲区大小。如果您的写入可以缓存在此缓冲区中,那么当然,您可以立即在以下套接字上进行迭代。否则这个缓冲区需要立即刷新到客户端。因此,在冲洗期间,您将被阻塞。如果您想避免在刷新缓冲区时被阻塞,您必须在非阻塞 I/O 中使用SocketChannel。 无论如何,并发写入多个套接字的最佳选择是使用不同的线程管理每个套接字,以便所有写入可以同时执行。

    【讨论】:

    • “否则这个缓冲区需要立即刷新到客户端”不是正确的放置方式。缓冲区一直在异步写入网络,但在对等方的接收缓冲区中有空间之前无法发送。如果对等端没有读取,或者读取速度变慢,他的接收缓冲区将填满,因此您的发送缓冲区将填满,因此您的后续写入将阻塞。
    • 尽管 Tomasz 的帖子是一个更好的例子,但这正是我在解释 EJP 方面所寻找的。非常感谢。
    猜你喜欢
    • 2012-09-03
    • 1970-01-01
    • 2020-09-16
    • 2011-04-09
    • 2017-01-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多