ブロッキングとノンブロッキングの概念
-
伝統的なIOは、流れを遮断しています。読み取りまたは書き込まれる、いくつかのデータがあるまで、それは)((スレッド呼び出しが読んだときに、ある)、または書き込み、スレッドはその間にスレッドが他のタスクを実行することができない、ブロックされています。したがって、通信ネットワーク入出力操作が完了すると、サーバは、クライアントごとに処理するための別々のスレッドを有していなければならないので、サーバが多数のクライアント、性能の急激な低下を処理する必要がある場合、スレッドは、ブロックされているため。
-
JavaのNIOは非ブロッキングモードです。スレッドは、チャネルからのデータを読み書きするときに使用可能なデータがない場合、スレッドは、他のタスクを実行することができます。スレッドは通常、別のスレッドが入出力複数のチャネルを管理するので、他のチャネル上のIOオペレーションを実行するためのIOアイドル時間を非ブロッキング。そのため、NIOは、サーバが同時にサーバーへのすべてのクライアント接続を処理するために、1つまたはスレッドの限られた数を使用することができます。
おもり
- NIO 3つのコアにネットワーク通信を使用して
①、チャンネル(チャンネル):接続するための責任を負います
java.nio.channels.Channel接口
のSelectableChannel
のSocketChannel
のServerSocketChannel
のDatagramChannel
Pipe.SinkChannel
Pipe.SourceChannel
②、バッファ(バッファ):データアクセスの責任
③、セレクタ(セレクタ):のSelectableChannelマルチプレクサです。SelectableChannelの状態監視のためのIO
- セレクタ(セレクタ)アプリケーション:
あなたがチャネルセレクタを登録します(セレクタSEL、int型OPS)を登録呼び出すと、セレクタは、チャネル上のイベントに耳を傾け、あなたは2番目のパラメータOPSを指定する必要があります。
イベントの種類に耳を傾けることができます(4つの定数で使用するとSelectionKey表現をすることができます):
読書:SelectionKey.OP_READ
写:SelectionKey.OP_WRITE
接続:SelectionKey.OP_CONNECT
レセプション:SelectionKey.OP_ACCEPT
登録されている場合より1つのイベントリスナーよりも、あなたは「ビットまたは」演算子を使用することができます。
例:
次のようにコードをブロックすることです。
//客户端
@Test
public void client() throws Exception{
//1,获取通道
SocketChannel socketChannel = SocketChannel.open(new InetSocketAddress("127.0.0.1", 9898));
FileChannel inChannel = FileChannel.open(Paths.get("1.jpg"),StandardOpenOption.READ);
//2,分配指定大小的缓冲区
ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
//3,读取本地文件,并发送到服务端
while (inChannel.read(byteBuffer) != -1){
byteBuffer.flip();
socketChannel.write(byteBuffer);
byteBuffer.clear();
}
socketChannel.shutdownOutput();
//接收服务端的反馈
int len = 0;
while ((len = socketChannel.read(byteBuffer)) != -1){
byteBuffer.flip();
System.out.println(new String(byteBuffer.array(),0,len));
byteBuffer.clear();
}
//4,关闭通道
inChannel.close();
socketChannel.close();
}
//服务端
@Test
public void server() throws Exception{
//1,获取通道
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
FileChannel outChannel = FileChannel.open(Paths.get("4.jpg"),StandardOpenOption.WRITE,StandardOpenOption.CREATE);
//2,绑定连接
serverSocketChannel.bind(new InetSocketAddress(9898));
//3,获取客户端连接的通道
SocketChannel accept = serverSocketChannel.accept();
//4,分配指定大小的缓冲区
ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
//5,接收客户端的数据,并保存到本地
while (accept.read(byteBuffer) != -1){
byteBuffer.flip();
outChannel.write(byteBuffer);
byteBuffer.clear();
}
//发送反映给客户端
byteBuffer.put("图片已收到,Over! Over!".getBytes());
byteBuffer.flip();
accept.write(byteBuffer);
//6,关闭通道
outChannel.close();
accept.close();
serverSocketChannel.close();
}
ノンブロッキング
コードは以下の通りであります:
//客户端
@Test
public void client() throws Exception {
//1,获取通道
SocketChannel socketChannel = SocketChannel.open(new InetSocketAddress("127.0.0.1", 8888));
//2,切换非阻塞式模式
socketChannel.configureBlocking(false);
//3,范围内配指定大小的缓冲区
ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
//4,发送数据给服务端
int num = 5;
while (num > 0) {
byteBuffer.put((LocalDateTime.now().toString() + ":" + num).getBytes());
byteBuffer.flip();
socketChannel.write(byteBuffer);
byteBuffer.clear();
--num;
}
//5,关闭通道
socketChannel.close();
}
//服务端
@Test
public void server() throws Exception {
//1,获取通道
ServerSocketChannel ssChannel = ServerSocketChannel.open();
//2,切换非阻塞模式
ssChannel.configureBlocking(false);
//3,绑定连接
ssChannel.bind(new InetSocketAddress(8888));
//4,获取选择器
Selector selector = Selector.open();
//5,将通道注册到选择器上,并指定“监听接收事件”
ssChannel.register(selector, SelectionKey.OP_ACCEPT);
//6,轮询式的获取选择器上已经“准备就绪”的事件
while (selector.select() > 0) {
//7,获取当前选择器中所有注册的“选择器(已就绪的监听事件)”
Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
while (iterator.hasNext()) {
//8,获取准备“就绪”的事件
SelectionKey sk = iterator.next();
//9,判断具体是什么事件准备就绪
if (sk.isAcceptable()) {
//10,若“接收就绪”,获取客户端连接
SocketChannel sChannel = ssChannel.accept();
//11,切换非阻塞模式
sChannel.configureBlocking(false);
//12,将该通道注册到选择器上
sChannel.register(selector, SelectionKey.OP_READ);
} else if (sk.isReadable()) {
//13,获取当前选择器上“读就绪”状态的通道
SocketChannel sChannel = (SocketChannel) sk.channel();
//14,读取数据
ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
int len = 0;
while ((len = sChannel.read(byteBuffer)) > 0) {
byteBuffer.flip();
System.out.println(new String(byteBuffer.array(),0, len));
byteBuffer.clear();
}
}
iterator.remove();
}
}
}
DatagramChannel
-
JavaのNIOのDatagramChannelは、UDPパケットを送信し、受信できるチャンネルです。
-
ステップ:
①、オープンのDatagramChannel
②、データを送信/受信します。コードは以下の通りであります:
@Test
public void send() throws Exception{
DatagramChannel dc = DatagramChannel.open();
dc.configureBlocking(false);
ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
int i = 5;
while (i > 0){
byteBuffer.put(("Tommey周"+i+"号").getBytes());
byteBuffer.flip();
dc.send(byteBuffer, new InetSocketAddress("127.0.0.1",9999));
byteBuffer.clear();
i--;
}
dc.close();
}
@Test
public void receive() throws Exception{
DatagramChannel dc = DatagramChannel.open();
dc.configureBlocking(false);
dc.bind(new InetSocketAddress(9999));
Selector selector = Selector.open();
dc.register(selector,SelectionKey.OP_READ);
while (selector.select() > 0){
Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
while (iterator.hasNext()){
SelectionKey sk = iterator.next();
if (sk.isReadable()){
ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
dc.receive(byteBuffer);
byteBuffer.flip();
System.out.println(new String(byteBuffer.array(),0,byteBuffer.limit()));
byteBuffer.clear();
}
}
iterator.remove();
}
}
パイプパイプライン
- JavaのNIOパイプは二つのスレッド間の単方向のデータ接続です。管路は、ソース及びシンクチャンネルを有しています。シンクチャンネルデータは、それがソースチャンネルから読み出され、書き込まれます。コードは以下の通りであります:
@Test
public void testPiPe() throws Exception{
//1,获取管道
Pipe pipe = Pipe.open();
//2,将缓冲区中的数据写入管道
ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
Pipe.SinkChannel sink = pipe.sink();
byteBuffer.put("通过单向管道发送数据".getBytes());
byteBuffer.flip();
sink.write(byteBuffer);
//3,读取缓冲区中的数据
Pipe.SourceChannel source = pipe.source();
byteBuffer.flip();
int len = source.read(byteBuffer);
System.out.println(new String(byteBuffer.array(),0, len));
source.close();
sink.close();
}