NIO选择器selector的使用详解---实现文件传输功能

什么是选择器Selector

选择器是NIO技术的核心组件(缓冲区Buffer、通道Channel、选择器Selector)之一。选择器的主要作用是使用1个线程来对多个通道中的已经就绪通道进行选择,选择出就绪的通道后,就可以对通道中的数据进行处理。选择器核心类包括Selector、SelectionKey、SelectableChannel。

通道的标记SelectionKey

每个注册到选择器中的通道,都有一个代表通道的SelectionKey对象。

可选择通道SelectableChannel

  1. ServerSocketChannel
  2. SocketChannel
  3. DatagramChannel
  4. SctpChannel
  5. SctpMultiChannel
  6. SctpServerChannel
  7. SinkChannel
  8. SourceChannel

通道的操作类型

  1. OP_ACCEPT
  2. OP_CONNECT
  3. OP_READ
  4. OP_WRITE

ServerSocketChannel和SocketChannel使用

Server端:

try {
            ServerSocketChannel channel = ServerSocketChannel.open();
            //设置为非阻塞模式
            channel.configureBlocking(false);
            channel.bind(new InetSocketAddress("localhost",8088));
            //打开选择器
            Selector selector = Selector.open();
            //注册连接通道
            channel.register(selector, SelectionKey.OP_ACCEPT);
            while (true){
                //选择已就绪的key
                selector.select();
                //获取已就绪的key的集合
                Set<SelectionKey> selectionKeys = selector.selectedKeys();
                Iterator<SelectionKey> iterator = selectionKeys.iterator();

                while (iterator.hasNext()){
                    SelectionKey readyKey = iterator.next();
                    iterator.remove();
                    //判断当前已就绪key是否是连接通道的key
                    if(readyKey.isAcceptable()) {
                        //接收客户端的连接
                        SocketChannel accept = channel.accept();
                        accept.configureBlocking(false);
                        //注册读取客户端数据的通道
                        accept.register(selector, SelectionKey.OP_WRITE);
                    }
                    //判断当前key是否是可写入的通道
                    if(readyKey.isWritable()){
                        SocketChannel socketChannel = (SocketChannel) readyKey.channel();
                        File file = new File("D:\\test001.txt");
                        FileInputStream fileInputStream = new FileInputStream(file);
                        FileChannel fileChannel = fileInputStream.getChannel();
                        // 设置帧头写入缓冲区,回写消息给客户端
                        boolean isFrame=true;
                        while (fileChannel.position()<fileChannel.size()){
                            ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
                            // 读取要发送的文件的内容,放入缓冲区
                            if(isFrame){
                                // 设置是否包含frame
                                byteBuffer.putInt(0);
                                // 写入文件名长度到帧头缓冲区 int为4字节
                                byteBuffer.putInt(file.getName().length());
                                // 写入文件名到帧头缓冲区
                                byteBuffer.put(file.getName().getBytes());
                                // 写入文件文件大小到帧头缓冲区
                                byteBuffer.putLong(fileChannel.size());
                                isFrame=false;
                            }else {
                                byteBuffer.putInt(1);
                            }
                            fileChannel.read(byteBuffer);
                            // 将文件内容写入缓冲区
                            byteBuffer.flip();
                            socketChannel.write(byteBuffer);
                            byteBuffer.clear();
                        }
                        socketChannel.close();
                        fileChannel.close();
                    }

                }
            }
         } catch (IOException e) {
            e.printStackTrace();
        }

Client端

try {
            SocketChannel socketChannel_1 = SocketChannel.open();
            socketChannel_1.configureBlocking(false);
            socketChannel_1.connect(new InetSocketAddress("localhost", 8088));
            Selector open = Selector.open();
            socketChannel_1.register(open, SelectionKey.OP_CONNECT);
            while (true) {
                open.select();
                Set<SelectionKey> selectionKeys = open.selectedKeys();
                Iterator<SelectionKey> iterator = selectionKeys.iterator();
                while (iterator.hasNext()) {
                    SelectionKey next = iterator.next();
                    iterator.remove();
                    if (next.isConnectable()) {
                        while (!socketChannel_1.finishConnect()) {
                        }
                        socketChannel_1.register(open, SelectionKey.OP_READ);
                    }
                    if (next.isReadable()) {
                        ByteBuffer allocate = ByteBuffer.allocate(1024);
                        int read = socketChannel_1.read(allocate);
                        while (read !=-1) {
                            read = socketChannel_1.read(allocate);
                        }
                        allocate.flip();
                        if(allocate.remaining() > 0 ) {
                            int isFrame = allocate.getInt();
                            //读取frame
                            if (isFrame == 0) {
                                int anInt = allocate.getInt();
                                System.out.println("filename length = " + anInt);
                                byte[] bytes = new byte[anInt];
                                allocate.get(bytes);
                                System.out.println("filename : " + new String(bytes, "utf8"));
                                long length = allocate.getLong();
                                System.out.println("content length = " + length);
                            }
                            //读取数据,写入本地文件
                            File file = new File("E:\\test001.txt");
                            if(!file.exists()){
                                file.createNewFile();
                            }
                            FileOutputStream fileInputStream = new FileOutputStream(file);
                            FileChannel fileChannel = fileInputStream.getChannel();
                            fileChannel.write(allocate);
                        }
                        allocate.clear();
                    }
                }


            }
        } catch (IOException e) {
            e.printStackTrace();
        }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值