3


3

わかりました、私はここに夢中になります。 私は自分のサーバー用にNIOコードを書き直していて、本当の頭痛の種にぶつかっています。 肝心なのは、NIOを「正しく」することは非常に難しいということです。 Roxのチュートリアルhttp://rox-xmlrpc.sourceforge.net/niotut/を参照する人もいました。 たとえば、キュ​​ーに入れられた発信ByteBuffersが送信された後にのみ、サーバー側で接続を閉じる方法を知る必要があります。 SocketChannel.close()は突発的であり、早すぎるとデータを失う可能性があります。 また、読み込んだByteBufferよりも大きい大きなパケットを送信する必要があります。 Roxコード(そして私が見た他のどのコードも)はこれを扱います。 キャッチされていない例外が適切に処理されていないように思われるところもたくさんあります。 私のテストではいくつかのエラーがあり、NIOの複雑さを考えるとそれらをどのように適切に処理するかは明確ではありません。

とにかく、私がこれらの問題を解決しようとすると、より複雑な微妙なことがより多く現れるようになり、それはかなり複雑になっています。 だから私は全く異なるアプローチを検討しています。 多くの人が、NIOは非常にエラーが発生しやすく、不必要に混乱して複雑であると言っています。 彼らは、各ソケット接続がそれ自身のスレッドで実行されるブロッキングIOを使用する「接続ごとのスレッド」モデルを使用することを提唱しています。 これは良い考えのように思えます、そして(スレッドのための)より高いオーバーヘッドを犠牲にして(NIOのように)すべての接続のために1つのセレクタースレッドを持つことによってフロントエンドのボトルネックを減らすでしょう。 この感情はhttp://paultyma.blogspot.com/2008/03/writing-java-multithreaded-servers.htmlやhttp://mailinator.blogspot.com/2008/02/kill-myth-のような投稿によって反映されています。お願いです - nio - is - not-fast-than.html

コードはNIOに比べて単純なはずですが、私は本当にいくつかのサンプルコードを見て欲しいのです。 何も見つからないようです。 問題は、この「接続ごとのスレッドをブロックするI / O」という戦略が、実際にGoogleで良い結果を得ることができるほど良い名前を持っているとは思わないことです。 誰かが私をいくつかのチュートリアルや簡単な例にリンクさせて、この「古い」入出力方法を使って説明し、スレッドプールを使ってそれを拡大することはできますか? それとも知恵の他の言葉がありますか? どうもありがとう!

3 Answer


1


NIOを使用している場合は、フレームワークを使用することもお勧めします。 私は Apache Minaを使って作業していますので、それをお勧めします。

ブロッキングIOに関しては、本質的にあなたは入ってくる接続を受け入れて、それぞれの接続を処理する追加のスレッドを生み出すリスナースレッドを必要とするでしょう。 最初にApache Felixプロジェクトに寄稿された、そのようなListenerコードの例はここにあります。 完全ではあるが変更されたバージョンを探しているなら、あなたは http://svn.apache.org/viewvc/felix/trunk/shell.remote/src/main/java/org/apache/felix/shell/remote/[browseでできますソースはこちら。

例えば

/ *
    *  1つ以上のライセンスでApache Software Foundation(ASF)にライセンス供与
    *  寄稿者ライセンス契約 で配布されているNOTICEファイルを参照してください。
    *  これは著作権の所有権に関する追加情報のために働きます。
    *  ASFはこのファイルをApache License、Version 2.0の下であなたにライセンスします。
    *  (「ライセンス」)に準拠している場合を除き、このファイルを使用することはできません。
    *  ライセンス あなたは*でライセンスのコピーを入手することができます。
    *  http://www.apache.org/licenses/LICENSE-2.0 *
        *  適用法により要求されない限り、または書面で合意された場合を除き、ソフトウェア
    *  ライセンスに基づいて配布されるものは、「現状のまま」で配布されます。
    *  明示的または黙示的を問わず、いかなる種類の保証または条件もありません。
    *  許可を管理する特定の言語については、ライセンスを参照してください。
    *  ライセンスに基づく制限 * / package org.apache.felix.shell.remote;

import java.io.IOException; import java.io.PrintStream; import java.net.ServerSocket; import java.net.Socket; import java.net.SocketException;

