ホームページ > Java > &#&チュートリアル > Java NIO を完全にマスターできるようにします (概要の共有)

Java NIO を完全にマスターできるようにします (概要の共有)

WBOY
リリース: 2022-03-31 11:54:05
転載
1854 人が閲覧しました

この記事では、java に関する関連知識を提供します。主に、NIO コア、BIO と NIO の比較、NIO ターミナル通信を介した単純なサーバー クライアントの実装など、NIO 関連の問題を紹介します。みんなの役に立つでしょう。

Java NIO を完全にマスターできるようにします (概要の共有)

推奨学習: 「

java チュートリアル

1. Java マインド マップ

Java NIO を完全にマスターできるようにします (概要の共有)##2. I/O モデル

I/O モデルの本質は、どのようなチャネルを使用してデータを送受信するかであり、プログラム通信のパフォーマンスを大きく決定します。

Java は 3 つのネットワーク プログラミング モデルをサポートします: BIO、NIO、AIO


    BIO: 同期とブロッキング、サービス実装モードは 1 つの接続と 1 つのスレッドです。つまり、クライアントは1 つの接続 リクエストを行うとき、サーバーは処理のためにスレッドを開始する必要があります。
  • NIO: 同期かつノンブロッキングのサーバー実装モードは、複数のリクエスト接続を処理するスレッドです。つまり、クライアントから送信されたリクエストはマルチプレクサに登録され、多重化されます。サーバーは、I/O リクエストの接続をポーリングし、それらを処理します。
  • AIO: 非同期ノンブロッキング、AIO は非同期チャネルの概念を導入し、プロアクター モードを採用し、プログラムの作成を簡素化し、有効なリクエストの後にのみスレッドを開始します。その特徴は、オペレーティング システムがまず完了後にサーバーに通知します。
  • 3. BIO、NIO、AIO アプリケーション シナリオ

    BIO 方式は、接続数が比較的少なく固定されたアーキテクチャに適しています。この方法は、サーバーのリソース要件が比較的高く、同時実行がアプリケーションに制限されている場合に適しており、JDK1.4 以前はこれしか選択肢がありませんでしたが、プログラムがシンプルで理解しやすいです。
  • NIO メソッドは、チャット サーバー、弾幕システム、サーバー間通信など、多数の接続と比較的短い接続 (軽い操作) を行うアーキテクチャに適しています。プログラミングはより複雑になり、JDK1.4 でサポートされ始めます。
  • AIO メソッドは、フォト アルバム サーバーなど、多数の接続と比較的長い接続 (重い操作) を伴うアーキテクチャで使用され、同時操作に参加するために OS を完全に呼び出します。プログラミングは比較的複雑で、JDK7 でサポートされ始めています
  • 4. BIO プログラミングの簡単なプロセス

    サーバーは ServerSocket を開始します。
  • クライアントはソケットを開始します。サーバーと通信するには、デフォルトでサーバーは各クライアントが通信するためのスレッドを確立する必要があります。
  • クライアントはリクエストを送信した後、まずスレッド応答があるかどうかをサーバーに問い合わせます、ない場合は待機するか拒否されます;
  • 応答がある場合、クライアントはスレッドは実行を続行する前にリクエストが終了するのを待ちます;
  • 5 、NIO コア

NIO には 3 つのコア部分があります: セレクター (セレクター)、チャンネル (チャネル)、そしてバッファ(バッファ)。

NIO はバッファ指向、つまりブロック指向のプログラミングです。データはバッファに読み込まれ、後で処理されます。必要に応じてバッファ内でデータを前後に移動できるため、処理プロセスの柔軟性が向上します。 Use It は、ノンブロッキングで拡張性の高いネットワークを提供します。

HTTP2.0 は多重化テクノロジーを使用して、同じ接続で複数のリクエストを同時に処理できるようにしており、同時リクエストの数は HTTP1.1 よりも数桁多くなります。
つまり、NIO は 1 つのスレッドで複数のリクエストを処理できます。

