• NIO 的基本使用


    yls 2020/5/23

    NIO中buffer的使用

    /**
     * 测试 nio 中 buffer的使用
     * buffer 内部就是一个数组 : final int[] hb;  、
     *
     * buffer 定义了 position,capacity,limit,mark四个属性来标记buffer中的数据信息
     * 可以通过debug的方式跟踪查看值的变化
     *     private int mark = -1;    标记
     *     private int position = 0;  下一个要被读或写的元素的索引,每次读写都会改变其值,为下次读写做准备
     *     private int limit;   表示缓冲区的当前终点,不能对超过limit限制的缓冲区别进行读写,limit可以修改
     *     private int capacity;   容量,创建buffer时设置,不能被改变
     *
     * buffer  读写切换时需要执行 flip()方法
     *      public final Buffer flip() {
     *         limit = position;
     *         position = 0;
     *         mark = -1;
     *         return this;
     *     }
     * buffer清除数据时调用 clear()方法,只改变标记的指向位置,不真正删除底层数组的值
     *     public final Buffer clear() {
     *         position = 0;
     *         limit = capacity;
     *         mark = -1;
     *         return this;
     *     }
     */
    public class BasicBuffer {
        public static void main(String[] args) {
            final IntBuffer intBuffer = IntBuffer.allocate(5);
          for (int i = 0; i <intBuffer.capacity() ; i++) {
                intBuffer.put(i*2);
            }
            intBuffer.flip();
            for (int i = 0; i < intBuffer.capacity(); i++) {
                System.out.println(intBuffer.get());
            }
        }
    }
    

    NIO中Socket通信实例

    1.服务端
    /**
     * 同步 非阻塞
     * FileChannel.transferTo实现了零拷贝,效率高,三十在windows中一次只能传8M,所以大文件需要断点续传
     */
    public class NioServer {
        public static void main(String[] args) throws IOException {
            //创建一个ServerSocketChannel
            final ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
            //绑定一个端口,在服务端监听
            serverSocketChannel.socket().bind(new InetSocketAddress(7777));
            //设置为非阻塞
            serverSocketChannel.configureBlocking(false);
            //得到一个selector对象
            Selector selector = Selector.open();
            //serverSocketChannel注册到selector,关心 事件 OP_ACCEPT
            serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
    
            //循环等待客户端连接
            while (true) {
                //等待一秒,若没有事件发生,返回
                if (selector.select(1000) == 0) {
                    System.out.println("服务器等待了1秒,没有请求连接。。。。");
                    continue;
                }
                //若返回的值>0,说明已经获取到相关的事件,则获取到相关的selectionKeys集合
                Set<SelectionKey> selectionKeys = selector.selectedKeys();
                //使用迭代器遍历
                Iterator<SelectionKey> iterator = selectionKeys.iterator();
                while (iterator.hasNext()) {
                    //获取到相应的key
                    SelectionKey key = iterator.next();
                    //根据key对应的通道发生的事件做出处理
                    if (key.isAcceptable()) {//如果是 isAcceptable,有新的连接
                        //这里的key对应的channel一定是serverSocketChannel
                        //为该客户端生成一个socketChannel
                        SocketChannel socketChannel = serverSocketChannel.accept();
                        //将新生成的socketChannel设置为非阻塞模式,否则会抛出异常
                        socketChannel.configureBlocking(false);
                        //将socketChannel注册到selector,关心事件为OP_READ,并关联一个buffer
                        socketChannel.register(selector, SelectionKey.OP_READ, ByteBuffer.allocate(128));
                    }
                    if (key.isReadable()) { //发生 isReadable 事件,表示有新数据发送过来
                        //根据key反向获取到相应的channel
                        SocketChannel channel = (SocketChannel) key.channel();
                        //获取到channel关联的buffer
                        ByteBuffer byteBuffer = (ByteBuffer) key.attachment();
                        //先将buffer置于初始状态
                        byteBuffer.clear();
                        //将channel中的数据读到buffer中
                        channel.read(byteBuffer);
                        //buffer读写切换
                        byteBuffer.flip();
                        //从buffer中读有效数据到bytes
                        //byteBuffer.array()直接返回buffer底层数组,如果后面发送的数据比之前发送的少,会将之前获取的值获取出来
                        byte[] bytes = new byte[byteBuffer.limit()];
                        byteBuffer.get(bytes);
                        System.out.println("from 客户端: " + new String(bytes));
                    }
    
                    //手动从集合中移除当前的key,防止重复操作
                    iterator.remove();
                }
            }
        }
    }
    
    2.客户端
    public class NioClient {
        public static void main(String[] args) throws IOException {
            //获取一个 socketChannel
            final SocketChannel socketChannel = SocketChannel.open();
            //设置 socketChannel 非阻塞
            socketChannel.configureBlocking(false);
            //提供服务端的ip和端口,连接服务端,不阻塞
            //通过 socketChannel.finishConnect() 判断是否连接成功
            boolean connect = socketChannel.connect(new InetSocketAddress("127.0.0.1", 6999));
            if (!connect) {
                while (!socketChannel.finishConnect()) {
                    System.out.println("因为连接需要时间,客户端不会阻塞,可以做其它事情。。。");
                }
            }
            //连接成功后。。。。
            System.out.println("1...");
            String s = "大忽忽";
            // Wraps a byte array into a buffer
            ByteBuffer byteBuffer = ByteBuffer.wrap(s.getBytes());
            //发送数据
            final int write = socketChannel.write(byteBuffer);
            System.out.println("2....");
            System.in.read();
        }
    }
    
    

    NIO中Scattering、Gathering的使用

    1.服务端
    /**
     * scattering: 分散,把数据写入buffer时,可以采用buffer数组,依次写
     * gathering:  聚合,从buffer中读取数据时,可以采用buffer数组,依次读
     */
    public class ScatteringAndGathering {
        public static void main(String[] args) throws IOException {
    
            //绑定端口到socket,并启动
            final ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
            serverSocketChannel.socket().bind(new InetSocketAddress(7000));
            //创建buffer数组
            final ByteBuffer[] byteBuffers = new ByteBuffer[2];
            byteBuffers[0] = ByteBuffer.allocate(4);
            byteBuffers[1] = ByteBuffer.allocate(4);
            //等待客户端连接,连接成功后生成SocketChannel
            final SocketChannel socketChannel = serverSocketChannel.accept();
            //循环读取
            while (true) {
                //从channel读取数据到buffer数组
                long read = socketChannel.read(byteBuffers);
    
                System.out.println("read=========" + read);
                if (read == 0 || read == -1) {
                    break;
                }
                Arrays.asList(byteBuffers).forEach(buffer -> {
                    System.out.println("position=" + buffer.position() + ", limit=" + buffer.limit());
                });
    
                //读写切换
                Arrays.asList(byteBuffers).forEach(buffer -> {
                    buffer.flip();
                });
    
                //将buffer数组中的数据写入channel,显示到客户端
                final long write = socketChannel.write(byteBuffers);
    
                //将每个buffer置于初始状态
                Arrays.asList(byteBuffers).forEach(buffer -> {
                    buffer.clear();
                });
            }
    
    
        }
    }
    
    2.客户端使用上边 Socket通信实例 中的就可以(改一下端口号)

    NIO 使用 Socket通信实现一个群发系统

    1.服务端
    import java.io.IOException;
    import java.net.InetSocketAddress;
    import java.nio.ByteBuffer;
    import java.nio.channels.*;
    import java.util.Iterator;
    import java.util.Set;
    
    /**
     * 简单的群聊系统(NIO 实现)
     * 服务端:可以监听客户端的上线和离线,可以接收客户端发送的数据并转发到其它客户端
     * 客户端:可以不阻塞的发送数据和接收其它客户端发送的数据
     */
    public class GroupChatServer {
        private ServerSocketChannel serverSocketChannel;
        private Selector selector;
        private static final int port = 7999;
    
        public GroupChatServer() throws IOException {
            serverSocketChannel = ServerSocketChannel.open();
            serverSocketChannel.configureBlocking(false);
            serverSocketChannel.socket().bind(new InetSocketAddress(port));
            selector = Selector.open();
            serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
        }
    
        //监听
        public void listen() {
            try {
                while (true) {
                    final int select = selector.select(2000);
                    if (select > 0) {//有事件发生
                        Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
                        while (iterator.hasNext()) {
                            //获取key
                            SelectionKey key = iterator.next();
                            //根据key对应的通道发生的事件做出处理
                            if (key.isAcceptable()) {//如果是 isAcceptable,有新的连接
                                //这里的key对应的channel一定是serverSocketChannel
                                //为该客户端生成一个socketChannel
    //                            SocketChannel socketChannel = serverSocketChannel.accept();
                                SocketChannel socketChannel = ((ServerSocketChannel) key.channel()).accept();
                                //将新生成的socketChannel设置为非阻塞模式,否则会抛出异常
                                socketChannel.configureBlocking(false);
                                //将socketChannel注册到selector,关心事件为OP_READ,并关联一个buffer
                                socketChannel.register(selector, SelectionKey.OP_READ);
                                //提示
                                System.out.println(socketChannel.getRemoteAddress() + ",上线了");
                            }
                            if (key.isReadable()) { //发生 isReadable 事件,表示有新数据发送过来
                                //专门写方法,处理读操作
                                readData(key);
                            }
    
                            //手动从集合中移除当前的key,防止重复操作
                            iterator.remove();
                        }
                    } else {
                        System.out.println("等待。。。");
                    }
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    
        private void readData(SelectionKey key) {
    
            SocketChannel channel = null;
            try {
                //根据key反向获取到相应的channel
                channel = (SocketChannel) key.channel();
                ByteBuffer byteBuffer = ByteBuffer.allocate(128);
                //将channel中的数据读到buffer中
                final int read = channel.read(byteBuffer);
                String msg = new String(byteBuffer.array(), 0, read);
                System.out.println("from 客户端: " + msg);
                //转发消息到其它客户端(除了自己),专门写一个方法
                sendMsgToOthers(msg, channel);
    
            } catch (Exception e) {
                e.printStackTrace();
                try {
                    System.out.println(channel.getRemoteAddress() + "离线了。。");
                    //关闭通道
                    channel.close();
                } catch (IOException ex) {
                    ex.printStackTrace();
                }
    
            }
        }
    
        private void sendMsgToOthers(String msg, SocketChannel self) {
    
            final Set<SelectionKey> keys = selector.keys();
            keys.forEach((key) -> {
                 SelectableChannel channel = key.channel();
                if (channel instanceof SocketChannel && channel != self) {
                    SocketChannel socketChannel=(SocketChannel)channel;
                    final ByteBuffer buffer = ByteBuffer.wrap(msg.getBytes());
                    try {
                        socketChannel.write(buffer);
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            });
        }
    
        public static void main(String[] args) throws IOException {
            final GroupChatServer groupChatServer = new GroupChatServer();
            groupChatServer.listen();
        }
    }
    
    2.客户端
    public class GroupChatClient {
        private String ip = "127.0.0.1";
        private int port = 7999;
        private Selector selector;
        private SocketChannel socketChannel;
        private String name;
    
        public GroupChatClient() throws IOException {
            socketChannel = SocketChannel.open(new InetSocketAddress(ip, port));
            socketChannel.configureBlocking(false);
            selector = Selector.open();
            socketChannel.register(selector, SelectionKey.OP_READ);
            name = socketChannel.getLocalAddress().toString().substring(1);
            System.out.println(name + " is start ..");
        }
    
        //发送消息
        public void sendData(String msg) throws IOException {
            msg = name + " 说:" + msg;
            socketChannel.write(ByteBuffer.wrap(msg.getBytes()));
        }
    
        //接收消息
        public void rec() {
            try {
                while (true) {
                    final int select = selector.select();
                    if (select > 0) {
                        final Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
                        while (iterator.hasNext()) {
                            final SelectionKey next = iterator.next();
                            if (next.isReadable()) {
                                SocketChannel channel = (SocketChannel) next.channel();
                                final ByteBuffer allocate = ByteBuffer.allocate(128);
                                final int read = channel.read(allocate);
                                System.out.println(new String(allocate.array(), 0, read));
                            }
                            iterator.remove();
                        }
                    }
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    
        public static void main(String[] args) throws IOException {
            final GroupChatClient groupChatClient = new GroupChatClient();
            //一个线程专门接收数据
            new Thread(() -> {
                groupChatClient.rec();
            }, "接收数据线程").start();
            //主线程用来发送数据
            final Scanner scanner = new Scanner(System.in);
            while (scanner.hasNext()) {
                final String s = scanner.nextLine();
                groupChatClient.sendData(s);
            }
        }
    }
    
  • 相关阅读:
    第一天站立会议
    Sprint会议计划
    软件需求分析
    团队介绍
    再写行转列和列转行
    二十三种设计模式之:组合(Composite)模式(部分·整体模式)
    对于类和对象的认识
    对排序的认识
    设计模式分类
    二十三种设计模式之:适配器模式
  • 原文地址:https://www.cnblogs.com/yloved/p/12940606.html
Copyright © 2020-2023  润新知