AsynchronousSocketChannel never returns read = -1 如果缓冲区已完全填满

AsynchronousSocketChannel never returns read = -1 if the buffer is completely filled

我正在尝试从 HTTP 请求中读取数据

如果我的缓冲区大小是 1024 字节,而传入的消息是 768 字节,它可以完美运行,我可以看到缓冲区未满,在这种情况下我可以假设它已完成

read=692, buffer=java.nio.HeapByteBuffer[pos=692 lim=1024 cap=1024] :: read < cap

如果我的缓冲区大小是 512,我仍然可以确定请求何时完成

read=512, buffer=java.nio.HeapByteBuffer[pos=512 lim=512 cap=512]
read=160, buffer=java.nio.HeapByteBuffer[pos=160 lim=512 cap=512] :: read < cap

在缓冲区大小为 64 时,它仍然有效

read=64, buffer=java.nio.HeapByteBuffer[pos=64 lim=64 cap=64]
...
read=64, buffer=java.nio.HeapByteBuffer[pos=64 lim=64 cap=64]
read=32, buffer=java.nio.HeapByteBuffer[pos=32 lim=64 cap=64] :: read < cap

当我进入个位数时,通道似乎正在读取垃圾数据并填满缓冲区

read=7, buffer=java.nio.HeapByteBuffer[pos=7 lim=7 cap=7]
...
read=7, buffer=java.nio.HeapByteBuffer[pos=7 lim=7 cap=7]
read=7, buffer=java.nio.HeapByteBuffer[pos=7 lim=7 cap=7] :: read == cap ?

这意味着我永远不知道何时读取了所有数据 缓冲区大小为 7,我希望最后一个是

692 % 7 = 98

692 - (98*7) = 6

我期待看到

read=6, buffer=java.nio.HeapByteBuffer[pos=6 lim=7 cap=7]

但我看到的是 read=7

如果我使缓冲区与 http 请求的大小完全相同,我会看到

read=692, buffer=java.nio.HeapByteBuffer[pos=692 lim=692 cap=692] :: read == cap ?

但它永远不会回到 channel.read::completed 直到我刷新浏览器,然后读取转到 -1,但是下一个请求挂起并且永远不会到达 read = -1 以触发通道关闭。

read=692, buffer=java.nio.HeapByteBuffer[pos=692 lim=692 cap=692]
read=-1, buffer=java.nio.HeapByteBuffer[pos=0 lim=692 cap=692]
DONE, closing channel
read=692, buffer=java.nio.HeapByteBuffer[pos=692 lim=692 cap=692] << hangs here until refresh, but then the next request does the same again

关于如何使“结束检测”更可靠的任何想法?

AsynchronousServerSocketChannel.open().let { server ->
        server.bind(InetSocketAddress(8080))

        // accept next socket
        server.accept(ByteBuffer.allocate(bufferSize), object : CompletionHandler<AsynchronousSocketChannel, ByteBuffer> {

            override fun completed(channel: AsynchronousSocketChannel, buffer: ByteBuffer) {

                // accept next
                server.accept(ByteBuffer.allocate(bufferSize), this)

                channel.read(
                    buffer,
                    channel,
                    object : CompletionHandler<Int, AsynchronousSocketChannel> {
                        override fun completed(read: Int, channel: AsynchronousSocketChannel) {

                            println(("read=$read, buffer=$buffer"))

                            if (read > 0 || buffer.position() > 0) {
                                buffer.rewind()
                                channel.read(
                                    buffer,
                                    channel,
                                    this
                                )
                            } else {
                                println("DONE, closing channel")
                                channel.close()
                            }
                        }
                        override fun failed(exception: Throwable, channel: AsynchronousSocketChannel) {
                            exception.printStackTrace()
                            channel.close()
                        }
                    }
                )
            }
            override fun failed(exception: Throwable, attachment: ByteBuffer?) {
                exception.printStackTrace()
            }
        })
    }

你通过读取小于缓冲区大小来检测流结束的整个想法是完全错误的。

套接字不是基于消息的,它们是数据流,不能保证数据在发送时到达。当您在一侧发送 692 字节时,它可能会作为 692 字节的单次读取到达,但它可能是例如两次读取:400 和 292 字节。即使读取缓冲区是1024字节。

检测流结束的两种方法是:

  • 发送端关闭套接字,接收端等待-1
  • 实施某种协议(或使用现有协议)在数据流之上对消息进行编码。最简单的就是先发送消息的大小,然后读取,只要我们收到预期的数据量即可。