2015-02-11 313 views
1

我粘貼了下面的服務器端代碼片段。此服務器代碼在正常情況下工作,但是,下面的方案管理破壞代碼。 服務器和客戶端位於同一臺計算機上。我使用了回送地址和實際的IP地址,這沒什麼區別。對SocketChannel的讀取在ServerSocketChannel執行接受後達到數據流尾端

方案

  1. 服務器在線,客戶端發出請求(WritableByteChannel.write(ByteBuffer src)返回12字節,這是正確的大小,但研究顯示,只有意味着12個字節寫入TCP緩衝區)。
  2. 服務器程序已關閉。客戶注意到通道在遠端被關閉並且在它自己的一端關閉它,它沒有提出任何請求。
  3. 服務器再次聯機。
  4. 客戶端嘗試發出請求,但失敗,因爲通道已關閉/無效並且無法重用(即使服務器再次聯機)。
  5. 客戶端檢查服務器的在線狀態,得到肯定的結果,再次連接並立即發出另一個請求。
  6. 服務器接受客戶端(代碼如下),然後處理帶有key.isReadable()條件的if子句,,但是在讀取時失敗,這表示數據流結束。

創建SSCCE會太複雜,請留下評論重要信息是否丟失或太抽象,我會提供更多信息。

問題

如何能在新創建/接受通道上的讀取操作失敗? 我錯過了什麼?我可以採取哪些措施來防止這種情況?

我已經試過了wireshark,但是我無法捕獲指定TCP端口上的任何數據包,即使通信是非常有效的。


問題/附加信息

  • 有可能捕獲數據包到.pcap文件與RawCap
  • 的問題是客戶端檢查服務器狀態的方式。我已經添加了下面的方法。

代碼段

片段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提到的第二點。

服務器希望獲得特定輸入,如果未滿足條件,則該輸入處於不一致狀態。 我已經添加了一些後備措施,現在重新連接過程 - 包括檢查方法 - 工作正常。

回答

2

顯然客戶端必須關閉連接。這是read()返回-1的唯一方法。

注:

  1. 你扔了不恰當的ClosedChannelExceptionread()返回-1。這個例外是由NIO引起的你已經已經關閉了頻道並繼續使用它。它與流結束無關,不應該用於此。如果你必須扔東西,扔EOFException

  2. 你也不應該循環你的方式。你只能循環,而read()返回一個正數。目前,您正在嘗試讀取可能永遠不會到達的數據的同時捱餓選擇循環。

+0

因此,這意味着我應該thouroghly修改重新連接過程。下次我在工作時,我會檢查一些隱藏的close()(...將在下週,因爲我是一名學生)並接受你的答案。 – mike 2015-02-11 16:00:01

+0

Thx也適合您的第二點。我知道你的意思,但這只是一個小小的握手的一部分(帶有兩個小的協議單元,這些單元一起發送),但主要通信使用非阻塞閱讀 – mike 2015-02-11 16:03:40

+0

@mike無關緊要。這是錯誤的,需要修復。否則,當它破壞別人可能會更糟。案例點[這裏](http://stackoverflow.com/a/28421784/207421)。 – EJP 2015-02-11 16:19:20

相關問題