Java NIO中的Buffer、Channel和Selector的概念和作用

转载:
http://ginobefunny.com/post/java_nio_interview_questions/
http://www.jianshu.com/p/052035037297

1、Buffer(缓冲区)

使用数组的方式不够灵活且性能差,Java NIO的缓冲区功能更加强大;容量(capacity)表示缓冲区的额定大小,需要在创建时指定(allocate静态方法);读写限制(limit)表示缓冲区在进行读写操作时的最大允许位置;读写位置(position)表示当前进行读写操作时的位置;缓冲区的很多操作(clear、flip、rewind)都是操作limit和position的值来实现重复读写;

2、Channel(通道)

channel表示为一个已经建立好的支持I/O操作的实体(如文件和网络)的连接,在此连接上进行数据的读写操作,使用的是缓冲区来实现读写。

例:

public void openAndWrite() throws IOException {
    FileChannel channel = FileChannel.open(Paths.get("my.txt"), StandardOpenOption.CREATE, StandardOpenOption.WRITE);
    ByteBuffer buffer = ByteBuffer.allocate(64);  //使用allocate函数分配缓冲区空间
    buffer.putChar('A').flip();  //flip()函数将缓冲区由写模式转变为读模式
    channel.write(buffer);  //从buffer中读数据到channel
}

3、Selector(多路复用器)

套接字通道的多路复用的思想比较简单,通过一个专门的选择器(Selector)来同时对多个套接字通道进行监听;当其中的某些套接字通道上有它感兴趣的事件发生时,这些通道就会变为可用状态,可以在选择器的选择操作中被选中;可用通道的选择一般是通过操作系统提供的底层操作系统调用来实现的,性能也比较高。

package javanio;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.net.URL;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;

/**
 * Created by Michael on 2017/7/31.
 */
public class LoadWebPageUseSelector {

    //通过Selector同时下载
    public void load(Set urls) throws IOException {

        Map mapping = urlToSocketAddress(urls);
        System.out.println(mapping);

        //创建Selector
        Selector selector = Selector.open();

        //将套接字的Channel注册到Selector上
        for(SocketAddress address : mapping.keySet()) {
            register(selector, address);
        }

        int finished = 0;
        int total = mapping.size(); //总的连接个数

        //接收数据的buffer
        ByteBuffer buffer = ByteBuffer.allocate(32 * 1024); //单位(字节)
        int len = -1;

        while(finished < total) {
            selector.select(); //会阻塞
            Iterator iterator = selector.selectedKeys().iterator();
            while(iterator.hasNext()) {
                SelectionKey key = iterator.next();
                iterator.remove();  //移除key
                if(key.isValid() && key.isConnectable()) {
                    //channel连接成功
                    SocketChannel channel = (SocketChannel) key.channel();
                    //如果连接成功
                    boolean success = channel.finishConnect();
                    if(!success) {
                        finished++;
                        key.cancel();
                    } else {
                        InetSocketAddress address = (InetSocketAddress) channel.getRemoteAddress();
                        String path = mapping.get(address); //获得地址路径
                        String request = "GET " + "/ HTTP/1.1" + "\r\n\r\n";
                        System.out.println(request);
                        ByteBuffer header = ByteBuffer.wrap(request.getBytes("UTF-8"));
                        //发送
                        channel.write(header);
                    }
                } else if(key.isValid() && key.isReadable()) {
                    //channel可读
                    SocketChannel channel = (SocketChannel) key.channel();
                    InetSocketAddress address = (InetSocketAddress) channel.getRemoteAddress();
                    String fileName = address.getHostName() + ".txt";
                    FileChannel fileChannel = FileChannel.open(Paths.get(fileName),
                            StandardOpenOption.APPEND, StandardOpenOption.CREATE);
                    buffer.clear();

                    while((len = channel.read(buffer)) > 0 || buffer.position() != 0) {
                        buffer.flip(); //转换成读模式
                        fileChannel.write(buffer);  //向文件channel写数据
                        buffer.compact();
                    }

                    if(len == -1) {
                        finished++;
                        key.cancel();
                    }
                }
            }
        }

    }


    /**
     * 将套接字注册到Selector上
     * @param selector
     * @param socketAddress
     */
    private void register(Selector selector, SocketAddress socketAddress) throws IOException {

        SocketChannel channel = SocketChannel.open();

        //设置Channel为非阻塞模式
        channel.configureBlocking(false);
        channel.connect(socketAddress);

        //将channel注册到selector上
        channel.register(selector, SelectionKey.OP_CONNECT | SelectionKey.OP_READ);
    }


    /**
     * url转SocketAddress
     * @param urls
     * @return
     */
    private Map urlToSocketAddress(Set urls) {

        Map map = new HashMap<>();
        for(URL url : urls) {
            int port;
            if(url.getPort() != -1) {
                port = url.getPort();
            } else {
                port = url.getDefaultPort();
            }

            SocketAddress address = new InetSocketAddress(url.getHost(), port);
            String path = url.getPath();
            if(url.getQuery() != null) {
                path = path + "?" + url.getQuery();
            }

            map.put(address, path);
        }

        return map;
    }
}

“`

你可能感兴趣的:(Java,NIO编程(Netty框架))