[英]java nio SocketChannel.read does not return -1 to indicate end-of-stream
[英]Read on SocketChannel reaches end-of-stream after ServerSocketChannel performs accept
我在下面粘贴了服务器端代码段。 该服务器代码在正常情况下可以工作,但是,以下情况设法破坏了代码。 服务器和客户端在同一台计算机上。 我使用了环回地址和实际的IP地址,没有区别。
脚本
WritableByteChannel.write(ByteBuffer src)
返回12个字节,这是正确的大小,但研究表明,这仅意味着将12个字节写入TCP缓冲区)。 key.isReadable()
条件处理if子句, 但随后在读取失败,表示流结束。 创建SSCCE太复杂了,如果重要信息丢失或过于抽象,请发表评论,我将提供更多信息。
题
新创建/接受的通道如何在读取操作中失败? 我想念什么? 我可以采取什么步骤来防止这种情况?
我已经尝试过wireshark,但是即使通信正常运行,也无法在指定的TCP端口上捕获任何数据包。
问题/其他信息
代码段
片段1
while (online)
{
if (selector.select(5000) == 0)
continue;
Iterator<SelectionKey> it = selector.selectedKeys().iterator();
while (it.hasNext())
{
SelectionKey key = it.next();
it.remove();
if (key.isAcceptable())
{
log.log(Level.INFO, "Starting ACCEPT!");
ServerSocketChannel serverSocketChannel = (ServerSocketChannel) key.channel();
SocketChannel channel = serverSocketChannel.accept();
channel.configureBlocking(false);
channel.register(selector, SelectionKey.OP_READ);
log.log(Level.INFO, "{0} connected to port {1}!",
new Object[] {channel.socket().getInetAddress().getHostAddress(), isa.getPort()});
}
boolean accepted = false;
if (key.isReadable())
{
log.log(Level.INFO, "Starting READ!");
SocketChannel channel = (SocketChannel) key.channel();
bb.clear();
bb.limit(Header.LENGTH);
try
{
NioUtil.read(channel, bb); // server fails here!
}
catch (IOException e)
{
channel.close();
throw e;
}
bb.flip();
片段2
public static ByteBuffer read(ReadableByteChannel channel, ByteBuffer bb) throws IOException
{
while (bb.remaining() > 0)
{
int read = 0;
try
{
read = channel.read(bb);
}
catch (IOException e)
{
log.log(Level.WARNING, "Error during blocking read!", e);
throw e;
}
// this causes the problem... or indicates it
if (read == -1)
{
log.log(Level.WARNING, "Error during blocking read! Reached end of stream!");
throw new ClosedChannelException();
}
}
return bb;
}
片段3
@Override
public boolean isServerOnline()
{
String host = address.getProperty(PropertyKeys.SOCKET_SERVER_HOST);
int port = Integer.parseInt(address.getProperty(PropertyKeys.SOCKET_SERVER_PORT));
boolean _online = true;
try
{
InetSocketAddress addr = new InetSocketAddress(InetAddress.getByName(host), port);
SocketChannel _channel = SocketChannel.open();
_channel.connect(addr);
_channel.close();
}
catch (Exception e)
{
_online = false;
}
return _online;
}
解
问题不在于检查的方法(如果服务可用/服务器处于联机状态)。 问题是EJP提到的第二点。
预期服务器会提供特定的输入,如果不满足该条件,则该输入将处于不一致状态。 我添加了一些后备措施,现在重新连接过程(包括检查方法)可以正常工作。
显然,客户端必须已关闭连接。 这是read()
返回-1的唯一方法。
笔记:
当read()
返回-1时,您将抛出不合适的ClosedChannelException
。 当您已经关闭通道并继续使用它时,NIO会抛出该异常。 它与流的末尾无关,不应该用于此目的。 如果必须扔东西,则抛出EOFException
。
您也不应该按自己的方式循环。 您只应在read()
返回正数时循环。 目前,在尝试读取可能永远不会到达的数据时,您正在选择循环不足。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.