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<URL> urls) throws IOException {

        Map<SocketAddress, String> 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<SelectionKey> 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<SocketAddress, String> urlToSocketAddress(Set<URL> urls) {

        Map<SocketAddress, String> 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;
    }
}

“`

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值