자바 기반 NIO 소개 및 사용

21219 단어 JavaNIO
NIO
java.nio 는 자바 non-blocking IO 라 고 합 니 다.jdk 1.4 및 이상 버 전에 서 제공 하 는 새로운 api(New IO)를 말 합 니 다.모든 원본 형식(boolean 형식 제외)에 캐 시 지원 데이터 용 기 를 제공 합 니 다.이 를 사용 하면 비 차단 식 고 신축성 네트워크 를 제공 할 수 있 습 니 다.
2,3 대 구성 요소
NIO 3 대 구성 요소:Channel,Buffer,Selector
1.Channel 과 Buffer
Channel 은 데 이 터 를 읽 고 기록 할 수 있 는 대상 입 니 다.NIO 를 원래 의 I/O 와 비교 하면 채널 은 마치 흐름 과 같 고 버퍼(Buffer)를 향 한 것 이다.모든 데 이 터 는 Buffer 대상 을 통 해 처 리 됩 니 다.채널 에 바이트 를 직접 쓰 지 않 고 하나 이상 의 바이트 가 포 함 된 버퍼 에 데 이 터 를 기록 합 니 다.채널 에서 바 이 트 를 직접 읽 지 않 고 채널 에서 버퍼 를 읽 고 버퍼 에서 이 바 이 트 를 가 져 옵 니 다.
Channel 은 데 이 터 를 읽 고 쓰 는 양 방향 채널 로 Channel 에서 데 이 터 를 Buffer 로 읽 을 수도 있 고 Buffer 의 데 이 터 를 Channel 로 쓸 수도 있 으 며 이전 Stream 은 입력(InputStream)이나 출력(OutputStream)으로 한 방향 으로 만 유통 된다.채널(Channel)은 읽 기,쓰기 또는 읽 기,쓰기 에 사용 할 수 있 습 니 다.
흔 한 채널
1.FileChannel
2.DatagramChannel
3.SocketChannel
4.ServerSocketChannel
Buffer 버퍼 는 데 이 터 를 읽 고 쓰 는 데 사 용 됩 니 다.흔히 볼 수 있 는 Buffer 입 니 다.
1.ByteBuffer
2.ShortBuffer
3.IntBuffer
4.LongBuffer
5.FloatBuffer
6.DoubleBuffer
7.CharBuffer
2.Selector
다 중 스 레 드 모드 에서 IO 를 막 을 때 한 스 레 드 는 http 요청 과 같은 요청 만 처리 할 수 있 습 니 다.요청 응답 식 으로 연결 을 닫 고 스 레 드 자원 을 방출 합 니 다.selector 선택 기의 역할 은 하나의 스 레 드 에 맞 춰 여러 채널 을 관리 하 는 것 입 니 다.이러한 채널 에서 발생 하 는 사건 을 가 져 오 는 것 입 니 다.이런 채널 작업 은 비 차단 모드 에서 스 레 드 를 한 채널 에 계속 두 지 않 고 연결 수가 매우 많 지만 데이터 가 낮은 장면 에 적합 합 니 다.
在这里插入图片描述
Selector 를 호출 하 는 select()방법 은 채널 에서 읽 기와 쓰기 가 완 료 된 이 벤트 를 보 낼 때 까지 막 힙 니 다.이 이벤트 들 이 발생 하면 select()방법 은
이 사건 들 을 thread 에 되 돌려 처리 합 니 다.
3.ByteBuffer 의 사용
속성:
Buffer 의 읽 기와 쓰기 동작 은 position,mark,limit 의 값 을 바 꾸 어 실현 하 는 것 입 니 다.그들 사이 의 관 계 를 주의 하면 쉽게 읽 고 쓰 고 덮어 쓸 수 있다.
  • position:쓰기 모드 에 대해 현재 쓰기 가능 한 데이터 의 아래 표 시 를 표시 하고 읽 기 모드 에 대해 다음 에 읽 을 수 있 는 데이터 의 아래 표 시 를 표시 합 니 다.현재 작업 위치의 아래 표 시 됩 니 다.position()방법 을 가 져 옵 니 다.
  • 4.567917.mark:현재 작업 위 치 를 표시 하고 mark()방법 을 호출 하여 mark=position 를 읽 고 쓰기 전환 과정 에서 이전 작업 의 아래 표 시 된 위 치 를 표시 할 수 있 습 니 다limit:Buffer 의 한계 치.쓰기 모드 에 대해 서 는 쓰기 가능 한 최대 값,배열 길이 에 해당 합 니 다.읽 기 모드 에 대해 가장 많이 읽 을 수 있 는 데 이 터 를 표시 하 는 위치 아래 표 시 는 flip()방법 으로 읽 기와 쓰기 전환 을 하고 원 리 는 position,mark,limit 의 값 을 바 꿉 니 다용량:배열 용량 크기방법:
    Buffer 의 방법 은 모두 position 의 값 을 바 꾸 는 것 에 따라 조작 된다.
  • put():put 방법 으로 데 이 터 를 기록 하면 하나의 byte 바이트 나 byte 배열 이 가능 합 니 다.또는 다른 유형 은 Buffer 인 스 턴 스 에 따라 정 합 니 다
  • get():get 방법 으로 데 이 터 를 읽 으 면 byte 배열 에 들 어 갈 수 있 고 전달 되 지 않 으 면 바이트 하 나 를 읽 을 수 있 습 니 다
  • mark():현재 아래 표 시 된 position 위 치 를 표시 합 니 다.mark=position.읽 기와 쓰기 동작 전환 또는 특수 요구 시 현재 아래 표 시 된 위 치 를 표시 합 니 다
  • reset():position 값 을 mark()방법 으로 표 시 된 값 으로 초기 화 합 니 다
  • array():Buffer 내 데이터 의 byte 배열.값 이 없 는 자 리 는 0 으로 보충 합 니 다
  • flip():limit 는 position 값 이 고 position 를 0,mark 초기 값 으로 설정 하 며 쓰기 동작 을 읽 기 동작 으로 전환 합 니 다
  • rewind():position 와 mark 를 초기 값 으로 설정 합 니 다.이 를 사용 하면 내용 을 계속 읽 거나 덮어 쓰기 전의 내용 을 계속 쓸 수 있 습 니 다.첫 번 째 부터 읽 거나 쓸 수 있 습 니 다
  • clear():속성 치 를 복원 합 니 다.이전 에는 array()배열 의 값 이 있 었 습 니 다
  • hasRemaining():마지막 까지 여 부 를 판단 합 니 다
  • 4.테스트 데모
    
    private static void buffer1() {
        String data = "abc";
        //byte[] bytes = new byte[1024];
        //         ,1024byte
        ByteBuffer byteBuffer = ByteBuffer.allocate(15);
        System.out.println(byteBuffer.toString());
        //       position  ,mark = position ,      
        System.out.println(byteBuffer.mark());
        //      ,             ,          ,      ,                  
        System.out.println(byteBuffer.limit());
        //       ,            ,      ,               
        System.out.println(byteBuffer.position());
        //capacity            
        System.out.println(byteBuffer.capacity());
        //        byteBuffer
        byteBuffer.put(data.getBytes());
        //          
        for(Byte b : byteBuffer.array()){
            System.out.print(b + " ");
        }
        System.out.println();
        System.out.println(new String(byteBuffer.array()));
        //          limit,position ,mark  
        byteBuffer.flip();
        //      ,        
        byteBuffer.put("n".getBytes()); // abc    nbc
        System.out.println(new String(byteBuffer.array()));
        // position       ,  mark()    mark = position,   position = mark,mark        
        byteBuffer.mark();
        byteBuffer.reset();
        // position   mark     ,                        ,       / 
        byteBuffer.rewind();
        for(Byte b : byteBuffer.array()){
            System.out.print(b + " ");
        }
        System.out.println();
        System.out.println(byteBuffer.toString());
        //          ,        
        byteBuffer.compact();
        System.out.println(byteBuffer.toString());
    }
    
    쓰기 읽 기 전체 작업
    
    private static void buffer(){
        //     
        String data = "1234";
        //         ,1024byte
        ByteBuffer byteBuffer = ByteBuffer.allocate(15);
        //    
        byteBuffer.put(data.getBytes());
        //    49 50 51 52 0 0 0 0 0 0 0 0 0 0 0
        println(byteBuffer.array());
        byteBuffer.put((byte) 5);
        //       : 49 50 51 52 5 0 0 0 0 0 0 0 0 0 0
        println(byteBuffer.array());
        //    
        byteBuffer.flip(); //       position = 0
        byteBuffer.put((byte) 1);
        byteBuffer.put((byte) 2);
        byteBuffer.put((byte) 3);
        byteBuffer.put((byte) 4);
        byteBuffer.put((byte) 5);
        //      : 1 2 3 4 5 0 0 0 0 0 0 0 0 0 0
        println(byteBuffer.array());
        //      
        byteBuffer.flip();//     
        while (byteBuffer.position() != byteBuffer.limit()){
            System.out.println(byteBuffer.get());
        }
        //   position       limit  
        System.out.println(byteBuffer.toString());
    
        //    
        byteBuffer.flip(); //  position   0,      
        //    byte  ,           
        byte[] bytes = new byte[byteBuffer.limit()];
        byteBuffer.get(bytes);
        //  bytes 1 2 3 4 5
        println(bytes);
    }
    
     private static void println(byte[] bytes){
         for(Byte b : bytes){
             System.out.print(b + " ");
         }
         System.out.println();
    }
    
    문자열 과 ByteBuffer 사이 의 변환
    
    public static void main(String[] args) {
    
        /*======================    buffer===========================*/
        //1.      buytebuffer               
        ByteBuffer buffer = ByteBuffer.allocate(1024);
        buffer.put("nio".getBytes());
    
        //2.charset         
        ByteBuffer buffer1 = StandardCharsets.UTF_8.encode("nio");
    
        //3, warp        
        ByteBuffer buffer2 = ByteBuffer.wrap("nio".getBytes());
    
        /*======================buffer    ===========================*/
        String str = StandardCharsets.UTF_8.decode(buffer1).toString();
        System.out.println(str);
    }
    
    5.채널 의 사용
    파일 프로 그래 밍 FileChannel
    FileChannel 은 차단 모드 에서 만 작 동 할 수 있 습 니 다.Selector 에서 사용 할 수 없습니다.Channel 은 양 방향 채널 이지 만 FileChannel 은 원본 을 가 져 오 는 데 따라 읽 거나 쓸 수 있 습 니 다.
    FileInputStream 가 져 오기,읽 기 전용FileOutputStream 가 져 오기,쓰기 만 가능
  • RandomAccessFile 가 져 오기,읽 기,쓰기(양 방향 채널)
  • 
    **
     *        Channel,FileChannel    
     * @throws Exception
     */
    private static void channel() throws Exception{
        FileInputStream in = new FileInputStream("C:\\Users\\zqq\\Desktop\\123.txt");
        FileOutputStream out = new FileOutputStream("C:\\Users\\zqq\\Desktop\\321.txt");
        //           channel
        FileChannel channel = in.getChannel();
        //  FileChannel
        FileChannel outChannel = out.getChannel();
        //     byteBuffer
        ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
        //   channel        byteBuffer ,channel   Buffer  
        while (channel.read(byteBuffer) != -1){
            //position   0,     
            byteBuffer.flip();
            outChannel.write(byteBuffer);
            //byteBuffer     
            byteBuffer.clear();
        }
        //      
        channel.close();
        out.flush();
        out.close();
        in.close();
        out.close();
    }
    
    
    /**
     *       Channel
     * @throws Exception
     */
    public static void channel1() throws Exception{
        // StandardOpenOption.READ :          ,             
        // StandardOpenOption.WRITE :           ,          ,             
        // StandardOpenOption.APPEND:           ,          ,             
        // StandardOpenOption.CREATE:       ,          
        // StandardOpenOption.CREATE_NEW:       ,         
        // StandardOpenOption.DELETE_ON_CLOSE:         
        //     
        FileChannel inChannel = FileChannel.open(Paths.get("C:\\Users\\zqq\\Desktop\\123.txt"), StandardOpenOption.READ);
        FileChannel outChannel = FileChannel.open(Paths.get("C:\\Users\\zqq\\Desktop\\321.txt"),
                StandardOpenOption.READ,StandardOpenOption.WRITE,StandardOpenOption.CREATE);
    
        //    
        MappedByteBuffer inByteBuffer = inChannel.map(FileChannel.MapMode.READ_ONLY,0,inChannel.size());
        MappedByteBuffer outByteBuffer = outChannel.map(FileChannel.MapMode.READ_WRITE,0,inChannel.size());
    
        //          
        byte[] bytes = new byte[inByteBuffer.limit()];
        inByteBuffer.get(bytes);//  inByteBuffer    ,  bytes   
        outByteBuffer.put(bytes);//   outByteBuffer
        inChannel.close();
        outChannel.close();
    }
    
    RandomAccessFile 채널 열기
    
    /**
     *   RandomAccessFile      
     * @throws Exception
     */
    private static void random() throws Exception{
        try (RandomAccessFile randomAccessFile = new RandomAccessFile("D:\\workspace\\Demo\\test.txt","rw")){
            //  Channel
            FileChannel fileChannel = randomAccessFile.getChannel();
            //    
            //fileChannel.truncate(10);
            //  ByteBuffer,      
            ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
            fileChannel.read(byteBuffer);
            System.out.println(new String(byteBuffer.array(),"GBK"));
            byteBuffer.clear();
            String data = "this is data\r";
            byteBuffer.put(data.getBytes());
            byteBuffer.flip();//    
            while (byteBuffer.hasRemaining()){
                fileChannel.write(byteBuffer);
            }
            //           
            fileChannel.force(true);
        }
    }
    
    FileChannel 데이터 전송 전송 전송
    
    /**
     *             (copy)
     */
    private static void transferTo() {
        try (
                FileChannel inChannel = new FileInputStream("C:\\Users\\44141\\Desktop\\demo\\in.txt").getChannel();
                FileChannel outChannel = new FileOutputStream("C:\\Users\\44141\\Desktop\\demo\\out.txt").getChannel()
        ){
            //               ,   。  2g
            inChannel.transferTo(0,inChannel.size(),outChannel);
        }catch (Exception e){
            e.printStackTrace();
        }
    }
    
    /**
     *   2g  
     */
    private static void transferToGt2g() {
        try (
                FileChannel inChannel = new FileInputStream("C:\\Users\\44141\\Desktop\\demo\\in.txt").getChannel();
                FileChannel outChannel = new FileOutputStream("C:\\Users\\44141\\Desktop\\demo\\out1.txt").getChannel()
        ){
            //  inChannel     
            long size = inChannel.size();
            //    
            for(long rsize = size; rsize > 0;){
                //transferTo        
                rsize -= inChannel.transferTo((size-rsize),rsize,outChannel);
            }
        }catch (Exception e){
            e.printStackTrace();
        }
    }
    
    네트워크 프로 그래 밍
    차단 모드
    차단 모드 서버 의 모든 방법 은 클 라 이언 트 작업 을 기다 리 는 것 을 막 습 니 다.예 를 들 어 accept()방법 은 클 라 이언 트 연결 을 기다 리 는 것 을 막 고 read()방법 은 클 라 이언 트 가 데 이 터 를 전송 하 는 것 을 막 으 며 동시에 방문 하면 효율 적 인 작업 을 할 수 없습니다.
    1.서버 코드
    
    private static void server() throws Exception{
    
        //1.     
        ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
    
        //2.      
        serverSocketChannel.bind(new InetSocketAddress(8080));
    
        while (true){
            System.out.println("      =============" );
            //4.accept           SocketChannel  ,accept    
            SocketChannel socketChannel = serverSocketChannel.accept();
            System.out.println("     ===============" );
            ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
            // read()     ,            
            socketChannel.read(byteBuffer);
            byteBuffer.flip();
            String str = StandardCharsets.UTF_8.decode(byteBuffer).toString();
            System.out.println("     =============:" + str);
        }
    }
    
    2.클 라 이언 트 코드
    
    private static void client() throws Exception {
        //1.     
        SocketChannel socketChannel = SocketChannel.open();
        //     
        socketChannel.connect(new InetSocketAddress("localhost",8080));
        //2       
        Thread.sleep(2 * 1000);
        socketChannel.write(StandardCharsets.UTF_8.encode("nio"));
        System.out.println();
    }
    
    비 차단 모드
    서버 에서 비 차단 모드 로 설정 합 니 다.클 라 이언 트 는 움 직 이지 마 세 요.
    
    private static void server() throws Exception{
    
        ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
    
        //        
        serverSocketChannel.configureBlocking(false);
        serverSocketChannel.bind(new InetSocketAddress(8080));
        while (true){
            SocketChannel socketChannel = serverSocketChannel.accept();
            //      ,SocketChannel    null
            if(socketChannel != null){
                //     
                socketChannel.configureBlocking(false);
                ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
                socketChannel.read(byteBuffer);
                byteBuffer.flip();
                String str = StandardCharsets.UTF_8.decode(byteBuffer).toString();
                System.out.println("     =============:" + str);
            }
        }
    }
    
    7.선택 기
    Selector 선택 기의 역할 은 하나의 스 레 드 에 맞 춰 여러 채널 을 관리 하 는 것 입 니 다.Selector 가 관리 하 는 Channel 은 반드시 비 차단 모드 에서 이 루어 져 야 하기 때문에 Selector 는 FileChannel(FileChannel 은 차단 모드 만 있 음)과 함께 사용 할 수 없습니다.
    선택 기 생 성
    Selector 를 만 들 려 면 정적 방법 만 호출 하 십시오.
    
    //  Selector
    Selector selector = Selector.open();
    Selector 는 채널 의 이 벤트 를 감청 할 수 있 습 니 다.모두 네 개의 이벤트 가 있 습 니 다.
    accept:연결 요청 이 있 을 때 터치 합 니 다.정적 상수 Selection Key.OPACCEPT
  • connect:클 라 이언 트 가 연결 을 만 든 후 터치 합 니 다.정적 상수 SelectionKey.OPCONNECT
  • 읽 을 수 있 을 때 터치,정적 상수 SelectionKey.OPREAD write:쓸 수 있 을 때 터치,정적 상수 SelectionKey.OPWRITE Selector 와 Channel 연결
    
    //        
    serverSocketChannel.configureBlocking(false);
    SelectionKey selectionKey = serverSocketChannel.register(selector,0,null);
    //       
    selectionKey.interestOps(SelectionKey.OP_ACCEPT);
    
    8.네트워크 프로 그래 밍(다 중 재 활용)
    1.클 라 이언 트 코드 SocketChannel
    
    public static void main(String[] args) throws Exception {
        client();
    
    }
    
    private static void client() throws Exception {
        //1.     
        SocketChannel socketChannel = SocketChannel.open();
        //     
        socketChannel.connect(new InetSocketAddress("localhost",8080));
        //2       
        Thread.sleep(2 * 1000);
        socketChannel.write(StandardCharsets.UTF_8.encode("nio"));
        //3.         
        ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
        socketChannel.read(byteBuffer);
        byteBuffer.flip();
        System.out.println("       =======:" + StandardCharsets.UTF_8.decode(byteBuffer).toString());
        //    
        socketChannel.close();
    }
    
    2.서버 코드
    
    public static void main(String[] args) throws Exception{
        server();
    }
    
    private static void server() throws Exception{
        //  Selector
        Selector selector = Selector.open();
        ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
        //        
        serverSocketChannel.configureBlocking(false);
        // Channel   selector (    )
        //          SelectionKey         Channel   
        SelectionKey selectionKey = serverSocketChannel.register(selector,0,null);
        //selectionKey   ACCEPT  (                 )
        selectionKey.interestOps(SelectionKey.OP_ACCEPT);
        serverSocketChannel.bind(new InetSocketAddress(8080));
        while (true){
            System.out.println("  ====================");
            // select             ,          
            selector.select();
            System.out.println("      =================");
            //     
            Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
            while (iterator.hasNext()){
                SelectionKey sk = iterator.next();
                //   SelectionKey    ,         (Selecotr      )
                iterator.remove();
                //    ,   (          ,selector.select()      )
                //sk.cancel();
                //             
                if(sk.isAcceptable()){ //       
                    //  SelectionKey      channel
                    ServerSocketChannel channel = (ServerSocketChannel) sk.channel();
                    SocketChannel socketChannel = channel.accept();
                    socketChannel.configureBlocking(false);
                    //      ,bytebuffer        (                   )
                    ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
                    // socketChannel  Selector
                    SelectionKey socketKey = socketChannel.register(selector,0,byteBuffer);
                    //      
                    socketKey.interestOps(SelectionKey.OP_READ);
                }else if(sk.isReadable()){//    
                    try {
                        //  Channel
                        SocketChannel socketChannel = (SocketChannel) sk.channel();
                        //      ByteBuffer
                        ByteBuffer byteBuffer = (ByteBuffer) sk.attachment();
                        int read = socketChannel.read(byteBuffer);
                        //        read = -1
                        if(read == -1){
                            //    
                            sk.cancel();
                            continue;
                        }
                        byteBuffer.flip();
                        String str = StandardCharsets.UTF_8.decode(byteBuffer).toString();
                        System.out.println("        ===========:" + str);
                        //       
                        ByteBuffer writeBuffer = StandardCharsets.UTF_8.encode("this is result");
                        socketChannel.write(writeBuffer);
                        //                     (           )
                        if(writeBuffer.hasRemaining()){
                            //      ,    , interestOps()                (  linux       777)
                            sk.interestOps(sk.interestOps() + SelectionKey.OP_WRITE);
                            sk.attach(writeBuffer);
                            //       
                            //sk.interestOps(sk.interestOps() | SelectionKey.OP_WRITE);
                        }
                    }catch (IOException e){
                        e.printStackTrace();
                        //          ,    
                        sk.cancel();
                    }
                }else if(sk.isWritable()){
                    //  Channel
                    SocketChannel socketChannel = (SocketChannel) sk.channel();
                    //      ByteBuffer
                    ByteBuffer writeBuffer = (ByteBuffer) sk.attachment();
                    socketChannel.write(writeBuffer);
                    //      ,        ,  writeBuffer  
                    if(!writeBuffer.hasRemaining()){
                        sk.attach(null);
                        //      
                        sk.interestOps(sk.interestOps() - SelectionKey.OP_WRITE);
                    }
                }
            }
        }
    }
    
    자바 기반 의 NIO 소개 및 사용 에 관 한 이 글 은 여기까지 소개 되 었 습 니 다.더 많은 자바 NIO 에 관 한 상세 한 내용 은 우리 의 이전 글 을 검색 하거나 아래 의 관련 글 을 계속 조회 하 시기 바 랍 니 다.앞으로 많은 응원 바 랍 니 다!

    좋은 웹페이지 즐겨찾기