我目前正在使用非阻塞 SocketChannel (Java 1.6) 作为 Redis 服务器的客户端。Redis 直接通过套接字接受纯文本命令,由 CRLF 终止并响应类似,一个简单的示例:
发送:'PING\r\n'
RECV: '+乒乓球\r\n'
Redis 还可以返回大量回复(取决于您要求的内容),其中包含许多以 \r\n 结尾的数据部分,所有这些都作为单个响应的一部分。
我正在使用标准的while(socket.read() > 0) {//append bytes}循环从套接字读取字节并将它们重新组装成客户端的回复。
注意:我没有使用选择器,只是连接到服务器的多个客户端 SocketChannel,等待服务发送/接收命令。
我感到困惑的是 SocketChannel.read() 方法在非阻塞模式下的合同,具体来说,如何知道服务器何时完成发送并且我有整个消息。
我有一些方法可以防止返回过快并让服务器有机会回复,但我坚持的一件事是:
- read()是否有可能返回字节,然后在后续调用中不返回字节,但在另一个后续调用中再次返回一些字节?
基本上,如果我收到至少 1 个字节并最终read()返回 0,我是否可以相信服务器已完成对我的响应,然后我知道我已经完成了,或者服务器可能只是忙并且可能会喷出一些如果我等待并继续尝试更多字节?
如果即使在 read() 返回 0 字节后(在之前的成功读取之后)它仍然可以继续发送字节,那么我不知道如何判断服务器何时完成与我的对话,实际上我很困惑 java.io.*样式通信甚至会知道服务器何时“完成”。
正如你们所知,除非连接已死,否则 read 永远不会返回 -1 并且这些是标准的长期数据库连接,所以我不会在每个请求时关闭和打开它们。
我知道一个流行的回答(至少对于这些 NIO 问题)是看看 Grizzly、MINA 或 Netty——如果可能的话,我真的很想在采用一些 3rd 方依赖项之前了解这一切是如何在原始状态下工作的。
谢谢你。
奖金问题:
我最初认为阻塞 SocketChannel 将是解决此问题的方法,因为在我处理他们的命令并给他们回复之前,我真的不希望调用者做任何事情。
如果这最终是一个更好的方法,我有点困惑看到 SocketChannel.read() 阻塞,只要没有足够的字节来填充给定的缓冲区......没有逐字节读取所有内容我无法弄清楚这个默认行为实际上是如何使用的......我永远不知道从服务器返回的回复的确切大小,所以我对 SocketChannel.read() 的调用总是阻塞直到超时(此时我终于看到内容位于缓冲区中)。
我不清楚使用阻塞方法的正确方法,因为它总是挂断读取。