BIO
当用户进程发起请求时,一直阻塞直到数据拷贝到用户空间为止才返回。
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.ServerSocket;
import java.net.Socket;
public class BIOEchoServer {
public static void main(String[] args) throws IOException {
ServerSocket serverSocket = new ServerSocket(8080);
while (true) {
Socket socket = serverSocket.accept();
System.out.println("new client conn: " + socket);
new Thread(() -> {
try {
BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
String msg;
while ((msg = reader.readLine()) != null) {
System.out.println("receive client msg: " + msg);
}
} catch (IOException e) {
e.printStackTrace();
}
}).start();
}
}
}
每来一个客户端连接都要分配一个线程,如果客户端一直增加,服务端线程会无限增加,直到服务器资源耗尽。
NIO
Java 中的 NIO 使用的是 IO 多路复用技术实现的,多个 IO 操作共同使用一个 selector(选择器)去询问哪些 IO 准备好了,selector 负责通知那些数据准备好了的 IO,它们再自己去请求内核数据。
public class NIOEchoServer {
public static void main(String[] args) throws IOException {
// 创建一个Selector
Selector selector = Selector.open();
// 创建ServerSocketChannel
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
// 绑定8080端口
serverSocketChannel.bind(new InetSocketAddress(8002));
// 设置为非阻塞模式
serverSocketChannel.configureBlocking(false);
// 将Channel注册到selector上,并注册Accept事件
serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
System.out.println("server start");
while (true) {
// 阻塞在select上(第一阶段阻塞)
selector.select();
// 如果使用的是select(timeout)或selectNow()需要判断返回值是否大于0
// 有就绪的Channel
Set<SelectionKey> selectionKeys = selector.selectedKeys();
// 遍历selectKeys
Iterator<SelectionKey> iterator = selectionKeys.iterator();
while (iterator.hasNext()) {
SelectionKey selectionKey = iterator.next();
// 如果是accept事件
if (selectionKey.isAcceptable()) {
// 强制转换为ServerSocketChannel
ServerSocketChannel ssc = (ServerSocketChannel) selectionKey.channel();
SocketChannel socketChannel = ssc.accept();
System.out.println("accept new conn: " + socketChannel.getRemoteAddress());
socketChannel.configureBlocking(false);
// 将SocketChannel注册到Selector上,并注册读事件
socketChannel.register(selector, SelectionKey.OP_READ);
} else if (selectionKey.isReadable()) {
// 如果是读取事件
// 强制转换为SocketChannel
SocketChannel socketChannel = (SocketChannel) selectionKey.channel();
// 创建Buffer用于读取数据
ByteBuffer buffer = ByteBuffer.allocate(1024);
// 将数据读入到buffer中(第二阶段阻塞)
int length = socketChannel.read(buffer);
if (length > 0) {
buffer.flip();
byte[] bytes = new byte[buffer.remaining()];
// 将数据读入到byte数组中
buffer.get(bytes);
// 换行符会跟着消息一起传过来
String content = new String(bytes, "UTF-8").replace("\r\n", "");
System.out.println("receive msg: " + content);
}
}
iterator.remove();
}
}
}
}
AIO
public class AIOEchoServer {
public static void main(String[] args) throws IOException {
// 启动服务端
AsynchronousServerSocketChannel serverSocketChannel = AsynchronousServerSocketChannel.open();
serverSocketChannel.bind(new InetSocketAddress(8003));
System.out.println("server start");
// 监听accept事件,完全异步,不会阻塞
serverSocketChannel.accept(null, new CompletionHandler<AsynchronousSocketChannel, Object>() {
@Override
public void completed(AsynchronousSocketChannel socketChannel, Object attachment) {
try {
System.out.println("accept new conn: " + socketChannel.getRemoteAddress());
// 再次监听accept事件
serverSocketChannel.accept(null, this);
// 消息的处理
while (true) {
ByteBuffer buffer = ByteBuffer.allocate(1024);
// 将数据读入到buffer中
Future<Integer> future = socketChannel.read(buffer);
if (future.get() > 0) {
buffer.flip();
byte[] bytes = new byte[buffer.remaining()];
// 将数据读入到byte数组中
buffer.get(bytes);
String content = new String(bytes, "UTF-8");
// 换行符会当成另一条消息传过来
if (content.equals("\r\n")) {
continue;
}
System.out.println("receive msg: " + content);
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
public void failed(Throwable exc, Object attachment) {
System.out.println("failed");
}
});
// 阻塞住主线程
System.in.read();
}
}
阻塞与非阻塞
数据就绪前要不要等待
阻塞: 没有数据传过来时,读会阻塞直到有数据,缓冲区满时,写操作也会阻塞
非阻塞遇到这些情况,都会直接返回。
同步与异步
数据就绪后,数据操作谁完成
数据就绪后需要自己去读取就是同步,数据就绪直接读好再回调给程序是异步。
为什么Netty 仅支持NIO
为什么不建议(deprdcate)阻塞I/O(BIO/OIO)
连接数高情况下:阻塞->消耗资源,效率低