一、普通IO
传统的IO当客户端请求服务端的资源,这个时候不确定是否准备好资源。如果这个时候存在问题,当前线程则会一直等待,不能再处理其他问题。就造成了阻塞。
二、NIO
NIO新增了选择器,所有的通道都会注册到选择器上,由选择器进行监控。当选择器监控到,客户端请求的资源都准备好的时候,再调用服务端的一个或多个线程,从而提高线程的利用率。
阻塞式:
package com.stu.nio;
import org.junit.Test;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
/**
* 一、使用 NIO 完成王阔通信的三个核心:
* 1. 通道(Channel):负责连接
* java.nio.channels.Channel 接口:
* |-- SelectableChannel:
* |-- SocketChannel:
* |-- ServerSocketChannel
* |-- DatagramChannel
*
* |-- Pipe.SinkChannel
* |-- Pipo.SourceChannel
*
* 2. 缓冲区(Buffer):负责数据的存取
* 3. 选择器(Selector): 是SelectableChannel的多路复用器。用于监控SelectableChannel 的IO 状况。
*
*/
public class TestBlockingNIO {
/**
* 客户端
*/
@Test
public void test1 () throws IOException {
// 1. 获取通道
SocketChannel socketChannel = SocketChannel.open(new InetSocketAddress("127.0.0.1", 9998));
FileChannel inChannel = FileChannel.open(Paths.get("1.jpeg"), StandardOpenOption.READ);
// 2. 分配指定大小的缓冲区
ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
// 3. 读取本地文件,并发送到服务器端
while (inChannel.read(byteBuffer) != -1) {
byteBuffer.flip();
socketChannel.write(byteBuffer);
byteBuffer.clear();
}
// 4. 关闭通道
inChannel.close();
socketChannel.close();
}
/**
* 服务端
*/
@Test
public void test2() throws IOException {
// 1. 获取通道
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
FileChannel outFileChannel = FileChannel.open(Paths.get("3.jpg"), StandardOpenOption.WRITE,
StandardOpenOption.CREATE, StandardOpenOption.READ);
// 2. 绑定连接
serverSocketChannel.bind(new InetSocketAddress(9998));
// 3. 获取客户端连接的通道
SocketChannel socketChannel = serverSocketChannel.accept();
// 4. 分配指定大小的缓存区
ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
// 5. 接收客户端的数据,并保存到本地
while (socketChannel.read(byteBuffer) != -1) {
byteBuffer.flip();
outFileChannel.write(byteBuffer);
byteBuffer.clear();
}
// 6. 关闭对应的通道
socketChannel.close();
outFileChannel.close();
serverSocketChannel.close();
}
}
package com.stu.nio;
import org.junit.Test;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
public class TestBlockingNIO2 {
/**
* 客户端
*/
@Test
public void client() throws IOException {
SocketChannel socketChannel = SocketChannel.open(new InetSocketAddress("127.0.0.1",9998));
FileChannel inFileChannel = FileChannel.open(Paths.get("1.jpeg"), StandardOpenOption.READ);
ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
while (inFileChannel.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();
}
inFileChannel.close();
socketChannel.close();
}
/**
* 服务端
*/
@Test
public void server() throws IOException {
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
FileChannel outFileChannel = FileChannel.open(Paths.get("5.jpg"), StandardOpenOption.WRITE,
StandardOpenOption.CREATE);
serverSocketChannel.bind(new InetSocketAddress(9998));
SocketChannel socketChannel = serverSocketChannel.accept();
ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
while (socketChannel.read(byteBuffer) != -1) {
byteBuffer.flip();
outFileChannel.write(byteBuffer);
byteBuffer.clear();
}
// 发送反馈给客户端
byteBuffer.put("服务端接收客户端数据成功".getBytes());
byteBuffer.flip();
socketChannel.write(byteBuffer);
socketChannel.close();
outFileChannel.close();
serverSocketChannel.close();
}
}
SelectionKey:
非阻塞式:
package com.stu.nio;
import org.junit.Test;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.util.Date;
import java.util.Iterator;
import java.util.Scanner;
public class TestNonBlockingNIO {
/**
* 客户端
*/
@Test
public void client() throws IOException {
// 1.获取通道
SocketChannel socketChannel = SocketChannel.open(new InetSocketAddress("127.0.0.1", 9998));
// 2.切换非阻塞模式
socketChannel.configureBlocking(false);
// 3. 分配指定大小的缓存区
ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
// 4. 发送数据给服务端
Scanner scanner = new Scanner(System.in);
while (scanner.hasNext()) {
String str = scanner.next();
String dateStr = new Date().toString() + "\n" +str;
byteBuffer.put(dateStr.getBytes());
byteBuffer.flip();
socketChannel.write(byteBuffer);
byteBuffer.clear();
}
// 5. 关闭通道
socketChannel.close();
}
/**
* 服务端
*/
@Test
public void server() throws IOException {
// 1. 获取通道
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
// 2. 切换非阻塞模式
serverSocketChannel.configureBlocking(false);
// 3. b绑定连接
serverSocketChannel.bind(new InetSocketAddress(9998));
// 4. 获取选择器
Selector selector = Selector.open();
// 5.将通道注册到选择器上
serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
// 6. 轮询式的获取选择器上已经“猪呢比就绪”的时间
while(selector.select() > 0 ) {
// 7.获取当前选择器中,所有注册的"选择件"(已就绪的舰艇)
Iterator<SelectionKey> it = selector.selectedKeys().iterator();
while(it .hasNext()) {
// 8. 获取猪呢比就绪的事件
SelectionKey selectionKey = it.next();
// 9. 判断具体
if (selectionKey.isAcceptable()) {
// 10. 若接收就绪,获取客户端连接
SocketChannel socketChannel = serverSocketChannel.accept();
// 11. 切换非阻塞模式
socketChannel.configureBlocking(false);
// 12. 将该通道注册到
socketChannel.register(selector, SelectionKey.OP_READ);
} else if (selectionKey.isReadable()) {
// 13. 获取当前选择器上“读就绪,”状态的通道
SocketChannel socketChannel = (SocketChannel) selectionKey.channel();
// 14. 读取数据
ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
int len = 1;
while ((len = socketChannel.read(byteBuffer)) > 0) {
byteBuffer.flip();
System.out.println("----" + new String(byteBuffer.array(),0,len));
byteBuffer.clear();
}
}
// 15. 取消选择键
it.remove();
}
}
}
}
package com.stu.nio;
import org.junit.Test;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.DatagramChannel;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.util.Date;
import java.util.Iterator;
import java.util.Scanner;
public class TestNonBlockingNIO2 {
@Test
public void send() throws IOException {
DatagramChannel datagramChannel = DatagramChannel.open();
datagramChannel.configureBlocking(false);
ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
Scanner scanner = new Scanner(System.in);
while (scanner.hasNext()){
String str = scanner.next();
byteBuffer.put((new Date().toString() + "\n" +str).getBytes());
byteBuffer.flip();
datagramChannel.send(byteBuffer, new InetSocketAddress("127.0.0.1", 9998));
byteBuffer.clear();
}
datagramChannel.close();
}
@Test
public void receive() throws IOException {
DatagramChannel datagramChannel = DatagramChannel.open();
datagramChannel.configureBlocking(false);
datagramChannel.bind(new InetSocketAddress(9998));
Selector selector = Selector.open();
datagramChannel.register(selector, SelectionKey.OP_READ);
while (selector.select() >0) {
Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
while (iterator.hasNext()) {
SelectionKey selectionKey = iterator.next();
if (selectionKey.isReadable()) {
ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
datagramChannel.receive(byteBuffer);
byteBuffer.flip();
System.out.println(new String(byteBuffer.array(), 0 , byteBuffer.limit()));
byteBuffer.clear();
}
}
iterator.remove();
}
}
}
学自,b站尚硅谷