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

sal*_*ira 10 java sockets implementation outputstream blocking

我目前正在为一个项目编写天真的网络代码,并且一位伙伴暗示我有可能当我以一种迭代的方式从服务器向所有客户端发送信息包时,当其中一个客户端没有正确响应时,我可能会遇到严重的延迟.

他因拖钓而闻名,所以当我实现一个现在负责将数据发送到客户端的辅助线程时,我有点怀疑,它有一个队列,服务器只是简单地添加了包,然后被线程读取以发送数据.

在考虑它之后我现在遇到的问题是天气与否,Java Socket的OutputStream实际上将他想要发送的东西排队,因此事先不需要队列.只有当客户端没有收到来自客户端的已收到发送对象的响应时,才会出现发生严重问题的可能性.

谢谢.

Tom*_*icz 12

你的朋友是对的,但它更多地与协议的工作方式有关.需要确认大大简化发送到客户端的数据包.如果客户端没有响应(无法读取传入数据,计算机负载过重等),服务器将不会收到确认并将停止发送数据.内置于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();
Run Code Online (Sandbox Code Playgroud)

还有一个简单的客户端,可以在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]);
}
Run Code Online (Sandbox Code Playgroud)

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


epp*_*uig 5

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

  • “否则这个缓冲区需要立即刷新到客户端”并不是正确的表达方式。缓冲区始终以异步方式写入网络,但只有在对等方的接收缓冲区中有空间时才能发送。如果对等方没有读取或读取速度变慢,则他的接收缓冲区将填满,因此您的发送缓冲区也将填满,因此您后续的写入将被阻塞。 (6认同)