什么是NIO:
NIO的全称是New IO,也就是新的IO,源于JDK1.4
NIO入门:
NIO的核心类是Channel和Buffer,其中:
Channel用于读写数据,Buffer用于缓存数据
1、Channel的类型与创建方法:
一个Buffer主要由position, limit, capacity三个变量来控制读写的过程
其中,position为当前指针所在的位置,limit为实际读取的数据长度,capacity为缓冲区的总长度
Buffer的常见类型有:Byte/Char/Short/Int/Long/Float/DoubleBuffer,还有MappedByteBuffer
NIO进阶:
NIO的最大优点是非阻塞IO,所谓非阻塞IO,是相对于阻塞IO来说的
对于传统的IO,当Server端调用accept()和read()方法时,线程都会被阻塞
而非阻塞IO的原理就是采用事件驱动机制,由Selector类来负责多个通道上事件的检测,并将事件分发给对应的Channel来处理
其中事件的类型为SelectionKey,其中包含了事件的状态信息,以及该事件对应绑定到的Channel
1、Selector的结构模型:
首先将Channel注册到Selector上(Channel的信息会被封装到SelectionKey类型的对象中)
当指定了这个Channel的请求来到时,该SelectionKey就会变成可用状态
这样Selector在轮询的时候,就可以把这个SelectionKey获取出来,并对其进行操作
这样做的好处是可以将多个Channel注册到同一个Selector上,而不用为每个Channel单独启动一个线程并让其阻塞
2、Selector的使用流程:
Android中使用NIO的注意事项:
由于Android2.2中对IPv6的协议支持有BUG,因此在2.2中如果使用NIO,应当在类中添加以下静态代码:
此外,由于Android4.0规范不允许在主线程中进行网络连接等耗时操作
因此在开发时,所有的NIO操作都应放到子线程中进行
NIO的全称是New IO,也就是新的IO,源于JDK1.4
NIO入门:
NIO的核心类是Channel和Buffer,其中:
Channel用于读写数据,Buffer用于缓存数据
1、Channel的类型与创建方法:
//FileChannel:读写文件的Channel
FileChannel in = fileInputStream.getChannel();
FileChannel out = fileOuputStream.getChannel();
//DatagramChannel:用于进行UDP连接的Channel
DatagramChannel udp = DatagramChannel.open();
//SocketChannel:用于进行TCP连接的Channel(Client端)
SocketChannel tcpClient = SocketChannel.open();
//ServerSocketChannel:用于进行TCP连接的Channel(Server端)
ServerSocketChannel tcpServer = ServerSocketChannel.open();
2、Buffer的类型与常用方法:
一个Buffer主要由position, limit, capacity三个变量来控制读写的过程
其中,position为当前指针所在的位置,limit为实际读取的数据长度,capacity为缓冲区的总长度
Buffer的常见类型有:Byte/Char/Short/Int/Long/Float/DoubleBuffer,还有MappedByteBuffer
ByteBuffer buffer = ByteBuffer.allocate(1024); //创建Buffer
buffer.flip(); //将position清零,以便进行读取
buffer.clear(); //将position和limit清零,以便进行写入
channel.read(buffer); //从buffer中读取数据,从position的位置读取到limit的位置
channel.write(buffer); //向buffer中写入数据,从position的位置写入到capacity的位置
3、字符集的处理:
Charset charset = Charset.forName("GBK"); //创建字符集
charset.encode(String source) //对字符串进行编码
String result = charset.decode(buffer); //对Buffer中的内容进行解码并返回String
NIO进阶:
NIO的最大优点是非阻塞IO,所谓非阻塞IO,是相对于阻塞IO来说的
对于传统的IO,当Server端调用accept()和read()方法时,线程都会被阻塞
而非阻塞IO的原理就是采用事件驱动机制,由Selector类来负责多个通道上事件的检测,并将事件分发给对应的Channel来处理
其中事件的类型为SelectionKey,其中包含了事件的状态信息,以及该事件对应绑定到的Channel
1、Selector的结构模型:
首先将Channel注册到Selector上(Channel的信息会被封装到SelectionKey类型的对象中)
当指定了这个Channel的请求来到时,该SelectionKey就会变成可用状态
这样Selector在轮询的时候,就可以把这个SelectionKey获取出来,并对其进行操作
这样做的好处是可以将多个Channel注册到同一个Selector上,而不用为每个Channel单独启动一个线程并让其阻塞
2、Selector的使用流程:
//创建并初始化Channel
SocketChannel client = SocketChannel.open(); //创建Channel
client.connect(new InetSocketAddress("localhost", port)); //使Channel连接到服务器
//创建Selector,并将Channel注册到Selector上
Selector selector = Selector.open(); //创建Selector
client.configureBlocking(false); //将Channel设置成非阻塞
SelectionKey key = channel.register(selector, SelectionKey.OP_READ); //注册Channel
//register方法的第二个参数表示Channel可以进行的操作,共有READ,WRITE,ACCEPT和CONNECT四种操作
//在将Channel注册到Selector之前,必须保证是非阻塞的,否则将抛出IllegalBlockingModeException异常
//通过Selector轮询可以进行操作的Channel,并通过SelectionKey对Channel进行操作
while(true) {
int readyCount = selector.select(1000); //在指定的时间内,返回可以进行操作的Channel的数量
if(readyCount == 0) continue; //如果没有可以进行操作的Channel,则不执行任何操作
Iterator
iter = selector.selectedKeys.iterator();
while(iter.hasNext()){
SelectionKey key = iter.next();
if (key.isReadable()) {
handleRead(selector, key);
}
}
iter.remove();
}
}
/** 处理接收到的消息 */
private void handleRead(Selector selector, SelectionKey key) throws Exception{
int length = 0;
String content = "";
ByteBuffer readBuffer = ByteBuffer.allocateDirect(10240);
SocketChannel channel = (SocketChannel) key.channel();
while ((length = channel.read(readBuffer)) > 0) {
readBuffer.flip();
while (readBuffer.hasRemaining()) {
int size = readBuffer.remaining();
byte [] contentBuffer = new byte[size];
readBuffer.get(contentBuffer);
content = new String(contentBuffer);
Log.i(TAG, "Message Received! Content: " + content);
}
readBuffer.clear();
}
//如果接收信息结束,则关闭Channel
if (length < 0) {
Log.i(TAG, "Client disconnected!");
channel.close();
}
}
3、SelectionKey的常用方法:
selectionKey.channel(); //获取其中封装的Channel对象
selectionKey.isReadable(); //判断selectionKey的注册类型是否为OP_READ
selectionKey.isWriteable(); //判断selectionKey的注册类型是否为OP_WRITE
selectionKey.isAcceptable(); //判断selectionKey的注册类型是否为OP_ACCEPT
selectionKey.isConnectable(); //判断selectionKey的注册类型是否为OP_CONNECT
Android中使用NIO的注意事项:
由于Android2.2中对IPv6的协议支持有BUG,因此在2.2中如果使用NIO,应当在类中添加以下静态代码:
static {
//解决Android2.2系统中不能启用IPV6协议的问题
java.lang.System.setProperty("java.net.preferIPv4Stack", "true");
java.lang.System.setProperty("java.net.preferIPv6Addresses", "false");
}
此外,由于Android4.0规范不允许在主线程中进行网络连接等耗时操作
因此在开发时,所有的NIO操作都应放到子线程中进行