Java Socket / Output Stream 写入:它们会阻塞吗?

45
如果我只是在输出流上向套接字编写,它会阻塞吗?只有读取可能会阻塞,是吗?有人告诉我写入也可能会阻塞,但我只看到套接字的读取方法有超时特性- Socket.setSoTimeout()
对我来说,写入可能被阻塞是没有意义的。

没有超时功能并不意味着它不会阻塞。一个表明它会阻塞的迹象是缺少返回值:如果它不会阻塞,为什么不返回实际写入的字节数?另一个迹象是尝试使用它。 - user207421
2个回答

55
如果我只是在输出流上写入套接字,它会阻塞吗?只有读取操作才会阻塞,对吗?
对于套接字的写入也可能会阻塞。操作系统只会缓冲一定量的未传输(或已传输但未确认)数据。如果你写入的速度比远程应用程序读取的速度快,套接字最终会堵塞,你的写入调用将会阻塞。
这对我来说没有意义,写入怎么会阻塞呢?
操作系统内核无法为缓冲未发送或未确认的数据提供无限量的内存。写入阻塞是处理这个问题的最简单方法。
回答这些后续问题:
有没有一种机制可以设置超时时间?我不确定它会有什么行为...也许如果缓冲区已满,会丢弃数据?或者可能删除缓冲区中的旧数据?
在java.net.Socket上没有设置写超时的机制。有一个Socket.setSoTimeout()方法,但它影响accept()和read()调用...而不是write()调用。显然,如果使用NIO、非阻塞模式和选择器,可以获得写超时,但这并不像你想象的那样有用。
一个正确实现的TCP堆栈不会丢弃缓冲数据,除非连接关闭。然而,当你遇到写超时时,无法确定当前在操作系统级别缓冲区中的数据是否已被对端接收。另一个问题是,你不知道上次写入的数据有多少实际传输到操作系统级别的TCP堆栈缓冲区中。在没有某种应用层协议来重新同步流的情况下,写超时后唯一安全的做法就是关闭连接。
相比之下,如果您使用数据报套接字(例如UDP),write()调用不会阻塞很长时间。但是缺点是,如果存在网络问题或远程应用程序无法跟上,消息将被丢弃,而没有通知任何一方。此外,您可能会发现消息有时会以乱序传递给远程应用程序。解决这些问题将由您(开发人员)来处理。
1 - 但不适用于DatagramSocket
2 - 理论上可以做到这一点,但对于大多数应用程序来说,在已经可靠(到一定程度)的TCP/IP流之上实施额外的重新同步机制是没有意义的。而且,如果确实有意义,您还需要处理连接关闭的可能性...所以更简单的方法是假设它已关闭。
3 - Javadoc并未说明send不会完全阻塞。但我期望操作系统会丢弃出站数据报而不是阻塞。

7

链接损坏,而“NIO和选择器”不能回答有关OutputStream的问题。 - user207421

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接