2009-05-25 29 views
1

嗨我試圖實現一個簡單的Java NIO服務器;它使用選擇器註冊socketChannel。因此,我希望聽取客戶的意見併發回一些迴應。在向選擇器註冊socketChannel之後,即使客戶端(非NIO)發送了一些數據,服務器也無法讀取;生成的密鑰仍在迭代中。NIO服務器無法收聽客戶端

詳細視圖:服務器端:

**First thread**: 

公共無效的run(){ 而(真){

ServerSocketChannel serverSocketChannel = ServerSocketChannel.open(); 
    serverSocketChannel.configureBlocking(true); 
    serverSocketChannel.socket().bind(inetAdressOfServer); 
    SocketChannel clientChannel = serverSocketChannel.accept(); 
    new Listener("").addSocketChannel(clientChannel); 

}} 

**Second Thread**: 

    static Selector selector = Selector.open(); 
    public boolean addSocketChannel(SocketChannel clientChannel) { 

     SelectionKey key = clientSocketChannel.register(selector, selector.OP_READ|SelectionKey.OP_WRITE);    
     key.attach(new ChannelCallback(clientSocketChannel)); 
     return key.isValid(); 
    } 

    public void run() { 

     Set keysSet = selector.keys(); 
     Iterator i = keysSet.iterator();   
     while (i.hasNext()) { 
      SelectionKey key = (SelectionKey) i.next(); 
     } 

     if (key.isReadable()) { 
      //read and do something 
     } 
    } 



Client Side: 

Socket socket = new Socket(serverIP, serverPort);  
DataOutputStream dos = new DataOutputStream(socket.getOutputStream());  
dos.writeBytes(str + "\n"); 

注意:當在單線程中完成,同樣的程序工作,但當以上述方式實施時,不會聽取客戶的意見。 請幫我解決這個問題。

+0

我每時每刻都在想,人們仍然會用迭代器的6年Java-1.4寫作風格。舊的Java書籍和教程仍然如此廣泛?這讓我很難過。 – 2009-05-25 18:42:20

回答

1

很難看到你在那裏做了什麼,但它看起來好像你已經標記爲「第二線程」被兩個線程使用(在實現Runnable /擴展Thread和實際線程方面有些混淆?)。特別是,我猜new Listener構造並啓動一個線程。然後在第一個線程中調用addSocketChannel。因此,有一個競爭條件。

另外,使selector爲靜態是一個糟糕的主意。

+0

嗨湯姆,對不起,代碼不清楚。第一個線程運行在不同的類中,該類具有接受套接字通道的邏輯並將其傳遞給註冊。 請你讓我知道應該做什麼樣的修改。謝謝。 – Nilesh 2009-05-25 13:43:32

+0

看起來可能最簡單的解決方法是將調用addSocketChannel移動到Listener構造函數(並在啓動線程之前調用它)。但是,你真的需要把你的線程整理出來。 – 2009-05-25 15:27:46

1

閱讀從另一個線程讀取的作品,這裏是您的代碼顯而易見的問題。

public void run() { 
    Set keysSet = selector.keys(); 

在這裏,您是從迭代器取按鍵,但沒有代碼永遠在選擇做選擇()或selectNow(),所以這集將永遠是空的。

Iterator i = keysSet.iterator();   
    while (i.hasNext()) { 
     SelectionKey key = (SelectionKey) i.next(); 
    } 
    if (key.isReadable()) { 
     //read and do something 
    } 
} 

這甚至沒有編譯,關鍵的'讀'檢查必須在while塊內完成。

SelectionKey key = clientSocketChannel.register(selector, 
               SelectionKey.OP_READ | 
               SelectionKey.OP_WRITE);    

兩個問題:通道應在非阻塞模式被設置在此之前完成,並SelectionKey.OP_WRITE不應設置,除非你想關鍵要每次運行一個選擇的時間返回。

如果您實際計劃執行寫操作,則只應設置SelectionKey.OP_WRITE。

最後,在這裏使用兩個線程是非常傳統的。這樣做的推薦方法是使用OP_ACCEPT將ServerSocketChannel註冊到Selector,然後在與讀/寫相同的線程上運行ServerSocket上的accept。

相關問題