Java NIO

作者: LynnGuo | 来源:发表于2018-05-22 11:18 被阅读0次

    Java NIO 由以下几个核心部分组成:

    Channels
    Buffers
    Selectors

    虽然Java NIO 中除此之外还有很多类和组件,但在我看来,Channel,Buffer 和 Selector 构成了核心的API。其它组件,如Pipe和FileLock,只不过是与三个核心组件共同使用的工具类。因此,在概述中我将集中在这三个组件上。其它组件会在单独的章节中讲到。

    Channel 和 Buffer
    基本上,所有的 IO 在NIO 中都从一个Channel 开始。Channel 有点象流。 数据可以从Channel读到Buffer中,也可以从Buffer 写到Channel中。这里有个图示:


    overview-channels-buffers1.png

    Channel和Buffer有好几种类型。下面是JAVA NIO中的一些主要Channel的实现:

    FileChannel ---------文件
    DatagramChannel ------------UDP
    SocketChannel ---------------TCP
    ServerSocketChannel -------------TCP

    正如你所看到的,这些通道涵盖了UDP 和 TCP 网络IO,以及文件IO。

    与这些类一起的有一些有趣的接口,但为简单起见,我尽量在概述中不提到它们。本教程其它章节与它们相关的地方我会进行解释。

    以下是Java NIO里关键的Buffer实现:

    ByteBuffer
    ShortBuffer
    IntBuffer
    LongBuffer

    FloatBuffer
    DoubleBuffer

    CharBuffer

    这些Buffer覆盖了你能通过IO发送的基本数据类型:byte, short, int, long, float, double 和 char。

    Java NIO 还有个 MappedByteBuffer,用于表示内存映射文件, 我也不打算在概述中说明。

    Selector
    Selector允许单线程处理多个 Channel。如果你的应用打开了多个连接(通道),但每个连接的流量都很低,使用Selector就会很方便。例如,在一个聊天服务器中。

    这是在一个单线程中使用一个Selector处理3个Channel的图示:


    overview-selectors.png

    要使用Selector,得向Selector注册Channel,然后调用它的select()方法。这个方法会一直阻塞到某个注册的通道有事件就绪。一旦这个方法返回,线程就可以处理这些事件,事件的例子有如新连接进来,数据接收等。

    代码示例:

    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 NIOServer {  
          
        /*标识数字*/ 
        private  int flag = 0;  
        /*缓冲区大小*/ 
        private  int BLOCK = 4096;  
        /*接受数据缓冲区*/ 
        private  ByteBuffer sendbuffer = ByteBuffer.allocate(BLOCK);  
        /*发送数据缓冲区*/ 
        private  ByteBuffer receivebuffer = ByteBuffer.allocate(BLOCK);  
        private  Selector selector;  
     
        public NIOServer(int port) throws IOException {  
            // 打开服务器套接字通道  
            ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();  
            // 服务器配置为非阻塞  
            serverSocketChannel.configureBlocking(false);  
            // 检索与此通道关联的服务器套接字  
            ServerSocket serverSocket = serverSocketChannel.socket();  
            // 进行服务的绑定  
            serverSocket.bind(new InetSocketAddress(port));  
            // 通过open()方法找到Selector  
            selector = Selector.open();  
            // 注册到selector,等待连接  
            serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);  
            System.out.println("Server Start----8888:");  
        }  
     
     
        // 监听  
        private void listen() throws IOException {  
            while (true) {  
                // 选择一组键,并且相应的通道已经打开  
                selector.select();  
                // 返回此选择器的已选择键集。  
                Set<SelectionKey> selectionKeys = selector.selectedKeys();  
                Iterator<SelectionKey> iterator = selectionKeys.iterator();  
                while (iterator.hasNext()) {          
                    SelectionKey selectionKey = iterator.next();  
                    iterator.remove();  
                    handleKey(selectionKey);  
                }  
            }  
        }  
     
        // 处理请求  
        private void handleKey(SelectionKey selectionKey) throws IOException {  
            // 接受请求  
            ServerSocketChannel server = null;  
            SocketChannel client = null;  
            String receiveText;  
            String sendText;  
            int count=0;  
            // 测试此键的通道是否已准备好接受新的套接字连接。  
            if (selectionKey.isAcceptable()) {  
                // 返回为之创建此键的通道。  
                server = (ServerSocketChannel) selectionKey.channel();  
                // 接受到此通道套接字的连接。  
                // 此方法返回的套接字通道(如果有)将处于阻塞模式。  
                client = server.accept();  
                // 配置为非阻塞  
                client.configureBlocking(false);  
                // 注册到selector,等待连接  
                client.register(selector, SelectionKey.OP_READ);  
            } else if (selectionKey.isReadable()) {  
                // 返回为之创建此键的通道。  
                client = (SocketChannel) selectionKey.channel();  
                //将缓冲区清空以备下次读取  
                receivebuffer.clear();  
                //读取服务器发送来的数据到缓冲区中  
                count = client.read(receivebuffer);   
                if (count > 0) {  
                    receiveText = new String( receivebuffer.array(),0,count);  
                    System.out.println("服务器端接受客户端数据--:"+receiveText);  
                    client.register(selector, SelectionKey.OP_WRITE);  
                }  
            } else if (selectionKey.isWritable()) {  
                //将缓冲区清空以备下次写入  
                sendbuffer.clear();  
                // 返回为之创建此键的通道。  
                client = (SocketChannel) selectionKey.channel();  
                sendText="message from server--" + flag++;  
                //向缓冲区中输入数据  
                sendbuffer.put(sendText.getBytes());  
                 //将缓冲区各标志复位,因为向里面put了数据标志被改变要想从中读取数据发向服务器,就要复位  
                sendbuffer.flip();  
                //输出到通道  
                client.write(sendbuffer);  
                System.out.println("服务器端向客户端发送数据--:"+sendText);  
                client.register(selector, SelectionKey.OP_READ);  
            }  
        }  
     
        /**  
         * @param args  
         * @throws IOException  
         */ 
        public static void main(String[] args) throws IOException {  
            // TODO Auto-generated method stub  
            int port = 8888;  
            NIOServer server = new NIOServer(port);  
            server.listen();  
        }  
    } 
    
    import java.io.IOException;  
    import java.net.InetSocketAddress;  
    import java.nio.ByteBuffer;  
    import java.nio.channels.SelectionKey;  
    import java.nio.channels.Selector;  
    import java.nio.channels.SocketChannel;  
    import java.util.Iterator;  
    import java.util.Set;  
     
    public class NIOClient {  
     
        /*标识数字*/ 
        private static int flag = 0;  
        /*缓冲区大小*/ 
        private static int BLOCK = 4096;  
        /*接受数据缓冲区*/ 
        private static ByteBuffer sendbuffer = ByteBuffer.allocate(BLOCK);  
        /*发送数据缓冲区*/ 
        private static ByteBuffer receivebuffer = ByteBuffer.allocate(BLOCK);  
        /*服务器端地址*/ 
        private final static InetSocketAddress SERVER_ADDRESS = new InetSocketAddress(  
                "localhost", 8888);  
     
        public static void main(String[] args) throws IOException {  
            // TODO Auto-generated method stub  
            // 打开socket通道  
            SocketChannel socketChannel = SocketChannel.open();  
            // 设置为非阻塞方式  
            socketChannel.configureBlocking(false);  
            // 打开选择器  
            Selector selector = Selector.open();  
            // 注册连接服务端socket动作  
            socketChannel.register(selector, SelectionKey.OP_CONNECT);  
            // 连接  
            socketChannel.connect(SERVER_ADDRESS);  
            // 分配缓冲区大小内存  
              
            Set<SelectionKey> selectionKeys;  
            Iterator<SelectionKey> iterator;  
            SelectionKey selectionKey;  
            SocketChannel client;  
            String receiveText;  
            String sendText;  
            int count=0;  
     
            while (true) {  
                //选择一组键,其相应的通道已为 I/O 操作准备就绪。  
                //此方法执行处于阻塞模式的选择操作。  
                selector.select();  
                //返回此选择器的已选择键集。  
                selectionKeys = selector.selectedKeys();  
                //System.out.println(selectionKeys.size());  
                iterator = selectionKeys.iterator();  
                while (iterator.hasNext()) {  
                    selectionKey = iterator.next();  
                    if (selectionKey.isConnectable()) {  
                        System.out.println("client connect");  
                        client = (SocketChannel) selectionKey.channel();  
                        // 判断此通道上是否正在进行连接操作。  
                        // 完成套接字通道的连接过程。  
                        if (client.isConnectionPending()) {  
                            client.finishConnect();  
                            System.out.println("完成连接!");  
                            sendbuffer.clear();  
                            sendbuffer.put("Hello,Server".getBytes());  
                            sendbuffer.flip();  
                            client.write(sendbuffer);  
                        }  
                        client.register(selector, SelectionKey.OP_READ);  
                    } else if (selectionKey.isReadable()) {  
                        client = (SocketChannel) selectionKey.channel();  
                        //将缓冲区清空以备下次读取  
                        receivebuffer.clear();  
                        //读取服务器发送来的数据到缓冲区中  
                        count=client.read(receivebuffer);  
                        if(count>0){  
                            receiveText = new String( receivebuffer.array(),0,count);  
                            System.out.println("客户端接受服务器端数据--:"+receiveText);  
                            client.register(selector, SelectionKey.OP_WRITE);  
                        }  
     
                    } else if (selectionKey.isWritable()) {  
                        sendbuffer.clear();  
                        client = (SocketChannel) selectionKey.channel();  
                        sendText = "message from client--" + (flag++);  
                        sendbuffer.put(sendText.getBytes());  
                         //将缓冲区各标志复位,因为向里面put了数据标志被改变要想从中读取数据发向服务器,就要复位  
                        sendbuffer.flip();  
                        client.write(sendbuffer);  
                        System.out.println("客户端向服务器端发送数据--:"+sendText);  
                        client.register(selector, SelectionKey.OP_READ);  
                    }  
                }  
                selectionKeys.clear();  
            }  
        }  
    } 
    

    相关文章

      网友评论

          本文标题:Java NIO

          本文链接:https://www.haomeiwen.com/subject/yenddftx.html