JAVA的BIO和NIO
2022-02-07 本文已影响0人
东南枝下
回顾telnet命令的使用
# 连接到主机
open 127.0.0.1 9000
BIO
阻塞IO,在建立连接serverSocket.accept()
和获取数据clientSocket.getInputStream().read(bytes)
处阻塞
单线程无法处理多个连接,高并发环境需要创建大量线程处理,线程会爆炸式增长
public class SocketServer {
public static void main(String[] args) throws IOException {
ServerSocket serverSocket = new ServerSocket(9000);
while (true) {
System.out.println("等待连接...");
// 没有连接,阻塞在此处
Socket clientSocket = serverSocket.accept();
System.out.println("有客户端连接了....");
handler(clientSocket);
clientSocket.close();
}
}
private static void handler(Socket clientSocket) throws IOException {
byte[] bytes = new byte[1024];
System.out.println("准备read...");
while (true) {
// 阻塞方法,阻塞在此处
int read = clientSocket.getInputStream().read(bytes);
System.out.println("read 完毕...");
if (read != -1) {
String inStr = new String(bytes, 0, read);
inStr = inStr.trim();
System.out.println("客户端接收到到数据 " + inStr);
if ("exit".equals(inStr)) {
break;
}
}
}
}
}
NIO
简单NIO
NIO是通过channel来建立连接,读写数据的
可以设置serverSocket.configureBlocking(false);
使建立连接serverSocket.accept();
为非阻塞
可以设置socketChannel.configureBlocking(false);
使读取数据channel.read(byteBuffer);
为非阻塞
单一线程可以处理多个连接
当连接数巨大时,每次读取需要遍历一个巨大的连接列表
package nio;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.ArrayList;
import java.util.List;
/**
* @author Jenson
*/
public class NioServer {
/**
* 保存客户端链接
*/
static List<SocketChannel> channelList = new ArrayList<>();
public static void main(String[] args) throws IOException {
// 创建NIO
ServerSocketChannel serverSocket = ServerSocketChannel.open();
serverSocket.socket().bind(new InetSocketAddress(9000));
// 设置为非阻塞
serverSocket.configureBlocking(false);
System.out.println("服务启动成功");
while (true) {
// 非阻塞在此处不会阻塞
// NIO非阻塞由操作系统内部实现,底层调用了linux内核的accept函数
SocketChannel socketChannel = serverSocket.accept();
if (socketChannel != null) {
System.out.println("链接成功");
// 设置为非阻塞
socketChannel.configureBlocking(false);
// 保存客户端链接
channelList.add(socketChannel);
}
// 遍历链接进行数据读取
for (SocketChannel channel : channelList) {
ByteBuffer byteBuffer = ByteBuffer.allocate(128);
// 非阻塞模式在此处就不会阻塞
int len = channel.read(byteBuffer);
if (len > 0) {
System.out.println("接收到到消息为 " + new String(byteBuffer.array()));
} else if (len == -1) {
// 客户端断开链接
channelList.remove(channel);
}
}
}
}
}
selector多路复用器
将Channel
注册到Selector
中,注册时需要指定监听的事件
Selector
相当于一个集合,记录了那些有事件发生的集合
当有事件发生时selector.select();
就会跳出阻塞
selectionKeySet.iterator()
可以获取到发生的事件
SelectionKey.channel()
可以获取到发生事件的Chanel
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.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import java.util.Set;
/**
* @author Jenson
*/
public class NioSelectorServer {
public static void main(String[] args) throws IOException {
// 创建NIO
ServerSocketChannel serverSocket = ServerSocketChannel.open();
serverSocket.socket().bind(new InetSocketAddress(9000));
// 设置为非阻塞
serverSocket.configureBlocking(false);
// 打开selector处理channel,创建epoll
Selector selector = Selector.open();
// 把ServerSocketChannel注册到Selector上
SelectionKey selectionKey = serverSocket.register(selector, SelectionKey.OP_ACCEPT);
System.out.println("服务启动成功");
while (true) {
// 等待阻塞需要处理到的事件发生 (SelectionKey.OP_ACCEPT)
selector.select();
// 获取selector注册的全部事件的selectionKey实例
Set<SelectionKey> selectionKeySet = selector.selectedKeys();
Iterator<SelectionKey> selectionKeyIterator = selectionKeySet.iterator();
while (selectionKeyIterator.hasNext()) {
SelectionKey key = selectionKeyIterator.next();
if (key.isAcceptable()) {
// 如果是OP_ACCEPT,则进行连接获取和事件注册
ServerSocketChannel server = (ServerSocketChannel) key.channel();
SocketChannel socketChannel = server.accept();
socketChannel.configureBlocking(false);
// 注册客户端 读事件
socketChannel.register(selector, SelectionKey.OP_READ);
System.out.println("客户端连接成功");
} else if (key.isReadable()) {
// 如果是OP_READ,则进行读取和打印
SocketChannel socketChannel = (SocketChannel) key.channel();
ByteBuffer byteBuffer = ByteBuffer.allocate(128);
int len = socketChannel.read(byteBuffer);
if (len > 0) {
System.out.println("接收到到消息为 " + new String(byteBuffer.array()));
} else if (len == -1) {
// 客户端断开链接
socketChannel.close();
}
}
selectionKeyIterator.remove();
}
}
}
}
参考学习: https://www.bilibili.com/video/BV1fA41157Ht?share_source=copy_web