6. BIO と NIO の比較

    BIO はストリーム内のデータを処理しますが、NIO はブロック内のデータを処理します。ブロック I/O はストリーム I より効率的です。 /O. /O ははるかに高い;
  • BIO はブロッキング、NIO はノンブロッキング;
  • BIO はバイト ストリームに基づいて動作し、文字ストリーム、NIO はチャネルとバッファに基づいて動作します。データは常にチャネルからバッファに読み取られるか、バッファからチャネルに書き込まれます。セレクターは、複数のチャネル イベント (接続要求、データ到着など) を監視するために使用されるため、単一のスレッドで複数のクライアント チャネルを監視できます。
  • #7. NIO の 3 つの中心原則の概略図

フローチャートの説明: Java NIO を完全にマスターできるようにします (概要の共有)

    セレクター対応 1 つのスレッド、1 つのスレッドが複数のチャネル (接続) に対応します;
  • この図は、セレクター //プログラムに登録されているチャネルが 3 つあることを示しています;
  • 各チャネルはバッファに対応します;
  • プログラムがどのチャネルに切り替わるかはイベントによって決定され、イベントは重要な概念です;
  • セレクターはさまざまなイベントに応じて各チャンネルをオンにします;
  • バッファはメモリ ブロックであり、下部には配列があります;
  • データはバッファを通じて読み書きされます。これは BIO と同じです。BIO は入力ストリームまたは出力ストリームのいずれかであり、双方向にすることはできませんが、NIO のバッファは読み書き可能であり、反転が必要です。切り替える方法;
  • チャネルは双方向であり、Linux などの基礎となるオペレーティング システムのステータスを返すことができます。基礎となるオペレーティング システム チャネルは双方向です。

8. バッファ(バッファ)

バッファは本質的に、データの読み書きができるメモリ ブロックです。コンテナ オブジェクト (配列を含む) として理解できます。このオブジェクトは、使いやすくするためのメソッドのセットを提供します。バッファ オブジェクトには、バッファのステータス変化を追跡および記録できるいくつかの組み込みメカニズムがあります。 Channel はファイルやネットワークからデータを読み取るためのチャネルを提供しますが、読み書きされるデータは Buffer を経由する必要があります。
NIO では、Buffer は最上位の親クラスであり、抽象クラスです。

1. 一般的に使用されるバッファ サブクラスのリスト

  • ByteBuffer、バイト データをバッファに格納します;

  • ShortBuffer、文字列データをバッファに保存します;

  • CharBuffer、文字データをバッファに保存します;

  • IntBuffer、整数データを保存しますバッファに保存;

  • LongBuffer、長整数データをバッファに保存;

  • DoubleBuffer、小数をバッファに保存;

  • FloatBuffer、バッファに小数を格納します;

##2. バッファの 4 つの主要な属性

    #mark: マーク
  • position: 位置、読み書きされる次の要素のインデックス。値は、バッファ データが読み書きされるたびに変更されます。次へ 読み取りと書き込みの準備。
  • limit: バッファの現在の終了点を示します。制限を超えるバッファでは読み取りおよび書き込み操作を実行できません。また、制限は変更できます。
  • capacity: 容量、つまり、収容できるデータの最大量は、バッファの作成時に設定され、変更できません。

Java NIO を完全にマスターできるようにします (概要の共有)#3. 共通バッファ API

## JDK1.4 のときに導入されました

##public Final int Capacity( )//このバッファの容量を返します

