Java BIO、NIO、AIO的对比
三种IO模式的对比
1、BIO(同步阻塞),适用于连接数目较少且架构比较固定,服务器资源丰富的情况
2、NIO(同步非阻塞 jdk 1.4 开始支持),适用于连接数目多且连接比较短的架构,如聊天服务器、弹幕系统
3、AIO(异步非阻塞 jdk 1.7 开始支持),适用于连接数目较多且连接比较长的架构,,如相册服务器,目前应用不是非常广
BIO的通讯实例
服务器端
package bio;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Scanner;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
/**
* @author bbyh
* @date 2022/11/13 0013 10:07
* @description
*/
public class Server {
public static final String IP = "127.0.0.1";
public static final int PORT = 9000;
public static final int BUF_SIZE = 128;
public static final String EXIT = "EXIT";
public static final ExecutorService EXECUTOR_SERVICE = new ThreadPoolExecutor(10, 15,
10, TimeUnit.MINUTES, new ArrayBlockingQueue<>(5), new ThreadPoolExecutor.AbortPolicy());
private static boolean FLAG = true;
public static void main(String[] args) throws Exception {
ServerSocket ss = new ServerSocket(PORT);
System.out.println("服务器在 " + PORT + " 开始监听" + " 输入 " + EXIT + " 关闭服务器");
EXECUTOR_SERVICE.execute(() -> {
try {
while (FLAG) {
Socket accept = ss.accept();
OutputStream os = accept.getOutputStream();
InputStream is = accept.getInputStream();
os.write("服务器向客户端问好".getBytes());
EXECUTOR_SERVICE.execute(() -> {
byte[] buf = new byte[BUF_SIZE];
while (FLAG) {
try {
int read = is.read(buf);
System.out.println(accept.getRemoteSocketAddress() + " : " + new String(buf, 0, read));
} catch (IOException e) {
throw new RuntimeException(e);
}
}
});
}
} catch (IOException e) {
throw new RuntimeException(e);
}
});
Scanner scanner = new Scanner(System.in);
String next;
while (FLAG) {
next = scanner.next();
if (next.equals(EXIT)) {
FLAG = false;
System.exit(0);
}
}
}
}
客户端
package bio;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.nio.charset.StandardCharsets;
import java.util.Scanner;
import static bio.Server.*;
/**
* @author bbyh
* @date 2022/11/13 0013 10:07
* @description
*/
public class Client {
private static boolean FLAG = true;
public static void main(String[] args) throws Exception {
Socket socket = new Socket(IP, PORT);
System.out.println("客户端连接上 " + socket.getRemoteSocketAddress() + " 输入 " + EXIT + " 退出");
InputStream is = socket.getInputStream();
OutputStream os = socket.getOutputStream();
EXECUTOR_SERVICE.execute(() -> {
byte[] buf = new byte[BUF_SIZE];
while (FLAG) {
try {
int read = is.read(buf);
System.out.println(socket.getRemoteSocketAddress() + " : " + new String(buf, 0, read));
} catch (IOException e) {
throw new RuntimeException(e);
}
}
});
Scanner scanner = new Scanner(System.in);
String next;
while (FLAG) {
next = scanner.next();
if (next.equals(EXIT)) {
FLAG = false;
System.exit(0);
}
os.write(next.getBytes(StandardCharsets.UTF_8));
}
}
}
BIO的优缺点:代码简单,但效率不高,经常阻塞,且线程创建很多,资源处于浪费中;阻塞的地方为 accept、read 方法
NIO通讯实例
服务器端
package nio;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.nio.charset.StandardCharsets;
import java.util.Iterator;
import java.util.Scanner;
import java.util.Set;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
/**
* @author bbyh
* @date 2022/11/12 0012 20:03
* @description
*/
public class Server implements Runnable {
public static final String IP = "127.0.0.1";
public static final int PORT = 9000;
public static final int BUF_SIZE = 128;
public static final String EXIT = "EXIT";
public static final ExecutorService EXECUTOR_SERVICE = new ThreadPoolExecutor(10, 15,
10, TimeUnit.MINUTES, new ArrayBlockingQueue<>(5), new ThreadPoolExecutor.AbortPolicy());
private static boolean FLAG = true;
private static void startServer() throws IOException {
Selector selector = Selector.open();
ServerSocketChannel ssc = ServerSocketChannel.open();
ssc.configureBlocking(false);
ssc.bind(new InetSocketAddress(PORT));
ssc.register(selector, SelectionKey.OP_ACCEPT);
System.out.println("服务器在 " + PORT + " 端口开启监听");
while (FLAG) {
selector.select();
Set<SelectionKey> selectionKeys = selector.selectedKeys();
Iterator<SelectionKey> iterator = selectionKeys.iterator();
while (iterator.hasNext()) {
SelectionKey key = iterator.next();
if (key.isAcceptable()) {
acceptOperation(ssc, selector);
}
if (key.isReadable()) {
readOperation(selector, key);
}
iterator.remove();
}
}
}
private static void readOperation(Selector selector, SelectionKey key) throws IOException {
SocketChannel sc = (SocketChannel) key.channel();
ByteBuffer buffer = ByteBuffer.allocate(BUF_SIZE);
int read = sc.read(buffer);
String message = "";
if (read > 0) {
message = new String(buffer.array(), 0, read);
}
sc.register(selector, SelectionKey.OP_READ);
if (message.length() > 0) {
System.out.println(sc.getRemoteAddress() + " 发送: " + message);
castMessage(message, selector, sc);
}
}
private static void castMessage(String message, Selector selector, SocketChannel sc) throws IOException {
Set<SelectionKey> keys = selector.keys();
for (SelectionKey key : keys) {
SelectableChannel targetChannel = key.channel();
if (targetChannel instanceof SocketChannel && targetChannel != sc) {
((SocketChannel) targetChannel).write(ByteBuffer.wrap((sc.getRemoteAddress() + " : " + message).getBytes(StandardCharsets.UTF_8)));
}
}
}
private static void acceptOperation(ServerSocketChannel ssc, Selector selector) throws IOException {
SocketChannel sc = ssc.accept();
sc.configureBlocking(false);
sc.register(selector, SelectionKey.OP_READ);
sc.write(ByteBuffer.wrap("欢迎进入聊天室,请注意隐私安全".getBytes(StandardCharsets.UTF_8)));
}
public static void main(String[] args) {
EXECUTOR_SERVICE.execute(new Server());
System.out.println("输入 EXIT 关闭服务器");
Scanner scanner = new Scanner(System.in);
String input = scanner.next();
while (FLAG) {
if (input.equals(EXIT)) {
FLAG = false;
System.exit(0);
}
input = scanner.next();
}
}
@Override
public void run() {
try {
startServer();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
客户端
package nio;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;
import java.nio.charset.StandardCharsets;
import java.util.Iterator;
import java.util.Scanner;
import java.util.Set;
import static nio.Server.*;
/**
* @author bbyh
* @date 2022/11/12 0012 20:03
* @description
*/
public class Client {
private static boolean FLAG = true;
private static void startClient() throws IOException {
SocketChannel sc = SocketChannel.open(new InetSocketAddress(IP, PORT));
System.out.println("连接上 " + sc.getRemoteAddress() + " 输入EXIT退出");
Selector selector = Selector.open();
sc.configureBlocking(false);
sc.register(selector, SelectionKey.OP_READ);
EXECUTOR_SERVICE.execute(() -> {
try {
readMessage(selector);
} catch (IOException e) {
throw new RuntimeException(e);
}
});
Scanner scanner = new Scanner(System.in);
String input;
while (FLAG) {
input = scanner.next();
if ("".equals(input.replaceAll(" ", ""))) {
continue;
}
if (input.equals(EXIT)) {
FLAG = false;
break;
}
sc.write(ByteBuffer.wrap(input.getBytes(StandardCharsets.UTF_8)));
}
}
private static void readMessage(Selector selector) throws IOException {
while (FLAG) {
selector.select();
Set<SelectionKey> selectionKeys = selector.selectedKeys();
Iterator<SelectionKey> iterator = selectionKeys.iterator();
while (iterator.hasNext()) {
SelectionKey key = iterator.next();
if (key.isReadable()) {
readOperation(selector, key);
}
iterator.remove();
}
}
}
private static void readOperation(Selector selector, SelectionKey key) throws IOException {
SocketChannel sc = (SocketChannel) key.channel();
ByteBuffer buffer = ByteBuffer.allocate(BUF_SIZE);
int read = sc.read(buffer);
String message = "";
if (read > 0) {
message = new String(buffer.array(), 0, read);
}
sc.register(selector, SelectionKey.OP_READ);
if (message.length() > 0) {
System.out.println(message);
}
}
public static void main(String[] args) throws IOException {
startClient();
}
}
NIO的三大组件:Buffer、Channel、Selector;以及更高级核心的Reactor模型,主从Reactor模型 + 事件驱动;理解这些可以更好的理解netty的工作原理
NIO的优缺点:代码相对复杂一些,但线程利用效率更高;采用事件驱动,由Selector进行事件状态判断与事件分发给Handler处理;但需要采用对Selector的轮询方式,某种程度上也效率偏低