繁体   English   中英

ServerSocketChannel执行接受后,对SocketChannel的读取达到流的末尾

[英]Read on SocketChannel reaches end-of-stream after ServerSocketChannel performs accept

我在下面粘贴了服务器端代码段。 该服务器代码在正常情况下可以工作,但是,以下情况设法破坏了代码。 服务器和客户端在同一台计算机上。 我使用了环回地址和实际的IP地址,没有区别。

脚本

  1. 服务器处于联机状态,客户端发出请求( WritableByteChannel.write(ByteBuffer src)返回12个字节,这是正确的大小,但研究表明,这仅意味着将12个字节写入TCP缓冲区)。
  2. 服务器程序已关闭。 客户注意到该通道在远程端已关闭,而在其自身端已关闭,它没有发出任何请求。
  3. 服务器再次联机。
  4. 客户端尝试发出请求,但失败了,因为通道已关闭/无效且无法重用(即使服务器再次联机)。
  5. 客户端检查服务器的在线状态 ,得到肯定的结果,再次连接并立即发出另一个请求。
  6. 服务器接受客户端(下面的代码),然后使用key.isReadable()条件处理if子句, 随后在读取失败,表示流结束。

创建SSCCE太复杂了,如果重要信息丢失或过于抽象,请发表评论,我将提供更多信息。

新创建/接受的通道如何在读取操作中失败? 我想念什么? 我可以采取什么步骤来防止这种情况?

我已经尝试过wireshark,但是即使通信正常运行,也无法在指定的TCP端口上捕获任何数据包。


问题/其他信息

  • 使用RawCap可以将数据包捕获到.pcap文件中
  • 问题是客户端检查服务器状态的方式。 我在下面添加了方法。

代码段

片段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的唯一方法。

笔记:

  1. read()返回-1时,您将抛出不合适的ClosedChannelException 已经关闭通道并继续使用它时,NIO会抛出该异常。 它与流的末尾无关,不应该用于此目的。 如果必须扔东西,则抛出EOFException

  2. 您也不应该按自己的方式循环。 您只应在read()返回正数时循环。 目前,在尝试读取可能永远不会到达的数据时,您正在选择循环不足。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM