NIO 是 Java 1.4 引入的 java.nio 包,提供了 Channel、Selector、Buffer 等新的抽象,可以构建多路复用的、同步非阻塞 IO 程序,同时提供了更接近操作系统底层高性能的数据操作方式。
一般在Java中涉及高性能服务器编程时,会时候Netty等网络库,Netty的底层就是依赖于Java提供NIO机制。熟悉NIO编程对于理解Netty等网络库有很大的帮助。
下面代码展示了简单的Java NIO编程流程。
服务器:NioServer
package com.nio;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.util.Iterator;
import java.util.Set;
public class NioServer {
public static void main(String[] args) {
try{
//得到一个Selecor对象(多路复用器)
Selector selector = Selector.open();
//Windows下: sun.nio.ch.WindowsSelectorImpl
//Linux下: sun.nio.ch.EPollSelectorImpl
System.out.println(selector.getClass());
//创建ServerSocketChannel,类似于BIO中的ServerSocket
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
//绑定端口9090
serverSocketChannel.bind(new InetSocketAddress(9090));
//设置为非阻塞
serverSocketChannel.configureBlocking(false);
//将serverSocketChannel注册到selector上,关注OP_ACCEPT
serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
//循环等待客户端连接
while (true) {
selector.select();
//返回selector得到的结果集
Set<SelectionKey> selectionKeys = selector.selectedKeys();
//遍历 Set<SelectionKey>, 使用迭代器遍历
Iterator<SelectionKey> keyIterator = selectionKeys.iterator();
while (keyIterator.hasNext()) {
//获取到SelectionKey
SelectionKey selectionKey = keyIterator.next();
//根据key 对应的channel发生的事件做相应处理
if(selectionKey.isValid()){
//OP_ACCEPT, 有新的客户端连接
if(selectionKey.isAcceptable()) {
//客户端生成SocketChannel
SocketChannel socketChannel = serverSocketChannel.accept();
//SocketChannel 设置为非阻塞
socketChannel.configureBlocking(false);
//将socketChannel 注册到selector, 关注OP_READ,同时给socketChannel关联一个Buffer
socketChannel.register(selector, SelectionKey.OP_READ, ByteBuffer.allocate(1024));
}
//OP_READ,有数据可以读
if(selectionKey.isReadable()) {
//通过selectionKey获取到对应channel
SocketChannel channel = (SocketChannel)selectionKey.channel();
//获取到该channel关联的buffer
ByteBuffer buffer = (ByteBuffer)selectionKey.attachment();
channel.read(buffer);
System.out.println("form Client:" + new String(buffer.array()).trim());
}
}
//从集合中移动当前的selectionKey
keyIterator.remove();
}
}
}catch(IOException e){
e.printStackTrace();
}
}
}
客户端:NioClient
package com.nio;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SocketChannel;
public class NioClient {
public static void main(String[] args) {
try{
//得到一个SocketChannel
SocketChannel socketChannel = SocketChannel.open();
//设置非阻塞
socketChannel.configureBlocking(false);
//提供服务器端的ip和端口
InetSocketAddress inetSocketAddress = new InetSocketAddress("127.0.0.1", 9090);
//连接服务器,非阻塞情况下,需要调用finishConnect
/*
* If this channel is in non-blocking mode then an invocation of this method initiates
* a non-blocking connection operation. If the connectionis established immediately,
* as can happen with a local connection, then this method returns true.
* Otherwise this method returns false and the connection operation must
* later be completed by invoking the finishConnect method.
*
* */
if(!socketChannel.connect(inetSocketAddress)){
while(!socketChannel.finishConnect()){
System.out.println("connect...");
}
}
//如果连接成功,就发送数据
String str = "hello, world";
ByteBuffer buffer = ByteBuffer.allocate(str.length());
buffer.put(str.getBytes());
buffer.flip();
//将数据写入 channel
socketChannel.write(buffer);
System.in.read();
}catch(IOException e){
e.printStackTrace();
}
}
}
以上代码参考韩顺平老师的Netty课程中的NIO讲解部分。