1。概要
BIO APIでは、入力と出力は、InputStreamとoutPutStreamの2つのストリームを介して実行されます。NIOは、それらの代わりに双方向通信チャネルを使用します。チャネル(チャネル)は、通信を実現するためにバッファー(バッファー)に依存する必要があります。
パイプライン比較ストリームには、ノンブロッキング、オフヒープメモリマッピング、ゼロコピーなどの機能があります。
2.バッファ定義
パイプラインはバッファに依存するため、最初にバッファの概念と使用法を紹介しましょう。
データを格納するために、バッファ内に配列が保持されます。バッファは、格納されているデータタイプをサポートせず、一部の基本的なデータタイプのみを格納できます。
読み取り、書き込み、空にする機能があります。スレッドの安全性がなければ、スレッドの安全性を自分で制御する必要があります。
無料のJavaアーキテクチャスキルマップを受け取るメリットをすべての人に与えましょう。無料であることに注意してください。
、
受け取るために必要な+ Vを自由に受け取る
3.バッファの内部構造
ByteBufferを例にとると、これはBufferのサブクラスであり、Byteタイプのデータを格納するために使用されます。他のバッファーも同様ですが、格納されるデータタイプは異なります。
バイト配列は、データを格納するために内部的に維持されます。
親クラスBufferには、次の4つの重要な属性があります。
-
容量
配列のサイズを示します
-
制限
バッファの読み取り可能および書き込み可能な範囲を制限します。デフォルトは容量と同じです。
-
ポジション
現在の読み取りおよび書き込み位置。デフォルトは0で、ビットが読み取られるか書き込まれるたびに+1になります。
-
マーク
リセットのマークを付けて、位置をこの位置に設定します。デフォルトは-1です
4つの位置関係:マーク<=位置<=制限<=容量
4.一般的に使用される操作をバッファリングします
4.1割り当て
バッファのストレージスペースを割り当て、すべての属性を初期化します。例えば:
得られた結果は次のとおりです。
4.2ラップ
配列に基づいてバッファをパックします。位置は0、制限は容量値です。
テストコードは次のとおりです。
バッファの結果は次のとおりです。
4.3プット
バッファに書き込みます。テストコードは次のとおりです。
パッティングの過程で、位置が変更されます。つまり、数値が書き込まれるたびに、位置が1ずつ増加します。7回目の書き込みでは、位置が制限を超え、この時点で例外BufferOverflowExeceptionがスローされます。
4.4フリップ
読み取りの準備をします。入れたら、フリップを実行して位置をリセットし、0に設定します。
後続の読み取り操作は、その位置から開始されます。
テストコードは次のとおりです。
put5を実行した後のバッファの結果は次のとおりです。
次にフリップを実行すると、結果は次のようになります。
結果によると、フリップの役割を確認し、位置を0に設定します。
そして、制限は元の位置に設定され、この時点での制限は読み取りの範囲を制限します。
4.5ゲット
バッファの内容を読み取ります。バッファを読み取るたびに、その位置の後に1つの位置を移動します。
テストコードは次のとおりです。
5番目を読み取ると、このときのバッファの内容は次のようになります。
このときの位置が限界に達していることがわかります。もう一度読むと、エラーが報告され、範囲外の例外BufferUnderflowExceptionが読み取られます
4.6マーク
マークビットを設定し、現在位置を記録します
テストコードは次のとおりです。
マークを実行した後、markプロパティをpositionに設定します。つまり、マークを作成します。
4.7リセット
リセットの機能は、位置の値をマークに設定することです。これは、バッファー内のデータを変更するために使用されます。または、バッファ内のデータを繰り返し読み取ります。
テストコードは次のとおりです。
リセット実行後のバッファ結果は以下のようになります。
マークマークを記録した後、2つの数字を読み取り、2つの数字を修正しました。リセット後、位置はマークで記録された位置に戻ります。このとき、再度書き込むと、バッファ内の2つの数値を変更できます。結果は次のとおりです。
4.8クリア
バッファの属性をリセットしますが、バッファ内のデータはクリアされません
位置の位置を0に設定し、マークを-1に設定します。制限は容量に設定されます
テストコードは次のとおりです。
clearを実行した後、バッファーの結果は次のようになります。
4.9巻き戻し
再読の準備をします。位置を0に設定し、制限を変更しません。マークが-1に設定されている
テストコードは次のとおりです。
巻き戻しを実行した後、バッファの結果は次のようになります。
4.10残り
返される読み取り可能な範囲の数
これは限界位置です
テストコードは次のとおりです
。この時点で、制限は5です。2回読み取った後、残りのメソッドが実行され、返される結果は3で、残りの読み取り可能なコンテンツを示します。
5.シャネル
パイプは、ファイル、ネットワークソケットなどを接続するために使用されます。2つのI / O操作を同時に実行できます。双方向パイプラインと呼ばれます。接続と閉鎖の2つの状態があります。パイプラインの作成時に開いています。閉じられると、I / O操作が呼び出されたときにレポートします。ClosedChannelException
。パイプラインのisOpen
方法により、開状態かどうかを判断することができます。
5.1FileChannelファイルパイプライン
実名は、ファイルの操作に使用されることを示しています。通常の操作に加えて、次の機能もサポートしています。
-
ファイルの指定された領域の読み取りと書き込みのサポート
-
オフヒープメモリマッピング。大きなファイルの読み取りと書き込みを行う場合、JVMで宣言されたメモリから直接マッピングして、読み取りと書き込みの効率を向上させることができます。
-
ゼロコピーテクノロジーは、 データをチャネルに送信する
transferFrom
かtransferTo
、チャネルに直接送信することで、パフォーマンスを大幅に向上させます。 -
他のプログラマーがアクセスできないように、ファイルの指定された領域をロックします
Open FileChannelは、現在、inputStream.getChannel()やoutputStream.getChannel()などのストリームを介してのみ開くことができ、入力ストリームを介して開かれたパイプラインにはアクセスのみが可能であり、outputStreamは書き込み用にのみ開くことができます。それ以外の場合、NonWritableChannelExceptionとNonReadableChannelExceptionは別々にスローされます。
パイプで読み取りと書き込みの両方をサポートする場合は、RandomAccessFile
読み取りと書き込みモードを使用する必要があります。
次のテストコードは、ファイルパイプラインの基本的な使用法です。
//1. 打开文件管道
FileChannel channel = new RandomAccessFile(file_name,"rw").getChannel();
ByteBuffer buffer=ByteBuffer.allocate(1024); // 声明1024个空间
// 从文件中 读取数据并写入管道 再写入缓冲 channel.read(buffer); buffer.flip();//上面学的,写完之后,需要将position归0,为了后面的读取做准备 byte[] bytes= new byte[buffer.remaining()]; int i =0; while (buffer.hasRemaining()){ bytes[i++]= buffer.get(); } System.out.println(new String(bytes)); // 把缓冲区数据写入到管道 channel.write(ByteBuffer.wrap("森林大帅哥".getBytes())); channel.close();
5.2 DatagramChannelUDPソケットパイプ
UDPは接続のないプロトコルであり、DatagramChannelは、このプロトコルがクライアントからメッセージを受信するためのサービスを提供します。
DatagramChannelの基本的な使用法は次のとおりです。
public void test1() throws IOException { DatagramChannel channel=DatagramChannel.open(); // 绑定端口 channel.bind(new InetSocketAddress(8080)); ByteBuffer buffer=ByteBuffer.allocate(8192); while (true){ buffer.clear(); // 清空还原 channel.receive(buffer); // 阻塞 buffer.flip(); byte[] bytes=new byte[buffer.remaining()]; buffer.get(bytes); System.out.println(new String(bytes)); } }
コマンドnc-uv127.0.0.1 8080を使用して、指定されたipポート番号にudpを送信します
アイデアコンソールは次のように出力されます。
5.3TCPソケットパイプ
TCPは接続プロトコルであり、通信は接続が確立された後にのみ実行できます。これには、次の2つのパイプラインが必要です。
-
** ServerSocketChannel:**クライアントとの接続を確立するために使用されます
-
** SocketChannel:**クライアントとのメッセージの読み取りと書き込みに使用されます
テストコードは次のとおりです。
@Test
public void test1() throws IOException { // 用于与客户端建立连接 ServerSocketChannel channel = ServerSocketChannel.open(); channel.bind(new InetSocketAddress(8080)); while (true) {//循环接收请求,分配子线程去执行请求 // 用于和客户端进行消息读写 SocketChannel socketChannel = channel.accept(); handle(socketChannel); } } public void handle(final SocketChannel socketChannel) throws IOException { // 2.通信 Thread thread = new Thread(new Runnable() { @Override public void run() { ByteBuffer buffer = ByteBuffer.allocate(8192); while (true) { try { buffer.clear(); socketChannel.read(buffer); // 从buffer 当中读出来 buffer.flip(); byte[] bytes = new byte[buffer.remaining()]; buffer.get(bytes); String message = new String(bytes); System.out.println(message); // 写回去 buffer.rewind(); socketChannel.write(buffer); if (message.trim().equals("exit")) { break; } } catch (Exception e) { e.printStackTrace(); } } try { socketChannel.close(); } catch (IOException e) { e.printStackTrace(); } } }); thread.start(); }
コマンドTCPサービスtelnet127.0.0.18080でテストできます