Java NIO FileChannel - 从 Android TUN 网络接口清空读取

Java NIO FileChannel - Empty reads from Android TUN network interface

上下文: 我最近开始在我的项目中使用 java.nio,它利用了 Android 的 VpnService。在我的实现中,我将 VpnServiceestablish() 方法返回的 FileDescriptor 包装到 java.nio.FileChannel 中,如下所示。

private val outboundNetworkChannel = FileInputStream(fd).channel

在那之后,我有一个 kotlin 协同程序,它无限期地从 FileChannel 读取并处理出站 IPv4 / IPv6 数据包。

问题: 下面提到的代码片段有效,但我看到 FileChannel 发生了很多空读取,这反过来又旋转了 while 循环没必要。

fun reader() = scope.launch(handler) {
    while (isActive) {
        val pkt = read()
        if(pkt !== DUMMY){
            // Send the read IPv4/IPv6 packet for processing
        }
    }
}

private suspend fun read(): IPDatagram =
    withContext(Dispatchers.IO) {
        val bytes = ByteBufferPool.acquire()
        outboundChannel.read(bytes) // Returns a lot of empty reads with return value as 0
        return@withContext marshal(bytes) // Read IPv4/IPv6 headers and wrap the packet
    }

我在找什么: 事实上,我知道 FileChannel 是一个阻塞通道,在这种情况下,因为该通道由网络支持接口,它可能没有准备好读取的数据包。有没有更好的方法,有/没有 FileChannel,这将导致更有效的实施,而不会浪费宝贵的 CPU 周期?我也乐于接受新想法:)

我在研究了 VpnService 的 Android 文档后设法解决了这个问题。默认情况下,当使用 VpnService.Builder 建立 VPN 连接时,fd 处于非阻塞模式。从 API 21 级开始,可以 setBlocking(true)

public VpnService.Builder setBlocking (boolean blocking)

的文档中所述

Sets the VPN interface's file descriptor to be in blocking/non-blocking mode. By default, the file descriptor returned by establish() is non-blocking.