public Final int Position( )//このバッファの位置を返します
  • publicfinal Bufferposition( int newPositio )//このバッファの位置を設定します
  • publicfinal int limit()//このバッファの制限を返します
  • publicfinalBufferlimit(intnewLimit)//このバッファの領域制限を設定します
  • publicfinalBuffermark()//このバッファの位置にマークを設定します
  • publicfinalBufferreset()//このバッファの位置を前のマークにリセットします Position
  • public Final Buffer clear( )//このバッファをクリアします。つまり、各マークを初期状態に戻しますが、データは実際には消去されず、その後の操作で上書きされます
  • public Final Buffer lip ( )//このバッファを反転
  • public Final Buffer rewind( )//このバッファを巻き戻す
  • public Final int Rewind( )//現在位置と制限値の間を返す 要素数
  • public Final boolean hasRemaining()//現在位置と制限値の間に要素があるかどうかを通知します
  • public abstract boolean isReadOnly();//このバッファが読み取りバッファのみであるかどうかを通知します
  • JDK1.6 で導入された API
  • #public abstract boolean hasArray();//このバッファにアクセス可能な基になる実装配列があるかどうかを通知します

public abstract Object array();//このバッファの基礎となる実装配列を返します

    public abstract int arrayOffset();//このバッファの基礎となる実装配列の最初のバッファを返します 領域要素のオフセット
  • public abstract boolean isDirect();//このバッファがダイレクト バッファかどうかを通知します
  • 9. チャネル

Java NIO を完全にマスターできるようにします (概要の共有)1 . 基本的な紹介

(1) NIO チャネルはストリームに似ています

チャネルは同時に読み取りと書き込みが可能で、ストリームは読み取りまたは書き込みのみ可能です。

#チャネルはデータを非同期に読み書きできます

チャネルはバッファからデータを読み取り、バッファにデータを書き込むことができます
  • #(2) BIO のストリームたとえば、FileInputStream オブジェクトはデータの読み取りのみが可能ですが、NIO のチャネル (Channel) は双方向であり、読み取りまたは書き込み操作が可能です。
  • (3) チャネルは NIO のインターフェイスです
  • (4) 一般的に使用されるチャネル クラスには、FileChannel、DatagramChannel、ServerSocketChannel、および SocketChannel が含まれます。 ServerSocketChanne は ServerSocket に似ており、SocketChannel は Socket に似ています。
  • (5) FileChannel はファイルデータの読み書きに使用され、DatagramChannel は UDP データの読み書きに使用され、ServerSocketChannel と SocketChannel は TCP データの読み書きに使用されます。
2. FileChannel



FileChannel は主にローカル ファイルで IO 操作を実行するために使用されます。一般的なメソッドは次のとおりです:

read 、チャネルからデータを読み取り、バッファに入れます

write、バッファ内のデータをチャネルに書き込みます
  • transferFrom、からターゲット チャネルから現在のチャネルにデータをコピーします
  • transferTo、現在のチャネルからターゲット チャネルにデータをコピーします

3. バッファとチャネルに関する注意と詳細

  • ByteBuffer は、型付きの put と get、put into データ型をサポートします。 get は、対応するデータ型を使用して取得する必要があります。そうでない場合は、BufferUnderflowException 例外が発生する可能性があります。

  • 通常のバッファを読み取り専用バッファに変換できます。

  • NIO は、ファイルをメモリ (ヒープ外のメモリ) 内で直接変更できるようにする MappedByteBuffer も提供しており、ファイルの同期方法は NIO によって完了します。

  • NIO は、複数のバッファ (バッファ配列) を介した読み取りおよび書き込み操作、つまりスキャッタリングとギャザリングもサポートします。

10. セレクター(selector)

1. 基本的な紹介

  • Java の NIO を使用します。ノンブロッキングIOモード。 1 つのスレッドを使用して複数のクライアント接続を処理でき、セレクターを使用します。

  • Selector は登録されている複数のチャネルでイベントが発生したかどうかを検出し、イベントが発生した場合はイベントを取得し、それぞれのイベントに応じて処理します。このように、複数のチャネルの管理、つまり複数の接続とリクエストの管理に 1 つのスレッドのみを使用できます。

  • 読み取りと書き込みは、接続/チャネル上に実際の読み取りおよび書き込みイベントがある場合にのみ発生するため、システムのオーバーヘッドが大幅に削減され、接続ごとにスレッドを作成する必要がなくなります。複数のスレッドを維持する必要があります。

  • 複数のスレッド間のコンテキストの切り替えによって発生するオーバーヘッドを回避します。

