【问题标题】:如果缓冲区完全填满,AsynchronousSocketChannel 永远不会返回 read = -1
【发布时间】:2022-01-23 15:08:28
【问题描述】:

我正在尝试从 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 直到我刷新浏览器,然后 read 转到 -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()
            }
        })
    }

【问题讨论】:

  • docs 的重要部分是 "-1 如果没有字节可以读取因为通道已到达流尾。" i> 只有当通道在另一侧关闭时,您才能到达流尾。
  • 这是有道理的,与我所看到的相符。我需要将此数据流视为没有尽头。

标签: java kotlin sockets asynchronous serversocket


【解决方案1】:

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

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

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

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

【讨论】:

  • 由于这是一个网络浏览器,我无法关闭套接字或控制消息大小。它甚至可能是一个 1GB 的文件上传。我想这意味着我需要读取标题,如果是 GET 请求,则在读取所有必需的标题后停止读取,如果是 POST,请阅读直到我获得正文大小标题,然后停止读取。
  • 我不是 HTTP 专家,但是是的,我认为您是正确的。您应该查找 Content-Length 标头,然后读取,直到获得预期的字节数。如果在读取所有数据之前得到-1,则应认为连接已断开。此外,GET 也可以拥有身体,尽管这种情况很少见。
猜你喜欢
  • 2015-07-29
  • 2013-10-29
  • 2012-05-25
  • 1970-01-01
  • 2021-05-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多