java NIO基本操作

基本概念

selector 选择器

channel 管道

一个selector管理多个channel


buffer 用于io读写数据分配的一块内存

操作见链接

http://ifeve.com/buffers/


nio实现Echo打印功能:

绑定监听套接字,并启动接收器

package com.xx.reader.jiapeng.nio;

import java.net.InetSocketAddress;
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;

public class NioEchoServer {
    private static final int BUF_SIZE = 256;
    private static final int TIMEOUT = 3000;

    public static void main(String args[]) throws Exception {
        // 打开服务端 Socket,可以监听新进来的TCP连接的通道,可多个,可选择阻塞和非阻塞模式获取
        ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
        // 打开 Selector 唤起一个选择器
        Selector selector = Selector.open();
        // 服务端 Socket 监听8080端口, 并配置为非阻塞模式
        serverSocketChannel.socket().bind(new InetSocketAddress(7777));
        serverSocketChannel.configureBlocking(false);
        // 将 channel 注册到 selector 中.
        // 通常我们都是先注册一个 OP_ACCEPT 事件, 然后在 OP_ACCEPT 到来时, 再将这个 Channel 的 OP_READ
        // 注册到 Selector 中.
        serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
        while (true) {
            //select上的channels已经准备好IO操作
            //这是个阻塞方法,入参为超时时间,返回值为准备好channel的个数,可能是0
            // 通过调用 select 方法, 阻塞地等待 channel I/O 可操作
            if (selector.select(TIMEOUT) == 0) {
                System.out.print(".");
                continue;
            }
            // 获取 I/O 操作就绪的 SelectionKey, 通过 SelectionKey 可以知道哪些 Channel 的哪类 I/O 操作已经就绪.
            Iterator keyIterator = selector.selectedKeys().iterator();
            while (keyIterator.hasNext()) {
                SelectionKey key = keyIterator.next();
                // 当获取一个 SelectionKey 后, 就要将它删除, 表示我们已经对这个 IO 事件进行了处理.
                keyIterator.remove();
                if (key.isAcceptable()) {
                    System.out.println("1");
                    // 当 OP_ACCEPT 事件到来时, 我们就有从 ServerSocketChannel 中获取一个 SocketChannel,
                    // 代表客户端的连接
                    // 注意, 在 OP_ACCEPT 事件中, 从 key.channel() 返回的 Channel 是 ServerSocketChannel.
                    // 而在 OP_WRITE 和 OP_READ 中, 从 key.channel() 返回的是 SocketChannel.
                    SocketChannel clientChannel = ((ServerSocketChannel) key.channel()).accept();
                    clientChannel.configureBlocking(false);
                    //在 OP_ACCEPT 到来时, 再将这个 Channel 的 OP_READ 注册到 Selector 中.
                    // 注意(1)这里我们如果没有设置 OP_READ 的话, 即 interest set 仍然是 OP_CONNECT 的话, 那么 select 方法会一直直接返回.
                    //(2)这里不能注册OP_WRITE 事件,因为socket只要send buffer不满就可以写!!!
                    // 刚开始send buffer为空,那么程序会循环执行打印3的操作,而没有真正写的内容,造成CPU负载很高缺没干事
                    clientChannel.register(key.selector(), SelectionKey.OP_READ, ByteBuffer.allocate(BUF_SIZE));
                }
                if (key.isReadable()) {
                    System.out.println("2");
                    SocketChannel clientChannel = (SocketChannel) key.channel();
                    ByteBuffer buf = (ByteBuffer) key.attachment();
                    long bytesRead = clientChannel.read(buf);
                    if (bytesRead == -1) {
                        clientChannel.close();
                    } else if (bytesRead > 0) {
                        key.interestOps(SelectionKey.OP_READ | SelectionKey.OP_WRITE);
                        System.out.println("Get data length: " + bytesRead);
                    }
                }
                if (key.isValid() && key.isWritable()) {
                    System.out.println("3");
                    ByteBuffer buf = (ByteBuffer) key.attachment();
                    //The limit is set to the current position and then the position is set to zero
                    //保证读取的内容从头开始,并且读取的长度是已经写入的长度
                    buf.flip();
                    SocketChannel clientChannel = (SocketChannel) key.channel();
                    clientChannel.write(buf);
                    if (!buf.hasRemaining()) {
                        //交替注册读写操作,实现echo的功能
                        key.interestOps(SelectionKey.OP_READ);
                    }
                    //清空buffer,清理已读取长度的buffer
                    buf.compact();
                }
            }
        }
    }
}


telnet 127.0.0.1 7777



参考文章

http://marlonyao.iteye.com/blog/1005690

The Rox Java NIO Tutorial

Architecture of a Highly Scalable NIO-Based Server





你可能感兴趣的:(网络编程,java,nio)