#2. セレクター関連のメソッド

  • ##open();//セレクター オブジェクトを取得します

  • select(long timeout);//登録されているすべてのチャネルを監視します。IO 操作が実行できる場合、対応する SelectionKey を内部コレクションに追加して返します。パラメータはタイムアウトの設定に使用されます。

  • selectedKeys();//内部コレクションからすべてのSelectionKeyを取得します。

3. 注意事項

NIO の ServerSocketChannel 関数は ServerSocket に似ており、SocketChannel 関数は Socket に似ています。

11. NIO を介した単純なサーバーとクライアントの通信

1. サーバー
package com.nezha.guor.nio;import java.io.IOException;import java.net.InetSocketAddress;import java.nio.ByteBuffer;import java.nio.channels.*;import java.util.Iterator;public class NioServer {
    private Selector selector;
    private ServerSocketChannel serverSocketChannel;
    private static final int PORT = 8080;

    public NioServer() {
        try {
            //获得选择器
            selector = Selector.open();
            serverSocketChannel =  ServerSocketChannel.open();
            //绑定端口
            serverSocketChannel.socket().bind(new InetSocketAddress(PORT));
            //设置非阻塞模式
            serverSocketChannel.configureBlocking(false);
            //将该ServerSocketChannel 注册到selector
            serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
        }catch (IOException e) {
            System.out.println("NioServer error:"+e.getMessage());
        }
    }

    public void listen() {

        System.out.println("监听线程启动: " + Thread.currentThread().getName());
        try {
            while (true) {
                int count = selector.select();
                if(count > 0) {
                    //遍历得到selectionKey集合
                    Iterator<selectionkey> iterator = selector.selectedKeys().iterator();
                    while (iterator.hasNext()) {
                        SelectionKey key = iterator.next();

                        if(key.isAcceptable()) {
                            SocketChannel sc = serverSocketChannel.accept();
                            sc.configureBlocking(false);
                            sc.register(selector, SelectionKey.OP_READ);
                            System.out.println(sc.getRemoteAddress() + " 上线 ");
                        }
                        //通道发送read事件,即通道是可读的状态
                        if(key.isReadable()) {
                            getDataFromChannel(key);
                        }
                        //当前的key 删除,防止重复处理
                        iterator.remove();
                    }
                } else {
                    System.out.println("等待中");
                }
            }
        }catch (Exception e) {
            System.out.println("listen error:"+e.getMessage());
        }
    }

    private void getDataFromChannel(SelectionKey key) {
        SocketChannel channel = null;
        try {
            channel = (SocketChannel) key.channel();
            ByteBuffer buffer = ByteBuffer.allocate(1024);

            int count = channel.read(buffer);
            //根据count的值做处理
            if(count > 0) {
                String msg = new String(buffer.array());
                System.out.println("来自客户端: " + msg);

                //向其它的客户端转发消息(排除自己)
                sendInfoToOtherClients(msg, channel);
            }
        }catch (IOException e) {
            try {
                System.out.println(channel.getRemoteAddress() + " 离线了");
                //取消注册
                key.cancel();
            }catch (IOException ex) {
                System.out.println("getDataFromChannel error:"+ex.getMessage());
            }
        }finally {
            try {
                channel.close();
            }catch (IOException ex) {
                System.out.println("channel.close() error:"+ex.getMessage());
            }
        }
    }

    //转发消息给其它客户(通道)
    private void sendInfoToOtherClients(String msg, SocketChannel self ) throws  IOException{
        System.out.println("服务器转发消息中...");
        System.out.println("服务器转发数据给客户端线程: " + Thread.currentThread().getName());
        //遍历 所有注册到selector 上的 SocketChannel,并排除 self
        for(SelectionKey key: selector.keys()) {
            Channel targetChannel = key.channel();

            //排除自己
            if(targetChannel instanceof  SocketChannel && targetChannel != self) {
                SocketChannel dest = (SocketChannel)targetChannel;
                //将信息存储到buffer
                ByteBuffer buffer = ByteBuffer.wrap(msg.getBytes());
                //将buffer数据写入通道
                dest.write(buffer);
            }
        }
    }

    public static void main(String[] args) {
        //创建服务器对象
        NioServer nioServer = new NioServer();
        nioServer.listen();
    }}</selectionkey>