/ **
     *  単一の接続を受け付ける単純なリスナーを実装します。
     *  *
     *  @author Dieter Wimberger(wimpi)* /クラスリスナー{

private int m_Port;プライベートスレッドm_ListenerThread。プライベートブール値m_Stop = false;プライベートServerSocket m_ServerSocket。 private AtomicInteger m_UseCounter; private int m_MaxConnections。

/ **
         *  リスナースレッド(telnetconsole.Listener)でこのリスナーをアクティブにします。 * / public void activate(){//システムプロパティから設定を試みる{m_Port = Integer.parseInt(System.getProperty( "osgi.shell.telnet.port"、 "6666")); } catch(NumberFormatException ex){Activator.getServices()。error( "Listener :: activate()"、ex); {m_MaxConnections = Integer.parseInt(System.getProperty( "osgi.shell.telnet.maxconn"、 "2"));を試してください。 } catch(NumberFormatException ex){Activator.getServices()。error( "Listener :: activate()"、ex); m_UseCounter = new AtomicInteger(0); m_ListenerThread = new Thread(new Acceptor()、 "telnetconsole.Listener"); m_ListenerThread.start(); //アクティブ化

/ **
         *  このリスナーを無効にします。
         *  リスナーのソケットは閉じられるでしょう。
         *  リスナースレッドとそれを返すことができます。 呼び出し側スレッドはリスナーに参加します
         *  それが戻るまで通す(きれいに止まるように)。 * / public void deactivate(){try {m_Stop = true;}} //リスナースレッドを待つm_ServerSocket.close(); m_ListenerThread.join(); catch(Exception ex){Activator.getServices()。error( "Listener :: deactivate()"、ex); //無効化

/ **
         *  リスナーの受け入れロジックをRunnableとして実装するクラス。 * / privateクラスAcceptorはRunnableを実装しています{

/ **
             *  サーバーソケットを常時待機し、着信接続を処理します。
             *  1つの接続が受け入れられてシェルにルーティングされ、他のすべての接続はシェルに接続されます。
             *  通知を受けて終了します。
             *  スレッドがServerSocket.accept()呼び出しからブロック解除できるようにするメカニズム
             *  現在別のスレッドからServerSocketを閉じています。 停止フラグが設定されると、
             *  これにより、スレッドは戻って停止します。 * / public void run(){try {/ * int floodProtectionで指定されたサイズの接続キューでサーバーソケットが開かれる 通常の状況下での同時ログイン処理は適切に処理されるべきですが、大規模な並列プログラムログインによるサービス拒否攻撃はこれで防がれるべきです。 * / m_ServerSocket = new ServerSocket(m_Port、1); do {try {Socket s = m_ServerSocket.accept();}; if(m_UseCounter.get()> = m_MaxConnections){//メッセージPrintStreamを拒否するout = new PrintStream(s.getOutputStream()); out.print(INUSE_MESSAGE); out.flush(); // close.close()を閉じます。 s.close(); } else {m_UseCounter.increment();} //接続スレッドで実行します。Thread connectionThread = new Thread(new Shell(s、m_UseCounter)); connectionThread.start(); catch(SocketException ex){}} while(!m_Stop);

} catch(IOException e){Activator.getServices()。error( "Listener.Acceptor :: activate()"、e);} //実行

//内部クラスAcceptor

private static final String INUSE_MESSAGE = "接続が拒否されました。\ r \ n"
            + "All possible connections are currently being used.\r\n";

//クラスリスナー

あなたは他の例を見つけることができますhttp://jamod.cvs.sourceforge.net/viewvc/jamod/jamod/src/net/wimpi/modbus/net/ModbusTCPListener.java?revision=1.4

ブロッキングモデルに対するNIOの優位性は、負荷が大きいほど有効になります。 ある時点から、スレッドの作成、管理、およびコンテキストの切り替えに余分な作業が必要になるため、システムのパフォーマンスが制限されます。


0


JDKのsample / nioディレクトリを調べることをお勧めします。 これはあなたが言及した二つを含む多くの簡単な例を持っています。


0


NIOを直接使用するのではなく、https://grizzly.dev.java.net/ [Grizzly]などのより高レベルのフレームワークを使用することを検討することもできます。 フレームワークはあなたがNIOの微妙さよりもあなたのユースケースに集中することを可能にするべきです。