NIO-Buffer、Channel、Selector
21868 ワード
Buffer
Bufferは本質的にメモリの1つで、データを書き込んだり取得したりすることができます.
java.NioはCharBufferShortBufferIntBufferLongBufferFloatBufferDoubleBufferByteBuffer->MappedByteBufferの実装を定義し、コアはByteBufferである.対応する基本タイプとして理解できる配列.
Bufferの重要な属性-position、limit、capacity
flip()メソッド-bufferを書き込みモードから読み取りモードに切り替えるためにのみ使用され、読み取り時に呼び出さなければデータが読めません.
なお、通常、NIOの読み出し動作については、チャネルからBufferにデータを読み出し、Bufferへの書き込み動作に対応している.
public final Buffer flip() {
limit = position; // limit
position = 0; // position 0
mark = -1;
return this;
}
Bufferの初期化
allocate(int capacity)
は、1つのBufferをインスタンス化することができ、さらにwrapメソッドは、byte[]パラメータを受信するBufferを初期化することもできる.Bufferの充填
putメソッドを使用してBufferを埋めます.byte、int(indexの位置を指定)およびbyte、byte[]のパラメータを受信することができ、Bufferサイズがcapacityを超えないように制御する必要がある.
あるいはchannelからのデータをBufferに埋め込み、前述したようにシステムレベルではNIOと呼ばれる読み取り動作を行う.
int num = channel.read(buffer);
チャンネルからBufferに読み込まれたデータサイズを返します.
フェッチBuffer
まずモードを切り替え,flip()メソッド,すなわちpositionとlimitを切り替える.一連のputメソッドに対応して、positionに従ってデータを取得するbyte get()や、指定された位置データを取得するbyte get(int index)、buffer中のデータを配列に書き込むByteBuffer get(byte[]dest)などの一連のgetメソッドもある.
さらによく使われるのは、Bufferの値を様々なチャンネルで対応する位置に書き込む書き込み操作です.
int num = channel.write(buffer);
mark() reset()
bufferのMark属性は主にPositionの値を一時的に保存するため,mark()メソッドを提供してmarkの値を現在のpositionに設定する.その後、必要に応じてreset()メソッドを呼び出し、Positionがmarkの場所に戻ることができます.
rewind() clear() compact()
rewind():positionを0にリセットし、markを-1に設定し、bufferを最初から読み書きすることができます.
clear():bufferの主なプロパティを再初期化します.bufferを再インスタンス化することに相当します.一般的にbufferを再充填する前にclear()を呼び出します.
clear()はbufferのデータを空にするのではなく、bufferの主なプロパティを初期化しただけです.そのため、後から書き換えたデータはposition=0の位置で書き換えられ、データが空になったことに相当します.
compact():bufferが新しいデータを書き込む前に呼び出す準備もできています.しかし、positionからlimitまでのデータを左に移動し、その上で書き込みを開始します.このときlimitはcapacityに等しく,positionは元のデータの右を指す.
具体的なcompact()実装:
// position limit 。
public LongBuffer compact() {
System.arraycopy(hb, ix(position()), hb, ix(0), remaining());
position(remaining());// position limit-position
limit(capacity());// limit capacity
discardMark();// mark -1
return this;
}
Channel
channelはデータソースまたはデータ書き込みの目的地javaである.NioパッケージにはFileChannelSocketChannelDatagramChannelServerSocketChannelが実装されています.
次のようになります.
SocketChannelとServerSocketChannelに注目します.
ChannelはIOのストリームに類似しており、読み出し操作時にはbufferにchannelのデータが埋め込まれ、書き込み操作時にはBufferのデータがchannelに書き込まれる.
なお、NIOレベルでの「読み出し」操作と「書き込み」操作は、チャネルに対して、すなわち「読み出し」はチャネルからbufferへ、「書き込み」はbufferからchannelへ(対応するchannelへの書き込み)である.一方、bufferに対応する操作は正反対であり、「読み取り」はbufferの「書き込み」に対応し、「書き込み」はbufferの「読み取り」に対応する.
読み取り操作:channel.read(buffer); channelからbufferにデータを読み込み、後続処理を行います.
書き込み操作:channel.write(buffer); Bufferからchannelにデータを書き込みます.
※どちらの方法もチャネルの実例方法です.※すべてのchannelはbufferとインタラクティブです.
SocketChannel
TCP接続を開きます.
SocketChannel sc = SocketChannel.open();
sc.connect(new InetSocketAddress("http://www.baidu.com", 80));
//
sc.read(buffer);
//
while(buffer.hasRemaining()) {
sc.write(buffer);
}
後続の部分は、Server SocketChannelでまた見ます.
ServerSocketChannel
マシンポートを傍受し、このポートから入るTCP接続を管理するために使用されます.
ServerSocketChannel ssc = ServerSocketChannel.open();
// 8080
ssc.socket().bind(new InetSocketAddress(8080));
while(true) {
// TCP , socketChannel 。
SocketChannel sc = ssc.accept();
}
DatagramChannel
DatagramChannelクラス処理サービス側とクライアント.
//
DatagramChannel dc = DatagramChannel.open();
dc.socket().bind(new InetSocketAddress(9090));
ByteBuffer byf = ByteBuffer.allocate(48);
ddc.receive(buf);
//
String data = "new Data";
ByteBuffer buf = ByteBuffer.allocate(48);
buf.put(data.getBytes());
buf.flip();// buffer
int byteSent = dc.send(buf, new InetSocketAddress("anyuri"), 80);
Selector
NIOのマルチプレクサは、1つのスレッドで複数のchannelを管理するためのものである.Selectorは非ブロックに基づいて構築する(FileChannelは使用できない).
使用方法(基本的なインタフェース操作):// Selector
Selector selector = Selector.open();
// Channel Selector
//
SocketChannel channel = SocketChannel.open(new InetSocketAddress("anyuri", 80));
channel.configureBlocking(false);
//
SelectionKey key = channel.register(selector, SelectionKey.OP_READ);
register(Selector s,int ops)の2番目のintパラメータは、リスニングが必要なタイプを表します.SelectionKeyでは、4種類の定数が定義されています.
OP_READ = 1 << 0; (00000001) OP_WRITE = 1 << 2; (00000100) OP_CONNECT = 1 << 3; (00001000) OP_ACCEPT = 1 << 4; (00010000)
readとacceptイベントを同時にリスニングするにはopsをOP_として指定するなど、複数のイベントを同時にリスニングできます.READ+OP_ACCEPTでいいです.
channelは、メソッドを登録してselectionKeyインスタンスを返します.
次にselect()メソッドを呼び出してチャネル情報を取得します.
手順をレビュー:
// Selector
Selector selector = Selector.open();
// Channel Selector
//
SocketChannel channel = SocketChannel.open(new InetSocketAddress("anyuri", 80));
channel.configureBlocking(false);
//
SelectionKey key = channel.register(selector, SelectionKey.OP_READ);
Selector操作の簡単な例:
SocketChannel sc = SocketChannel.open(new InetSocketAddress("anyuri", 80));
sc.configureBlocking(false);
Selector selector = Selector.open();
SelectionKey key = sc.register(selector, SelectionKey.OP_READ);
while(true) {
int readyChannelNum = selector.select();
if (readyChannelNum == 0) continue;
Set<SelectionKey> selectedKeys = selector.selectedKeys();
Iterator<SelectionKey> keyIterator = selectedKeys.iterator();
while(keyIterator.hasNext()) {
if (key.isAcceptable()) {
// accept
} else if (key.isConnectable()) {
// ,
} else if (key.isReadable()) {
//
} else if (key.isWritable()) {
//
}
// key
keyIterator.remove();
}
}