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
。
- 实施某种协议(或使用现有协议)在数据流之上对消息进行编码。最简单的就是先发送消息的大小,然后读取,只要我们收到预期的数据量即可。
我正在尝试从 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
。 - 实施某种协议(或使用现有协议)在数据流之上对消息进行编码。最简单的就是先发送消息的大小,然后读取,只要我们收到预期的数据量即可。