2009-05-25 14 views
1

こんにちは私は単純なJava NIOサーバーを実装しようとしています。ソケットセレクタはセレクタにsocketChannelを登録します。したがって、私はクライアントに耳を傾け、いくつかの応答を返信したい。 socketChannelがセレクタに登録された後、クライアント(NIO以外)が何らかのデータを送信しても、Serverは読み取ることができません。生成されたキーはまだ反復されています。NIOサーバーがクライアントを聞くことができません

詳細表示:サーバー側:

**First thread**: 

ます。public void実行(){ ながら、(真){

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"); 

NB:単一のスレッドで実行すると、同じプログラムが動作しますが、上記の方法で実装された場合、クライアントに耳を傾けません。 この問題を解決するのを手伝ってください。

+0

今日、人々は6年前のJava-1.4書式をイテレータに使用しているのはいつも不思議です。古いJavaの本とチュートリアルはまだまだ広まっていますか?それは私を悲しくする。 –

答えて

1

「2番目のスレッド」としてマークしたものが両方のスレッドで使用されているかのように見えます(Runnable/Threadの実装と実際のスレッドを混同していますか?特に、私はnew Listenerの構成を推測し、スレッドを開始します。最初のスレッドでaddSocketChannelに電話をかけています。したがって、競合状態が存在します。

また、selectorを静的にすることは賢明ではありません。

+0

こんにちはトム、申し訳ありませんがコードははっきりしていませんでした。最初のスレッドは、ソケットチャネルを受け入れるロジックを持つ異なるクラスで実行され、ソケットチャネルを渡して登録されます。 変更内容をお知らせください。ありがとう。 – Nilesh

+0

addSocketChannelへの呼び出しをリスナーコンストラクタに移動して(スレッドを開始する前に呼び出す)、おそらく動作するかもしれない最短の修正のようです。しかし、実際にスレッドをソートする必要があります。 –

+0

こんにちはTom、NIOスレッドの学習と改善の指針を教えてください。また、静的なセレクタの代替があるかどうか、私に知らせることもできます。これ以上のアプローチはありませんか? – Nilesh

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 
    } 
} 

これもコンパイルされませんが、キーに「読み取り」のチェックはしばらくの間、ブロック内で行わなければなりません。

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

2つの問題:これが行われる前に、チャネルが非ブロックモードに設定する必要があり、あなたは、キーは、あなたが選択を実行するたびに返される場合を除き、SelectionKey.OP_WRITEを設定するべきではありません。

実際に書き込みを行う予定がある場合は、SelectionKey.OP_WRITEを設定する必要があります。

最後に、ここで2つのスレッドを使用することは非常に慣習的ではありません。これを行うには、ServerSocketChannelをセレクタにOP_ACCEPTで登録し、読み込み/書き込みと同じスレッドでServerSocketでacceptを実行します。

+0

Nuojiありがとうございました。私はスレッドのコンセプトに関してあなたに同意しますが、これは普遍的なものですが、パフォーマンスの最適化を試してみてください。 データを読み込んだ後でkey.isReadableをfalseに設定する方法を教えてください。 – Nilesh

+1

通常、iterator.remove()を使用して、読み取り後にセットからキーを削除します。私はそれ以外の場合は、あなたがselect()を行うたびに表示されることを覚えているようです。 remove()の後に、再度読み込み可能なデータがあるまで、キーが選択されたキーの一部ではないことが保証されます。 もちろん、キーの読み上げをオフにしたい場合(つまり、この接続で読み続ける必要がない場合)は、代わりにkey.interestOps(int ops)を使用してこのキーの新しい関心を設定する必要があります例えば、読み出しも書き込みもない場合は0)。 – Nuoji

+0

'Selector.keys()'は* ready *キーではなく*登録された*キーのセットを返します。 – EJP