簡體   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