Java 中的 OutputStream 是否阻塞?(插座)

我目前正在为一个项目编写幼稚的网络代码,一位伙伴向我暗示,当我以迭代方式将一组信息从服务器发送到所有客户端时,当其中一个客户端未正确响应时,我可能会遇到严重的滞后。

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

在考虑了一下之后,我现在的问题是天气,Java Socket的输出流实际上将他想要自己发送的东西排队,从而消除了事先排队的需要。只有当服务器阻塞时,只要他没有从客户端获得已接收发送对象的响应,才会发生严重问题的可能性。

谢谢。


答案 1

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

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

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

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


答案 2

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


推荐