ログイン後にコピー

2. クライアント
package com.nezha.guor.nio;import java.io.IOException;import java.net.InetSocketAddress;import java.nio.ByteBuffer;import java.nio.channels.SelectionKey;import java.nio.channels.Selector;import java.nio.channels.SocketChannel;import java.util.Iterator;import java.util.Scanner;public class NioClient {
    private final int PORT = 8080; //服务器端口
    private Selector selector;
    private SocketChannel socketChannel;
    private String username;

    public NioClient() throws IOException {
        selector = Selector.open();
        socketChannel = socketChannel.open(new InetSocketAddress("127.0.0.1", PORT));
        //设置非阻塞
        socketChannel.configureBlocking(false);
        //将channel注册到selector
        socketChannel.register(selector, SelectionKey.OP_READ);
        username = socketChannel.getLocalAddress().toString().substring(1);
        System.out.println(username + " is ok...");
    }

    //向服务器发送消息
    public void sendInfo(String info) {
        info = username + " 说:" + info;
        try {
            socketChannel.write(ByteBuffer.wrap(info.getBytes()));
        }catch (IOException e) {
            System.out.println("sendInfo error:"+e.getMessage());
        }
    }

    //读取从服务器端回复的消息
    public void readInfo() {
        try {
            int readChannels = selector.select();
            if(readChannels > 0) {
                Iterator<selectionkey> iterator = selector.selectedKeys().iterator();
                while (iterator.hasNext()) {
                    SelectionKey key = iterator.next();
                    if(key.isReadable()) {
                        //得到相关的通道
                        SocketChannel sc = (SocketChannel) key.channel();
                        //得到一个Buffer
                        ByteBuffer buffer = ByteBuffer.allocate(1024);
                        //读取
                        sc.read(buffer);
                        //把读到的缓冲区的数据转成字符串
                        String msg = new String(buffer.array());
                        System.out.println(msg.trim());
                    }
                }
                iterator.remove(); //删除当前的selectionKey, 防止重复操作
            } else {
                System.out.println("没有可以用的通道...");
            }
        }catch (Exception e) {
            System.out.println("readInfo error:"+e.getMessage());
        }
    }

    public static void main(String[] args) throws Exception {
        NioClient nioClient = new NioClient();
        new Thread() {
            public void run() {
                while (true) {
                    nioClient.readInfo();
                    try {
                        Thread.currentThread().sleep(2000);
                    }catch (InterruptedException e) {
                        System.out.println("sleep error:"+e.getMessage());
                    }
                }
            }
        }.start();

        //发送数据给服务器端
        Scanner scanner = new Scanner(System.in);

        while (scanner.hasNextLine()) {
            nioClient.sendInfo(scanner.nextLine());
        }
    }}</selectionkey>
ログイン後にコピー

3. コンソール出力

Java NIO を完全にマスターできるようにします (概要の共有)
Java NIO を完全にマスターできるようにします (概要の共有)## 推奨学習: 「

java チュートリアル

>>

以上がJava NIO を完全にマスターできるようにします (概要の共有)の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

関連ラベル:
ソース:csdn.net
このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。
人気のチュートリアル
詳細>
最新のダウンロード
詳細>
ウェブエフェクト
公式サイト
サイト素材
フロントエンドテンプレート