2013-06-21 30 views
0

我正在寫一個服務器到客戶端之間交換消息。還有一個問題需要解決,即如何在客戶關閉時發佈頻道。我所做的就是啓動監視器線程,其中監視所有客戶端映射,並且如果在嘗試寫入()時檢測到異常,我會嘗試刪除()通道。但是,在關閉客戶端之後,monitor線程中的write()方法不會拋出異常,所以無用的通道永遠不會被釋放。任何人都知道爲什麼?SocketChannel的write()方法不拋出一個異常時,它應該做的

public class ServerMonitor extends Thread{ 
private Map<String, SocketChannel> allClients; 
private Set set; 
private Iterator it; 
private Entry entry; 
private SocketChannel channel; 
private ByteBuffer buf; 

public ServerMonitor(Map<String, SocketChannel> allClients) { 
    this.allClients = allClients; 
    buf = ByteBuffer.allocateDirect(10); 
    byte b = 0; 
    buf.put(b); 
    buf.flip(); 
} 

public void run(){ 
    while(true) { 
     if(!allClients.isEmpty()) { 
      set = allClients.entrySet(); 
      it = set.iterator(); 
      while(it.hasNext()) { 
       entry = (Entry) it.next(); 
       channel = (SocketChannel) entry.getValue(); 
       try{ 
        channel.write(buf); 
       } catch(Exception e) { 
        allClients.remove(entry.getKey()); 
        //set.remove(entry); 
       } 
      } 
     } 
     try { 
      Thread.sleep(1000 * 5); 
     } catch (InterruptedException e) { 
      e.printStackTrace(); 
     } 
    } 
} 

}

+0

'this.allClients = allClients;'< - 這是相當危險的,除非你真的知道自己在做什麼......如果主叫方修改了地圖,你的類地圖將看到的變化! 'ConcurrentModificationException'的好源... – fge

+0

@fge我想執行this.allClients = allClients;因爲當監視器線程檢測到無法寫入字節的通道時,我需要修改源allClients(釋放一個無用的通道)。 – dastan

回答

1

寫入TCP套接字本地緩存和異步提上線。因此,在對等關閉失敗後,您無法依賴第一次寫入。您可以依靠後續的寫入失敗,但可能需要一些寫入才能到達那裏。

+0

那麼我可以在通道中寫入bytebuffer.size()字節,以便能夠在第一次寫入時檢測到異常? – dastan

+0

我剛剛回答了這個問題。不需要ByteBuffer.size()與它無關。 – EJP

+0

大小取決於TCP本身? – dastan

0

編寫了TCP發送數據的應用程序時,我碰到的這個問題。您已經發現,知道客戶是否已關閉連接的唯一方法是撥打write(...)致電IOException。這幾乎是它的工作方式。

有一個清晰的解決方案。首先,您必須始終處理客戶在不知情的情況下斷開連接的情況,並在處獲得時正確刪除它們。但是,如果客戶端發送一條消息告訴服務器它正在斷開連接,那麼可以使用它在您看到它時關閉連接。

+0

是的,我真的照你說的做,但是寫一個調用(...)不會導致IOException,這使我困惑。 – dastan

+0

就像EJP所說的那樣,你不能依賴特定的寫入失敗,但最終會失敗。寫入套接字的數據越多,失敗的速度就越快。 – VolatileDream

相關問題