• Java NIO


    Java NIO (New IO)是Java 1.4版本开始引入的新的IO API。和IO的区别在于NIO是一个异步、非阻塞的IO,可以用一个线程管理多个连接。比如1个服务器,5个客户端,如果用IO,在服务器上就需要5个线程,而使用NIO的话,只需要1个线程就可以同时管理5个连接,节约了资源。

    NIO的核心部分包括:Channel、Buffer和Selector。Channel把数据读写到Buffer,或者说通过Buffer实现数据的读写。Selector可以同时管理多个Channel,Channel注册到Selector,当Channel中有事件就绪时,Selector执行获得一个SelectionKey集合,并进行处理,从而实现了异步、非阻塞,一个线程管理多个连接。

    下面的例子,通过NIO实现了1个服务器线程管理5个客户端连接,直接上代码。

    NIOServer.java:

    package server;
    
    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 {
        
        // 端口号
        final static int startPort = 10000;
        int[] port = {startPort, startPort+1, startPort+2, startPort+3, startPort+4};
        
        // Channel编号
        int[] num = {0, 1, 2, 3, 4};
        
        // Channel发送消息的次数
        int[] times = {0, 0, 0, 0, 0};
         
        // 缓冲区大小
        private int BLOCK = 4096;  
        // 接收数据缓冲区 
        private ByteBuffer sendbuffer = ByteBuffer.allocate(BLOCK);  
        // 发送数据缓冲区 
        private ByteBuffer receivebuffer = ByteBuffer.allocate(BLOCK);  
        private Selector selector; 
        
        // 构造函数
        public NIOServer(String host) throws IOException{
            // 打开ServerSocketChannel
            ServerSocketChannel serverSocketChannel1 = ServerSocketChannel.open(); 
            ServerSocketChannel serverSocketChannel2 = ServerSocketChannel.open(); 
            ServerSocketChannel serverSocketChannel3 = ServerSocketChannel.open(); 
            ServerSocketChannel serverSocketChannel4 = ServerSocketChannel.open(); 
            ServerSocketChannel serverSocketChannel5 = ServerSocketChannel.open(); 
            // 设置为非阻塞
            serverSocketChannel1.configureBlocking(false);
            serverSocketChannel2.configureBlocking(false);
            serverSocketChannel3.configureBlocking(false);
            serverSocketChannel4.configureBlocking(false);
            serverSocketChannel5.configureBlocking(false);
            // 获得ServerSocket
            ServerSocket serverSocket1 = serverSocketChannel1.socket();
            ServerSocket serverSocket2 = serverSocketChannel2.socket();
            ServerSocket serverSocket3 = serverSocketChannel3.socket();
            ServerSocket serverSocket4 = serverSocketChannel4.socket();
            ServerSocket serverSocket5 = serverSocketChannel5.socket();
            // 绑定主机和端口
            serverSocket1.bind(new InetSocketAddress(host, port[0]));
            serverSocket2.bind(new InetSocketAddress(host, port[1]));
            serverSocket3.bind(new InetSocketAddress(host, port[2]));
            serverSocket4.bind(new InetSocketAddress(host, port[3]));
            serverSocket5.bind(new InetSocketAddress(host, port[4]));
            // 获得Selector  
            selector = Selector.open();  
            // 注册到selector,等待连接  
            serverSocketChannel1.register(selector, SelectionKey.OP_ACCEPT, num[0]);  
            serverSocketChannel2.register(selector, SelectionKey.OP_ACCEPT, num[1]);
            serverSocketChannel3.register(selector, SelectionKey.OP_ACCEPT, num[2]);
            serverSocketChannel4.register(selector, SelectionKey.OP_ACCEPT, num[3]);
            serverSocketChannel5.register(selector, SelectionKey.OP_ACCEPT, num[4]);
            // 成功
            System.out.println("Server start (Host:" + host + ", Port:" + port[0] + ") successful!"); 
            System.out.println("Server start (Host:" + host + ", Port:" + port[1] + ") successful!");
            System.out.println("Server start (Host:" + host + ", Port:" + port[2] + ") successful!");
            System.out.println("Server start (Host:" + host + ", Port:" + port[3] + ") successful!");
            System.out.println("Server start (Host:" + host + ", Port:" + port[4] + ") successful!");
        }
        
        // 监听
        public void listen() throws IOException{  
            while (true) {  
                // 选择一组键,并且相应的Channel已经打开  
                selector.select();  
                // 返回此选择器的SelectionKey集
                Set<SelectionKey> selectionKeys = selector.selectedKeys();  
                Iterator<SelectionKey> iterator = selectionKeys.iterator();  
                while (iterator.hasNext()) {          
                    SelectionKey selectionKey = iterator.next(); 
                    // 先删除该SelectionKey,再处理
                    iterator.remove();  
                    handleKey(selectionKey);  
                }  
            }  
        }  
        
        // 处理请求  
        private void handleKey(SelectionKey selectionKey) throws IOException {  
            // 接受请求  
            ServerSocketChannel server = null;  
            SocketChannel client = null;  
            String receiveText;  
            String sendText;  
            int count = 0; 
            int num = 0;
            num = (int) selectionKey.attachment();
            // 此SelectionKey是否可以Accept  
            if (selectionKey.isAcceptable()) {  
                // 获得此SelectionKey的Channel
                server = (ServerSocketChannel) selectionKey.channel(); 
                // 接受连接,返回Channel
                client = server.accept();  
                // 配置为非阻塞  
                client.configureBlocking(false);  
                // 注册到selector,等待读取 
                client.register(selector, SelectionKey.OP_READ, num);  
            } 
            // 此SelectionKey是否可以Read
            else if (selectionKey.isReadable()) {  
                // 获得此SelectionKey的Channel
                client = (SocketChannel) selectionKey.channel();  
                // 清空缓冲区
                receivebuffer.clear();  
                // 读取客户端发送来的数据到缓冲区中 
                count = client.read(receivebuffer);   
                if (count > 0) { 
                    // Byte转换成String
                    receiveText = new String(receivebuffer.array(), 0, count);  
                    System.out.println("Server Channel " + num + " received from client:" + receiveText);
                    // 注册到selector,等待写入
                    client.register(selector, SelectionKey.OP_WRITE, num);  
                }  
            } 
            // 此SelectionKey是否可以Write
            else if (selectionKey.isWritable()) {  
                // 清空缓冲区
                sendbuffer.clear();  
                // 获得此SelectionKey的Channel  
                client = (SocketChannel) selectionKey.channel();  
                int tim = ++times[num];
                sendText = "Server(Number:" + num + " Times:" + tim +")";  
                // 向缓冲区中输入数据 
                sendbuffer.put(sendText.getBytes());  
                // 读写模式切换  
                sendbuffer.flip();  
                // 输出到Channel  
                client.write(sendbuffer);   
                client.register(selector, SelectionKey.OP_READ, num);  
            }  
        }
        
        public static void main(String[] args) throws IOException {
            // 启动服务器
            NIOServer nioserver = new NIOServer("localhost");
            nioserver.listen();
        }
    
    }

    NIOClient.java:

    package client;
    
    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 extends Thread{
        
        // Client编号
        int num;
        // 端口号
        int port;
         
        // 缓冲区大小
        private  int BLOCK = 4096;  
        // 接收数据缓冲区 
        private ByteBuffer sendbuffer = ByteBuffer.allocate(BLOCK);  
        // 发送数据缓冲区 
        private ByteBuffer receivebuffer = ByteBuffer.allocate(BLOCK);  
        private Selector selector; 
        
        Set<SelectionKey> selectionKeys;  
        Iterator<SelectionKey> iterator;  
        SelectionKey selectionKey;  
        SocketChannel client;  
        String receiveText;  
        String sendText;  
        int count = 0; 
        // 发送消息的次数
        int flag = 0;
        
        public NIOClient(int num) throws IOException{
            this.num = num;
            this.port = 10000 + num;
            InetSocketAddress SERVER_ADDRESS = new InetSocketAddress("localhost", port);
            // 打开SocketChannel
            SocketChannel socketChannel = SocketChannel.open();
            // 设置为非阻塞
            socketChannel.configureBlocking(false); 
            // 获得Selector 
            selector = Selector.open(); 
            // 注册连接服务端socket动作 
            socketChannel.register(selector, SelectionKey.OP_CONNECT); 
            // 连接
            socketChannel.connect(SERVER_ADDRESS);
        }
        
        public void run(){
            while (true){
                try {
                    // 选择
                    selector.select();
                    selectionKeys = selector.selectedKeys();
                    iterator = selectionKeys.iterator();
                    while (iterator.hasNext()) {  
                        selectionKey = iterator.next();  
                        if (selectionKey.isConnectable()) {                           
                            client = (SocketChannel) selectionKey.channel();  
                            // 判断此Channel上是否正在进行连接操作
                            if (client.isConnectionPending()) { 
                                // 完成连接
                                client.finishConnect();  
                                System.out.println("Client " + num + " connected!");   
                                sendbuffer.clear();  
                                flag++;
                                String sendString = "Client " + num + " send: " + flag + "!";
                                sendbuffer.put(sendString.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("Client " + num + " received from server: " + receiveText);
                                client.register(selector, SelectionKey.OP_WRITE);  
                            }  
         
                        } 
                        else if (selectionKey.isWritable()) {  
                            sendbuffer.clear();  
                            client = (SocketChannel) selectionKey.channel();  
                            flag++;
                            String sendString = "Client " + num + " send: " + flag + "!"; 
                            sendbuffer.put(sendString.getBytes());  
                             // 将缓冲区各标志复位,因为向里面put了数据标志被改变要想从中读取数据发向服务器,就要复位  
                            sendbuffer.flip();  
                            client.write(sendbuffer);  
                            client.register(selector, SelectionKey.OP_READ);  
                        }  
                    }  
                    selectionKeys.clear();  
                } catch (IOException e) {
                    // 出错
                    e.printStackTrace();
                } 
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    // 出错
                    e.printStackTrace();
                }
            }
        }
        
        public static void main(String[] args) throws IOException {
            // 启动客户端
            NIOClient nioclient0 = new NIOClient(0);
            NIOClient nioclient1 = new NIOClient(1);
            NIOClient nioclient2 = new NIOClient(2);
            NIOClient nioclient3 = new NIOClient(3);
            NIOClient nioclient4 = new NIOClient(4);
            nioclient0.start();
            nioclient1.start();
            nioclient2.start();
            nioclient3.start();
            nioclient4.start();
        }
    }
  • 相关阅读:
    Oracle 如何循环查询结果集,进行新增或修改
    绕过CPU:英伟达与IBM致力推动GPU直连SSD以大幅提升性能
    搭建grafana图形化展示metrics数据
    同步mysql数据到ElasticSearch的最佳实践
    Nacos1.4.0启动报错解决方案
    mysql修改my.ini配置文件后无法启动问题解决办法,及修改mysql默认编码为utf8mb4的方法
    业务系统 django 应用报错 MySQL Connection not available 问题处理
    111
    2222
    就软件架构师如何工作的看法
  • 原文地址:https://www.cnblogs.com/mstk/p/6771334.html
Copyright © 2020-2023  润新知