次のシステムは、原則、プロセスなどを通じてJava Nioの機能メカニズムと使用法の詳細な紹介を提供します。それを学びましょう。
序文
この記事では、主にJavaのIOメカニズムについて説明しています
2つのピースに分かれています。
最初の部分では、マルチスレッドの下でのIOメカニズムについて説明します。 2番目の部分では、IOメカニズム(新しいIO)の下でCPUリソースの無駄を最適化する方法を説明しています
エコーサーバー
単一のスレッドでソケットメカニズムを導入する必要はありません。わからない場合は、多くのスレッドの下で情報を確認できます。ソケットを使用した場合はどうなりますか?
私たちは誰もが理解するのを助けるために最も単純なエコーサーバーを使用します
まず、マルチスレッド中のサーバーとクライアントのワークフロー図を見てみましょう。
複数のクライアントがリクエストを同時にサーバーに送信することがわかります
サーバーは、複数のスレッドが対応するクライアントに一致するようにするための尺度を作成しました。
そして、各スレッドはクライアントリクエストを単独で完了します
原則が終了したら、それがどのように実装されているかを見てみましょう
ここに簡単なサーバーを書きました
スレッドプールテクノロジーを使用して、スレッドを作成します(特定のコード関数についてコメントしました)。
パブリッククラスMyServer {private static executorservice executorservice = executors.newcachedthreadpool(); //スレッドプールの作成プライベート静的クラスのハンドルメスは実行可能{//新しいクライアントリクエストが発生したら、このスレッドを作成してソケットクライアントを処理します。 //クライアントを作成するpublic handlemsg(socket client){// parameter Binding this.client = client; } @Override public void run(){bufferedreader bufferedreader = null; //文字を作成するキャッシュ入力ストリームprintwriter printwriter = null; //文字書き込みストリームを作成するtry {bufferedreader = new BufferedReader(new inputStreamReader(client.getInputStream()); //クライアントの入力ストリームPrintWriterを取得= new PrintWriter(client.getOutputStream()、true); //クライアントの出力ストリームを取得すると、trueは文字列inputline = nullを更新することです。 long a = system.currenttimemillis(); while((inputline = bufferedreader.readline())!= null){printwriter.println(inputline); } long b = system.currenttimemillis(); system.out.println( "このスレッドが次のようになりました:"+(ba)+"秒!"); } catch(ioexception e){e.printstacktrace(); }最後に{try {bufferedreader.close(); printwriter.close(); client.close(); } catch(ioexception e){e.printstacktrace(); }}}} public static void main(string [] args)throws ioException {//サーバーのメインスレッドは、ループServersocket Server = new Serversocket(8686)でクライアント要求をリッスンするために使用されます。 //ポート8686ソケットクライアント= nullでサーバーを作成します。 while(true){// loop risten client = server.accept(); //サーバーはクライアントリクエストSystem.out.println(client.getRemoteSocketAddress()+"クライアント接続が成功!"); executorservice.submit(new Handlemsg(client)); //処理のためにスレッドプールを介してクライアントリクエストをhandlmsgスレッドに入れます}}}上記のコードでは、クラスを使用して、シンプルなエコーサーバーを書き込み、デッドループを使用してメインスレッドでポートリスニングを有効にします。
シンプルなクライアント
サーバーを使用すると、アクセスして、文字列データを送信できます。サーバーの機能は、これらの文字列を返して、スレッドを印刷することです。
サーバーに応答する簡単なクライアントを書きましょう。
public class myClient {public static void main(string [] args)throws ioexception {socket client = null; printwriter printwriter = null; BufferedReader BufferedReader = null; try {client = new Socket(); client.connect(new inetsocketAddress( "localhost"、8686)); printwriter = new PrintWriter(client.getOutputStream()、true); printwriter.println( "hello"); printwriter.flush(); BufferedReader = new BufferedReader(new inputStreamReader(client.getInputStream())); //サーバーと出力System.out.printlnによって返された情報を読み取ります( "サーバーからの情報は:"+bufferedreader.readline()); } catch(ioexception e){e.printstacktrace(); }最後に{printwriter.close(); bufferedreader.close(); client.close(); }}}コードでは、文字ストリームを使用してHello Stringを送信します。コードが問題ない場合、サーバーはハローデータを返し、設定したログ情報を印刷します。
エコーサーバーの結果表示
実行しましょう:
1.サーバーを開き、ループリスニングを有効にします。
2。クライアントを開く:
クライアントが返品結果を印刷していることがわかります
3.サーバーログを確認します。
非常に良い、シンプルなマルチスレッドソケットプログラミングが実装されています
しかし、それについて考えてください:
クライアントがリクエストした場合は、IOの書き込み中にサーバーにスリープを追加します。
各リクエストを10秒間サーバースレッドを占有する
その後、クライアントのリクエストがたくさんあり、それぞれがそれほど長く取り上げています
その後、サーバーの統一機能が大幅に削減されます
これは、サーバーに多くの重いタスクがあるからではなく、サービススレッドがIOを待っているからです(受け入れ、読み取り、書き込みがすべてブロックされているためです)
高速CPUが非効率的なネットワークioを待つことは非常に無給ですio
現時点で何をすべきですか?
ニオ
新しいIOは上記の問題を正常に解決しました。どのようにそれを解決しましたか?
IOがクライアント要求を処理するための最小ユニットはスレッドです
NIOは、スレッドよりも1レベルの1つのレベルのユニットを使用します:チャンネル(チャネル)
NIOでは、すべてのレセプション、読書、執筆、その他の操作を完了するために必要なスレッドは1つだけであると言えます。
NIOを学ぶには、最初にその3つのコアポイントを理解する必要があります
セレクター、セレクター
バッファー、バッファー
チャネル、チャネル
ブロガーは才能があるわけではないので、彼は醜い写真を描き、印象を深めました^。 ^
TCPの下にある別のNIOワークフロー図を教えてください(ラインを描くのは非常に難しい...)
大まかに理解するだけで、段階的に行きましょう
バッファ
まず第一に、あなたはバッファーが何であるかを知る必要があります
データインタラクションは、IOメカニズムのようなストリームを使用しなくなりました
代わりに、バッファ(バッファー)を使用します
ブロガーは、写真が最も簡単に理解できると考えています
それで...
ワークフロー全体のバッファーがどこにあるかを見ることができます
実際のものを見てみましょう。上記の図の特定のコードは次のとおりです。
1.まずバッファーにスペースをバッテに割り当てます
bytebuffer bytebuffer = bytebuffer.allocate(1024);
ByteBufferオブジェクトを作成し、メモリサイズを指定します
2。バッファーにデータを書き込む:
1)。チャネルからバッファーへのデータ:channel.read(bytebuffer); 2)。クライアントからバッファーへのデータ:bytebuffer.put(...);
3.バッファからデータを読む:
1)。バッファからチャネルへのデータ:Channel.Write(bytebuffer); 2)。バッファからサーバーへのデータ:bytebuffer.get(...);
セレクタ
セレクターはNIOのコアであり、チャンネルの管理者です
select()ブロッキング方法を実行することにより、チャネルの準備ができているかどうかを聞いてください
データが読み取られたら、この方法の返品値はselectionkeysの数です
したがって、サーバーは通常、チャンネルの準備が整うまでselect()メソッドデッドループを実行します。
各チャネルは、イベントをセレクターにバインドし、SelectionKeyオブジェクトを生成します。
注意すべきは、次のとおりです。
チャネルとセレクターがバインドされている場合、チャネルは非ブロッキングモードでなければなりません。
FileChannelはソケットチャネルではないため、非ブロッキングモードに切り替えることができないため、FileChannelはセレクターでイベントをバインドできません
NIOには4種類のイベントがあります。
1.SelectionKey.op_connect:接続イベント
2.SelectionKey.op_accept:イベントを受信します
3.SelectionKey.op_read:イベントを読む
4.SelectionKey.op_write:イベントを書き込みます
チャネル
合計で4つのチャネルがあります。
FileChannel:IOファイルストリームに作用します
DatagramChannel:UDPプロトコルで動作します
Socketchannel:TCPプロトコルで動作します
Serversocketchannel:TCPプロトコルに作用します
この記事では、一般的に使用されるTCPプロトコルを介してNIOを説明します
Serversocketchannelを例として取りましょう。
Serversocketchannelチャネルを開きます
serversocketchannel serversocketchannel = serversocketchannel.open();
Serversocketchannelチャネルを閉じます:
serversocketchannel.close();
ループソッケッチャンネル:
while(true){socketchannel socketchannel = serversocketchannel.accept(); clientchannel.configureblocking(false);} clientChannel.configureBlocking(false);ステートメントは、このチャネルを非ブロッキングに設定することです。つまり、ブロッキングまたは非ブロッキングの非同期自由制御はNIOの特性の1つです
SelectionKey
SelectionKeyは、チャネルとセレクター間の相互作用のコアコンポーネントです
たとえば、Socketchannelでセレクターをバインドし、接続イベントとして登録します。
socketchannel clientchannel = socketchannel.open(); clientchannel.configureblocking(false); clientchannel.connect(new inetsocketAddress(port)); clientchannel.register(selector、selectionkey.op_connect);
コアはregister()メソッドにあり、selectionKeyオブジェクトを返します
チャネルイベントを検出することは、次の方法を使用できるイベントの種類です。
selectionKey.Isaceptable(); selectionKey.isconnectable(); selectionKey.isreadable(); selectionKey.isWritable();
サーバーは、これらのメソッドを介してポーリングに対応する操作を実行します
もちろん、チャネルとセレクターにバインドされたキーも順番に取得できます。
Channel Channel = SelectionKey.Channel(); selector selector = selectionKey.selector();
チャネルでイベントを登録する場合、バッファーをバインドすることもできます。
clientchannel.register(key.selector()、selectionkey.op_read、bytebuffer.allocatedirect(1024));
またはオブジェクトをバインドします:
selectionkey.attach(object); object anthorobj = selectionkey.attachment();
NIOのTCPサーバー
多くのことを話した後、私たちは最もシンプルで最もコアコードを見ていきます(それほど多くのコメントを追加するのはエレガントではありませんが、誰もが理解するのに便利です):
パッケージcn.blog.test.niotest;インポートjava.io.ioexception; Import java.net.inetsocketAddress; Import java.nio.bytebuffer; Import java.nio.channels。 //セレクタープライベート最終static int port = 8686を作成します。プライベート最終static int buf_size = 10240; private void initserver()throws ioException {//チャネルマネージャーオブジェクトセレクターを作成this.selector = selector.open(); //チャンネルオブジェクトを作成しますServersocketchannelチャンネル= Serversocketchannel.open(); Channel.ConfigureBlocking(false); //チャンネルを非ブロッキングchannel.socket()。bind(new inetsocketAddress(port))に設定します。 //ポート8686でチャネルをバインドする//上記のチャネルマネージャーとチャネルにバインドし、チャンネルのop_acceptイベントを登録します//イベントを登録した後、selector.select()は返されます(キー)。イベントがselector.select()に到達しない場合、selectionkey selectionkey = channel.register(selector、selectionkey.op_accept)をブロックします。 while(true){// poll selector.select(); //これはブロッキングメソッドであり、データが読み取られるまで待ちます。また、返品値はキーの数(複数の存在することができます)set keys = selector.selectedKeys(); //チャンネルにデータがある場合、キーコレクションに生成されたキーにアクセスしますiterator iterator = keys.iterator(); //このキーコレクションのiteratorを取得しますwhile(iterator.hasnext()){// iteratorを使用してコレクションSelectionKey key =(selectionkey)iterator.next(); //コレクションITERATOR.REMOVE()でキーインスタンスを取得します。 //現在のキーインスタンスを取得した後、iteratorでこの要素を削除することを忘れないでください。これは非常に重要です。そうしないと、(key.isaceptable()){//現在のキーで表されるチャネルが許容状態にあるかどうか、もしそうなら、doaccept(key); } else if(key.isreadable()){doread(key); } else if(key.iswritable()&& key.isvalid()){dowrite(key); } else if(key.isconnectable()){system.out.println( "connectable!"); }}}} public void doaccept(selectionkey key)throws ioexception {serversocketchannel serverchannel =(serversocketchannel)key.channel(); System.out.println( "Serversocketchannelはループで聴いています"); socketchannel clientchannel = serverChannel.accept(); clientchannel.configureblocking(false); clientchannel.register(key.selector()、selectionkey.op_read); } public void doread(selectionkey key)throws ioexception {socketchannel clientchannel =(socketchannel)key.channel(); bytebuffer bytebuffer = bytebuffer.allocate(buf_size); long bytesread = clientchannel.read(bytebuffer); while(bytesread> 0){bytebuffer.flip(); byte [] data = bytebuffer.array();文字列info = new String(data).trim(); system.out.println( "クライアントから送信されたメッセージは次のとおりです。"+info); bytebuffer.clear(); bytesread = clientchannel.read(bytebuffer); } if(bytesread == -1){clientchannel.close(); }} public void dowrite(selectionkey key)throws ioexception {bytebuffer bytebuffer = bytebuffer.allocate(buf_size); bytebuffer.flip(); socketchannel clientchannel =(socketchannel)key.channel(); while(bytebuffer.hasremaining()){clientchannel.write(bytebuffer); } bytebuffer.compact(); } public static void main(string [] args)throws ioexception {mynioserver mynioserver = new mynioserver(); mynioServer.initserver(); }}モニターチャネルを印刷して、Serversocketchannelが実行を開始したときにお知らせしました
NIOクライアントのデバッグと協力すると、はっきりと見つけることができます。 select()ポールを入力する前
Acceptイベントの鍵はすでにありますが、select()はデフォルトで呼び出されません
代わりに、AccectのselectionKeyを呼び出す前に、他の興味深いイベントがSelect()によってキャプチャされるまで待つ必要があります
その場合にのみ、Serversocketchannelは円形監視の実行を開始しました
言い換えれば、セレクターでは、Serversocketchannelは常に維持されます。
およびserverChannel.accept();本当に非同期です(channel.configureblocking(false); initserverメソッド)
接続が受け入れられない場合、nullが返されます
Socketchannelが正常に接続されている場合、このSocketchannelは書き込み(読み取り)イベントを登録します
非同期を設定します
NIOのTCPクライアント
サーバーがある場合はクライアントがいる必要があります
実際、サーバーを完全に理解できる場合
クライアントのコードは似ています
パッケージcn.blog.test.niotest;インポートjava.io.ioexception; Import java.net.inetsocketAddress; Import java.nio.bytebuffer; Import java.nio.channels.selectionKey; Import java.nio.channels.selector; import java.nio.nio.channel.seckerchannel. sokecherchanknel. mynioclient {private selector selector; //セレクタープライベート最終static int port = 8686を作成します。プライベート最終static int buf_size = 10240; private staticbuffer bytebuffer = bytebuffer.allocate(buf_size); private void initclient()throws ioException {this.selector = selector.open(); socketchannel clientchannel = socketchannel.open(); clientchannel.configureblocking(false); clientchannel.connect(new inetsocketAddress(ポート)); clientchannel.register(selector、selectionkey.op_connect); while(true){selector.select(); iterator <selectionKey> iterator = selector.selectedKeys()。iterator(); while(iterator.hasnext()){selectionkey key = iterator.next(); iterator.remove(); if(key.isconnectable()){doconnect(key); } else if(key.isreadable()){doread(key); }}} public void doconnect(selectionkey key)throws ioexception {socketchannel clientchannel =(socketchannel)key.channel(); if(clientchannel.isconnectionpending()){clientchannel.finishconnect(); } clientchannel.configureblocking(false);文字列info = "hello server !!!"; bytebuffer.clear(); bytebuffer.put(info.getBytes( "utf-8")); bytebuffer.flip(); clientchannel.write(bytebuffer); //clientchannel.register(key.selector()、selectionkey.op_read); clientchannel.close(); } public void doread(selectionkey key)throws ioexception {socketchannel clientchannel =(socketchannel)key.channel(); clientchannel.read(bytebuffer); byte [] data = bytebuffer.array();文字列msg = new String(data).trim(); system.out.println( "サーバーはメッセージを送信します:"+msg); clientchannel.close(); key.selector()。close(); } public static void main(string [] args)throws ioexception {mynioclient mynioclient = new mynioclient(); mynioclient.initclient(); }}出力結果
ここでは、サーバーと2つのクライアントを開きます。
次に、同時に1000のクライアントを開くことができます。 CPUが十分に強力である限り、サーバーは閉塞のためにパフォーマンスを低下させることができません。
上記は、Java Nioの詳細な説明です。ご質問がある場合は、以下のメッセージ領域で説明できます。