java套接字/输出流写入:它们会阻塞吗?

2022-09-01 02:34:37

如果我只是写到输出流上的套接字,它会阻塞吗?只有读取才能阻止,对吧?有人告诉我写入可以阻塞,但我只看到套接字的读取方法的超时功能 - .Socket.setSoTimeout()

对我来说,写入可能会阻塞是没有意义的。


答案 1

套接字上的写入也可能阻塞,特别是如果它是 TCP 套接字。操作系统只会缓冲一定数量的未传输(或传输但未确认)的数据。如果你写东西的速度比远程应用程序能够读取它的速度快,套接字最终会备份,你的调用将被阻止。write

回答以下后续问题:

那么有没有一种机制来为此设置超时呢?我不确定它会有什么行为...如果缓冲区已满,可能会丢弃数据?或者可能删除缓冲区中的旧数据?

没有在 java.net.Socket 上设置写入超时的机制。有一种方法,但它影响并调用...而不是电话。显然,如果您使用NIO,非阻塞模式和选择器,则可以获得写入超时,但这并不像您想象的那么有用。Socket.setSoTimeout()accept()read()write()

正确实现的 TCP 堆栈不会丢弃缓冲数据,除非连接已关闭。但是,当您获得写入超时时,不确定当前在操作系统级缓冲区中的数据是否已被另一端接收...还是没有。另一个问题是,您不知道上一个数据中有多少数据实际上被传输到操作系统级TCP堆栈缓冲区。如果没有一些用于重新同步流*的应用程序级协议,则在超时后唯一安全的做法是关闭连接。writewrite

相反,如果使用 UDP 套接字,则调用不会阻塞任何很长的时间长度。但缺点是,如果存在网络问题或远程应用程序没有跟上,消息将被丢弃在地板上,而不会向任何一端发出通知。此外,您可能会发现消息有时无序传递到远程应用程序。由您(开发人员)来处理这些问题。write()

* 理论上可以做到这一点,但对于大多数应用程序来说,在已经可靠(在某种程度上)的 TCP/IP 流之上实现额外的重新同步机制是没有意义的。如果它确实有意义,您还需要处理连接关闭的可能性...因此,假设它关闭会更简单。


答案 2

执行此操作的唯一方法是使用 NIO 和选择器。

请参阅 Sun/Oracle 工程师在此错误报告中的书面说明:https://bugs.java.com/bugdatabase/view_bug.do?bug_id=4031100


推荐