NIO中主要包含以下3大对象
- Buffer
- Channel
- Selector
NIO的核心原理如下图所示
缓冲区Buffer
常用方法
名称 | 说明 |
flip() | 切换为读模式,limit设置为position位置,position设置为0 |
get() | 读取一个字节 |
get(int index) | 读取指定索引的字节 |
get(byte[] dest) | 读取多个字节 |
rewind() | 将position设置为0,可以重复读取 |
clear() | 切换为写模式,position设置为0,limit设置为capacity |
array() | 把缓冲区转成字节数组 |
通道Channel
1.通道可读可写,而流一般是单向的(所以有InputStream, OutputStream)
2.通道可以异步读写
3.通道总是基于Buffer来读写
Channel相关的常用类
- FileChannel
- DatagramChannel
- ServerSocketChannel
- SocketChannel
FileChannel用于文件的数据读写,DatagramChannel用于UDP的数据读写,ServerSocketChannel和SocketChannel主要用于TCP的数据读写(类似于ServerSocket和Socket)。
代码示例如下:
NIOServerDemo.java
public class NIOServerDemo {
public static void main(String[] args) throws IOException, InterruptedException {
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
serverSocketChannel.bind(new InetSocketAddress(9999));
serverSocketChannel.configureBlocking(false);
System.out.println("server is started...");
while (true) {
SocketChannel socketChannel = serverSocketChannel.accept();
if(socketChannel==null){
System.out.println("no client, do other things...");
Thread.sleep(2000);
continue;
}
ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
int read = socketChannel.read(byteBuffer); // read为正:读到的有效字节数; read=0: 没有读到数据; read=-1:读到末尾
String message = new String(byteBuffer.array(), 0, read, StandardCharsets.UTF_8);
System.out.println("client msg: "+message);
socketChannel.write(ByteBuffer.wrap("hello client".getBytes(StandardCharsets.UTF_8)));
socketChannel.close();
}
}
}
NIOClientDemo.java
public class NIOClientDemo {
public static void main(String[] args) throws IOException {
SocketChannel socketChannel = SocketChannel.open();
socketChannel.connect(new InetSocketAddress("127.0.0.1", 9999));
socketChannel.write(ByteBuffer.wrap("hi server".getBytes(StandardCharsets.UTF_8)));
ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
int read = socketChannel.read(byteBuffer);
String message = new String(byteBuffer.array(), 0, read, StandardCharsets.UTF_8);
System.out.println("server msg:"+message);
socketChannel.close();
}
}
选择器Selector
selector基本介绍
常用API
selector.open() 返回一个选择器对象
selector.select() 阻塞
selector.select(1000) 阻塞1000毫秒
selector.selectedKeys() 返回selectedKey集合
SelectionKey.isAcceptable() 是否是连接继续事件
SelectionKey.isConnectable() 是否是连接就绪事件
SelectionKey.isReadable() 是否是读就绪事件
SelectionKey.isWritable() 是否是写就绪事件
SelectionKey中定义的4种事件
SelectionKey.OP_ACCEPT 接收连接继续事件
SelectionKey.OP_CONNECT 连接就绪事件
SelectionKey.OP_READ 读就绪事件
SelectionKey.OP_WRITE 写就绪事件
selector编码
基于Selector的使用示例如下:
NIOSelectorServerDemo.java
public class NIOSelectorServerDemo {
public static void main(String[] args) throws IOException {
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
serverSocketChannel.bind(new InetSocketAddress(9999));
serverSocketChannel.configureBlocking(false);
Selector selector = Selector.open();
serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
System.out.println("server started...");
while (true) {
int select = selector.select(2000);
if (select == 0) {
System.out.println("no event...");
continue;
}
Set<SelectionKey> selectionKeys = selector.selectedKeys();
Iterator<SelectionKey> iterator = selectionKeys.iterator();
while (iterator.hasNext()) {
SelectionKey selectionKey = iterator.next();
if (selectionKey.isAcceptable()) {
SocketChannel socketChannel = serverSocketChannel.accept();
System.out.println("client try to connect...");
socketChannel.configureBlocking(false);
socketChannel.register(selector, SelectionKey.OP_READ);
}
if (selectionKey.isReadable()) {
SocketChannel socketChannel = (SocketChannel) selectionKey.channel();
ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
int read = socketChannel.read(byteBuffer);
if (read > 0) {
System.out.println("msg from client:" + new String(byteBuffer.array(), 0, read, StandardCharsets.UTF_8));
//write msg to client
socketChannel.write(ByteBuffer.wrap("hello client".getBytes(StandardCharsets.UTF_8)));
socketChannel.close();
}
}
iterator.remove();
}
}
}
}
NIOSelectorClientDemo.java
public class NIOSelectorClientDemo {
public static void main(String[] args) throws IOException {
SocketChannel socketChannel = SocketChannel.open();
socketChannel.connect(new InetSocketAddress("127.0.0.1", 9999));
socketChannel.write(ByteBuffer.wrap("hi server".getBytes(StandardCharsets.UTF_8)));
ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
int read = socketChannel.read(byteBuffer);
String message = new String(byteBuffer.array(), 0, read, StandardCharsets.UTF_8);
System.out.println("server msg:"+message);
socketChannel.close();
}
}