• nio入门教程


    1、通过拿NIO和传统IO做对比来了解NIO

       面向流与面向缓冲
      Java NIO和IO之间第一个最大的区别是,IO是面向流的,NIO是面向缓冲区的。 Java IO面向流意味着每次从流中读一个或多个字节,直至读取所有字节,它们没有被缓存在任何地方。此外,它不能前后移动流中的数据。如果需要前后移动从流中读取的数据,需要先将它缓存到一个缓冲区。 Java NIO的缓冲导向方法略有不同。数据读取到一个它稍后处理的缓冲区,需要时可在缓冲区中前后移动。这就增加了处理过程中的灵活性。但是,还需要检查是否该缓冲区中包含所有您需要处理的数据。而且,需确保当更多的数据读入缓冲区时,不要覆盖缓冲区里尚未处理的数据。

      阻塞与非阻塞IO
      Java IO的各种流是阻塞的。这意味着,当一个线程调用read() 或 write()时,该线程被阻塞,直到有一些数据被读取,或数据完全写入。该线程在此期间不能再干任何事情了。 Java NIO的非阻塞模式,使一个线程从某通道发送请求读取数据,但是它仅能得到目前可用的数据,如果目前没有数据可用时,就什么都不会获取。而不是保持线程阻塞,所以直至数据变的可以读取之前,该线程可以继续做其他的事情。 非阻塞写也是如此。一个线程请求写入一些数据到某通道,但不需要等待它完全写入,这个线程同时可以去做别的事情。 线程通常将非阻塞IO的空闲时间用于在其它通道上执行IO操作,所以一个单独的线程现在可以管理多个输入和输出通道(channel)。

      通道管理器(Selectors)
      Java NIO的通道管理器允许一个单独的线程来监视多个输入通道,你可以注册多个通道使用一个通道管理器,然后使用一个单独的线程来“选择”通道:这些通道里已经有可以处理的输入,或者选择已准备写入的通道。这种选择机制,使得一个单独的线程很容易来管理多个通道。
    2、demo

    2.1 NioServer.java

    package server;
    
    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.ServerSocketChannel;
    import java.nio.channels.SocketChannel;
    import java.util.Iterator;
    
    /**
     * Created by Administrator on 2017/3/13 0013.
     */
    public class NioServer {
        /**
         * 通道管理器
         */
        private Selector selector;
    
        /**
         * 获得一个ServerSocket通道,并对该通道做一些初始化的工作
         *
         * @param port 绑定的端口号
         * @throws IOException
         */
        public  void initServer(int port) throws IOException {
                  /* 获得一个ServerSocket通道 */
            ServerSocketChannel serverChannel = ServerSocketChannel.open();
            // 设置通道为非阻塞
            serverChannel.configureBlocking(false);
            // 将该通道对应的ServerSocket绑定到port端口
            serverChannel.socket().bind(new InetSocketAddress(port));
            // 获得一个通道管理器
            this.selector = Selector.open();
            //将通道管理器和该通道绑定,并为该通道注册SelectionKey.OP_ACCEPT事件,注册该事件后,
            //当该事件到达时,selector.select()会返回,如果该事件没到达selector.select()会一直阻塞。
            serverChannel.register(selector, SelectionKey.OP_ACCEPT);
        }
    
        /**
         * 采用轮询的方式监听selector上是否有需要处理的事件,如果有,则进行处理
         *
         * @throws IOException
         */
        @SuppressWarnings("unchecked")
        public  void listen() throws IOException {
            System.out.println("服务端启动成功!");
            // 轮询访问selector
            while (true) {
                //当注册的事件到达时,方法返回;否则,该方法会一直阻塞
                selector.select();
                // 获得selector中选中的项的迭代器,选中的项为注册的事件
                Iterator ite = this.selector.selectedKeys().iterator();
                while (ite.hasNext()) {
                    SelectionKey key = (SelectionKey) ite.next();
                    // 删除已选的key,以防重复处理
                    ite.remove();
                    // 客户端请求连接事件
                    if (key.isAcceptable()) {
                        ServerSocketChannel server = (ServerSocketChannel) key
                                .channel();
                        // 获得和客户端连接的通道
                        SocketChannel channel = server.accept();
                        // 设置成非阻塞
                        channel.configureBlocking(false);
                        //在这里可以给客户端发送信息哦
                        channel.write(ByteBuffer.wrap(new String("向客户端发送了一条信息").getBytes()));
                        //在和客户端连接成功之后,为了可以接收到客户端的信息,需要给通道设置读的权限。
                        channel.register(this.selector, SelectionKey.OP_READ);
                        // 获得了可读的事件
                    } else if (key.isReadable()) {
                        read(key);
                    }
    
                }
    
            }
        }
        /**
              * 处理读取客户端发来的信息 的事件
             * @param key
         .     * @throws IOException
              */
           public  void read(SelectionKey key) throws IOException{
                // 服务器可读取消息:得到事件发生的Socket通道
                 SocketChannel channel = (SocketChannel) key.channel();
                 // 创建读取的缓冲区
                 ByteBuffer buffer = ByteBuffer.allocate(10);
                  channel.read(buffer);
                   byte[] data = buffer.array();
                  String msg = new String(data).trim();
                  System.out.println("服务端收到信息:"+msg);
                   ByteBuffer outBuffer = ByteBuffer.wrap(msg.getBytes());
                   channel.write(outBuffer);// 将消息回送给客户端
                }
    
            public static void main(String[] args) {
                NioServer server = new NioServer();
                try {
                     server.initServer(8000);
                     server.listen();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
    }

    2.2、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;
    
    /**
     * Created by Administrator on 2017/3/13 0013.
     */
    public class NioClient {
        //通道管理器
        private Selector selector;
    
        /**
         * 获得一个Socket通道,并对该通道做一些初始化的工作
         *
         * @param ip   连接的服务器的ip
         * @param port 连接的服务器的端口号
         * @throws IOException
         */
        public void initClient(String ip, int port) throws IOException {
            // 获得一个Socket通道
            SocketChannel channel = SocketChannel.open();
            // 设置通道为非阻塞
            channel.configureBlocking(false);
            // 获得一个通道管理器
            this.selector = Selector.open();
    
            // 客户端连接服务器,其实方法执行并没有实现连接,需要在listen()方法中调
            //用channel.finishConnect();才能完成连接
            channel.connect(new InetSocketAddress(ip, port));
            //将通道管理器和该通道绑定,并为该通道注册SelectionKey.OP_CONNECT事件。
            channel.register(selector, SelectionKey.OP_CONNECT);
        }
    
        /**
         * 采用轮询的方式监听selector上是否有需要处理的事件,如果有,则进行处理
         *
         * @throws IOException
         */
        @SuppressWarnings("unchecked")
        public void listen() throws IOException {
            // 轮询访问selector
            while (true) {
                selector.select();
                // 获得selector中选中的项的迭代器
                Iterator ite = this.selector.selectedKeys().iterator();
                while (ite.hasNext()) {
                    SelectionKey key = (SelectionKey) ite.next();
                    // 删除已选的key,以防重复处理
                    ite.remove();
                    // 连接事件发生
                    if (key.isConnectable()) {
                        SocketChannel channel = (SocketChannel) key
                                .channel();
                        // 如果正在连接,则完成连接
                        if (channel.isConnectionPending()) {
                            channel.finishConnect();
    
                        }
                        // 设置成非阻塞
                        channel.configureBlocking(false);
    
                        //在这里可以给服务端发送信息哦
                        channel.write(ByteBuffer.wrap(new String("向服务端发送了一条信息").getBytes()));
                        //在和服务端连接成功之后,为了可以接收到服务端的信息,需要给通道设置读的权限。
                        channel.register(this.selector, SelectionKey.OP_READ);
    
                        // 获得了可读的事件
                    } else if (key.isReadable()) {
                        read(key);
                    }
    
                }
    
            }
        }
    
        /**
         * 处理读取服务端发来的信息 的事件
         *
         * @param key
         * @throws IOException
         */
        public void read(SelectionKey key) throws IOException {
            //和服务端的read方法一样
        }
    
    
        /**
         * 启动客户端测试
         *
         * @throws IOException
         */
        public static void main(String[] args) throws IOException {
            NioClient client = new NioClient();
            client.initClient("localhost", 8000);
            client.listen();
        }
    
    
    }

    3、测试结果

    测试成功,这还有一个nio,半包和粘包的问题,教程二解决

      

  • 相关阅读:
    如何使用Javascript调用.NET 2.0用户控件中的方法和属性
    “应用”按钮,一个让人比较郁闷的设计
    用VS2003的宏统计项目中文件和程序行数
    WEB下引用.NET Windows Control(Windows控件)经常出现的错误和解决办法
    强制填写XML注释
    在VS2003中直接用DREAMWEAVER8打开ASPX文件
    从MS的源代码中分析出来了我的Bug
    为什么中国人勤劳而不富有?
    用友U8 “科目(xxxxxx)正在被机器(xxxx)上的用户(xxx)进行(xxxx)操作锁定,请稍候再试” 的解决
    又发现了个VS2005的小改进
  • 原文地址:https://www.cnblogs.com/520playboy/p/6545385.html
Copyright © 2020-2023  润新知