NIO中的缓冲区

本最近在学习Java的NIO知识,缓冲区的概念在其中十分的重要,也差了很多资料,看到一篇写的很好,大致记录下

关于NIO缓冲区,有三个重要的状态和两个重要的方法

  • position
  • limit
  • capacity

  • flip()
  • clear()

position

position指定了当前存储的位置,就像一个量杯装水,position就是指现在量杯的水位线。指的是下次要装就从这个地方开始。

limit

有两种情况
1,当写入数据到缓冲区的时候,表示还有多少空间可以放入数据
2,当从缓冲区读取数据时,表示有多少数据要被取出

capacity

指的是缓冲区的容量,就是量杯的容量的意思


flip();

当数据在写入缓冲区的时候,就像往量杯中导入水,这个时候position会随着水的倒入逐渐的增加。这个时候limit的值减少,position增加。
flip()方法用于把缓冲区里面的数据写出去的前面,执行方法后,limit变为position,而position变为0。这时候开始读数据就可以从最开始读,当读到limit的时候则证明缓冲区里的数据已经读取完毕

clear();

clear()方法没什么好说的,就是清除缓冲区的数据,这个时候position为0,而limit则是capacity的值。这样这个缓冲区就可以重新继续装数据了。

Selector 选择器

在网络编程中使用NIO,可以同时注册多个Channel在同一个Selector上这样就可以实现单个线程同时监控多个网络连接。

原本如果是普通IO,使用一个线程去服务一个连接,这样会导致系统开销过大。
而NIO的Selector模式,采用的是让多个连接同一管理,并且把每一个连接的操作看做是一个事件,存放在一个队列中(例如,A线程读,B线程写,C线程读),然后在按照顺序去执行者几个任务。

package cn.nio;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import java.util.Set;

public class MultiPortEcho {
    private int ports[];
    // 缓冲区
    private ByteBuffer echoBuffer = ByteBuffer.allocate(1024);

    public MultiPortEcho(int ports[]) throws IOException {
        this.ports = ports;
        go();
    }

    private void go() throws IOException {
        // 1. 创建一个selector,select是NIO中的核心对象
        // 它用来监听各种感兴趣的IO事件
        Selector selector = Selector.open();
        // 为每个端口打开一个监听, 并把这些监听注册到selector中
        for (int i = 0; i < ports.length; ++i) {
            // 2. 打开一个ServerSocketChannel
            // 其实我们每监听一个端口就需要一个channel
            ServerSocketChannel ssc = ServerSocketChannel.open();
            // 设置为非阻塞
            ssc.configureBlocking(false);
            ServerSocket ss = ssc.socket();
            InetSocketAddress address = new InetSocketAddress(ports[i]);
            ss.bind(address);// 监听一个端口
            // 3. 注册到selector
            // register的第一个参数永远都是selector
            // 第二个参数是我们要监听的事件
            // OP_ACCEPT是新建立连接的事件
            // 也是适用于ServerSocketChannel的唯一事件类型
            SelectionKey key = ssc.register(selector, SelectionKey.OP_ACCEPT);
            System.out.println("Going to listen on " + ports[i]);
        }
        // 4. 开始循环,我们已经注册了一些IO兴趣事件
        while (true) {
            // 这个方法会(阻塞),直到至少有一个已注册的事件发生。当一个或者更多的事件发生时
            // select() 方法将返回所发生的事件的数量。
            int num = selector.select();
            // 返回发生了事件的 SelectionKey 对象的一个 集合
            Set<SelectionKey> selectedKeys = selector.selectedKeys();
            // 我们通过迭代 SelectionKeys 并依次处理每个 SelectionKey 来处理事件
            // 对于每一个 SelectionKey,您必须确定发生的是什么 I/O 事件,以及这个事件影响哪些 I/O 对象。
            Iterator<SelectionKey> it = selectedKeys.iterator();
            while (it.hasNext()) {
                SelectionKey key = it.next();
                // 5. 监听新连接。程序执行到这里,我们仅注册了 ServerSocketChannel
                // 并且仅注册它们“接收”事件。为确认这一点
                // 我们对 SelectionKey 调用 readyOps() 方法,并检查发生了什么类型的事件
                if ((key.readyOps() & SelectionKey.OP_ACCEPT) == SelectionKey.OP_ACCEPT) {
                    // 6. 接收了一个新连接。因为我们知道这个服务器套接字上有一个传入连接在等待
                    // 所以可以安全地接受它;也就是说,不用担心 accept() 操作会阻塞
                    ServerSocketChannel ssc = (ServerSocketChannel) key
                            .channel();
                    SocketChannel sc = ssc.accept();
                    sc.configureBlocking(false);
                    // 7. 讲新连接注册到selector。将新连接的 SocketChannel 配置为非阻塞的
                    // 而且由于接受这个连接的目的是为了读取来自套接字的数据,所以我们还必须将 SocketChannel 注册到
                    // Selector上
                    SelectionKey newKey = sc.register(selector,
                            SelectionKey.OP_READ);
                    it.remove();
                    System.out.println("Got connection from " + sc);
                } else if ((key.readyOps() & SelectionKey.OP_READ) == SelectionKey.OP_READ) {
                    // Read the data
                    SocketChannel sc = (SocketChannel) key.channel();
                    // Echo data
                    int bytesEchoed = 0;
                    while (true) {
                        echoBuffer.clear();
                        int r = sc.read(echoBuffer);
                        if (r <= 0) {
                            break;
                        }
                        echoBuffer.flip();
                        sc.write(echoBuffer);
                        bytesEchoed += r;
                    }
                    String msg = new String(echoBuffer.array());
                    System.out.println("Echoed " + bytesEchoed + " from " + sc);
                    System.out.println("Msg " + msg);

                    // 写入
                    writeBack(msg, sc);

                    it.remove();
                } else if ((key.readyOps() & SelectionKey.OP_WRITE) == SelectionKey.OP_WRITE) {
                    // 把数据写出去
                }
            }
            // System.out.println( "going to clear" );
            // selectedKeys.clear();
            // System.out.println( "cleared" );
        }
    }

    private void writeBack(String msg, SocketChannel sc) throws IOException {
        ByteBuffer buffer = ByteBuffer.allocate(1024);
        String sendBackMsg = "receive";
        byte[] message = sendBackMsg.getBytes();
        for (int i = 0; i < message.length; ++i) {
            buffer.put(message[i]);
        }
        buffer.flip();
        // 第三步:把缓冲区数据写入通道中
        sc.write(buffer);
    }

    static public void main(String args2[]) throws Exception {
        String args[] = { "9001", "9002", "9003" };
        if (args.length <= 0) {
            System.err
                    .println("Usage: java MultiPortEcho port [port port ...]");
            System.exit(1);
        }
        int ports[] = new int[args.length];
        for (int i = 0; i < args.length; ++i) {
            ports[i] = Integer.parseInt(args[i]);
        }
        new MultiPortEcho(ports);
    }
}

参考资料:http://blog.csdn.net/suifeng3051/article/details/48441629

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值