Java中的OutputStream是阻塞的吗? (sockets)

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

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

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

谢谢。

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

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

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

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