2012-08-17 29 views
2

比方说,我们有SocketChannel(处于非阻塞模式),它已向Selector注册以获得读取兴趣。比方说select()之后,Selector告诉我们这个通道已经准备好读取,并且我们有一些ByteBuffer。我们想从我们的通道读取一些字节到这个缓冲区(在读取之前,ByteBuffer被清除)。为此,我们使用channel的read()方法返回实际读取的字节数。让我们假设从通道读取这个数字是正数,并且ByteBuffer的方法hasRemaining()返回true。在这种情况下立即尝试从同一频道读取更多内容是否实用? write()的问题。如果write()返回正值并且并非缓冲区的所有内容都已发送,那么在write()返回0之前立即再次尝试是否可行?使用Java NIO的高效读/写方法

+0

只要您处于非阻塞状态,您应该读/写,直到返回值为<= 0,这意味着它会阻塞或结束流。顺便提一下,你的频道通常会写入准备好,所以你应该避免使用写入兴趣,因为它会让你的'select'循环旋转。当写入数据之前'SocketChannel.write'会被阻塞时,才会对写入准备状态感兴趣。 – oldrinb 2012-08-17 02:13:28

+0

@veer是的,我忘了提及所有这些都是关于非阻塞模式。 – Rubio 2012-08-17 02:16:26

+0

但是直到返回值<= 0才尝试真的有效吗?我的意思是,如果上一次read()返回> 0但未能完全填充缓冲区,是否真的值得尝试再次读取?这个下一个read()会返回正值的概率很高吗?或者正返回值和非完全填充的缓冲区表明我们(很可能)耗尽了内部套接字缓冲区并应该返回到选择状态? – Rubio 2012-08-17 02:28:11

回答

0

如果您得到一个短读取结果,没有更多的数据要读取没有阻塞,所以你不能再读,直到存在。否则,下一次读取几乎肯定会返回零或-1。

如果读取填充缓冲区,从一个连接的角度来看,它可能是有意义的,直到它返回< = 0,但是您正在从其他通道中窃取周期。你也需要考虑公平。一般来说,你应该做一个阅读并继续迭代选定的键。如果有更多的数据,那么选择下次会告诉你。

使用大缓冲区。

这也意味着在每次读取之前清除缓冲区是错误的。您应该用flip/get/compact循环获取数据,然后缓冲区已准备好再次读取,您不会冒失去数据的风险。这反过来意味着您需要每个连接的缓冲区。

+0

感谢您的回答!缓冲区清除仅仅是为了举例 - 为了澄清缓冲区的初始状态。 – Rubio 2012-08-17 04:37:20

0

这一切都取决于数据到达的数据速率以及应用程序的延迟要求。如果您根本不关心延迟,那么您可能会延迟读取兴趣,直到您怀疑有足够的数据到达缓冲区,才会获得稍高的带宽。

虽然你必须小心。延迟读取可能会迫使内核缓冲更多数据,可能会填充缓冲区,并且必须开始丢弃数据包或以其他方式进行一些流量控制。这不仅会杀死最后一段的任何好处。

因此,一般来说,您希望尽可能早地阅读。批量读取的好处很少,最大的潜在缺陷可能很大。请记住,您看到的是非完整读取的事实意味着您正在处理数据的速度比进入的速度要快。换句话说,您处于有CPU刻录的状态,所以额外的较小读取的开销基本上是免费的。

+0

我并不完全同意“批量读取的好处很少” - 请小心,因为许多小读取会给机械驱动器带来巨大的负担。当你有多个用户时,这会放大,因为驱动器会寻找很多,只能读取小块数据。同时请求数量,可用内存和延迟要求之间的平衡。 – Joe 2012-11-05 20:03:47

+0

@Joe:首先,我们谈论的是网络,而不是磁盘。即使它是磁盘,磁盘(和操作系统)也有缓存,所以你实际上不会在每个请求上寻找。 – 2012-11-05 20:11:45