什么是NIO:
NIO的全称是New IO,也就是新的IO,源于JDK1.4
NIO入门:
NIO的核心类是Channel和Buffer,其中:
Channel用于读写数据,Buffer用于缓存数据
1、Channel的类型与创建方法:
2、Buffer的类型与常用方法:
一个Buffer主要由position, limit, capacity三个变量来控制读写的过程
其中,position为当前指针所在的位置,limit为实际读取的数据长度,capacity为缓冲区的总长度
Buffer的常见类型有:Byte/Char/Short/Int/Long/Float/DoubleBuffer,还有MappedByteBuffer
3、字符集的处理:
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的使用流程:
3、SelectionKey的常用方法:
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();
一个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的位置
- 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<selectionkey> 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();
- }
- }
- </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操作都应放到子线程中进行