>  기사  >  Java  >  Java NIO를 완전히 마스터할 수 있도록 안내합니다(요약 공유).

Java NIO를 완전히 마스터할 수 있도록 안내합니다(요약 공유).

WBOY
WBOY앞으로
2022-03-31 11:54:051798검색

이 글은 NIO 코어, BIO와 NIO의 비교, NIO를 통한 간단한 서버-클라이언트 통신 등 NIO 관련 문제를 주로 소개하는 java에 대한 관련 지식을 제공합니다.

Java NIO를 완전히 마스터할 수 있도록 안내합니다(요약 공유).

추천 학습: "java 튜토리얼"

1. Java 마인드 맵

Java NIO를 완전히 마스터할 수 있도록 안내합니다(요약 공유).

2. I/O 모델

데이터를 처리하는 데 사용되는 I/O 모델의 핵심은 무엇인가요? 송수신은 프로그램 통신의 성능을 크게 좌우합니다.
Java는 BIO, NIO, AIO

  • BIO의 총 세 가지 네트워크 프로그래밍 모델을 지원합니다. 동기화 및 차단, 서비스 구현 모드는 하나의 연결과 하나의 스레드입니다. 즉, 클라이언트가 연결 요청을 하면 서버가 처리를 위해 스레드를 시작해야 합니다.

  • NIO: 동기식 및 비차단형 서버 구현 모드는 하나의 스레드가 여러 요청 연결을 처리하는 것입니다. 즉, 클라이언트가 보낸 모든 요청은 멀티플렉서에 등록되고 멀티플렉서는 연결이 완료될 때까지 폴링합니다. I/O O 요청이 처리됩니다.

  • AIO: 비동기 비차단, AIO는 비동기 채널 개념을 도입하고 Proactor 모드를 채택하며 프로그램 작성을 단순화하고 유효한 요청 후에만 스레드를 시작하는 것이 특징입니다. 서버.

3. BIO, NIO, AIO 애플리케이션 시나리오

  • BIO 방식은 상대적으로 적고 연결 수가 고정된 아키텍처에 적합합니다. 이 방식은 서버 리소스에 대한 요구 사항이 상대적으로 높으며 동시성은 애플리케이션으로 제한됩니다. JDK1.4 이전에는 유일한 옵션이지만 절차가 간단하고 이해하기 쉽습니다.

  • NIO 방식은 채팅 서버, 사격 시스템, 서버 간 통신 등과 같이 연결 수가 많고 상대적으로 연결이 짧은(가벼운 작업) 아키텍처에 적합합니다. 프로그래밍은 더욱 복잡해졌으며 JDK1.4에서는 이를 지원하기 시작했습니다.

  • AIO 방법은 사진 앨범 서버와 같이 연결 수가 많고 연결이 상대적으로 긴 아키텍처에서 사용됩니다. JDK7은 프로그래밍이 더 복잡합니다.

4. 간단한 BIO 프로그래밍 프로세스

  • 서버는 ServerSocket을 시작합니다.

  • 클라이언트는 서버와 통신하기 위해 소켓을 시작합니다.

  • 클라이언트가 요청을 보낸 후 먼저 서버에 문의하여 스레드 응답이 있는지 확인합니다. 그렇지 않으면 기다리거나 거부됩니다. 클라이언트 스레드는 실행을 계속하기 전에 요청이 끝날 때까지 기다립니다.

  • 5. NIO 코어

  • NIO 세 가지 핵심 부분이 있습니다: 선택기, 채널 및 버퍼.
NIO는 버퍼 지향 또는 블록 지향 프로그래밍입니다. 데이터는 나중에 처리할 버퍼로 읽혀지며, 필요할 때 버퍼에서 앞뒤로 이동할 수 있습니다. 그것을 사용하십시오. 비차단, 확장성이 뛰어난 네트워킹을 제공합니다.

HTTP2.0은 멀티플렉싱 기술을 사용하여 동일한 연결이 여러 요청을 동시에 처리할 수 있도록 하며, 동시 요청 수는 HTTP1.1보다 몇 배 더 큽니다.

간단히 말해서 NIO는 하나의 스레드로 여러 요청을 처리할 수 있습니다.


6. BIO와 NIO의 비교

BIO는 데이터를 스트림으로 처리하는 반면, NIO는 블록 I/O의 효율성이 스트림 I/O의 효율성보다 훨씬 높습니다. BIO는 차단합니다. 예, NIO는 비차단입니다.

  • BIO는 바이트 스트림과 문자 스트림을 기반으로 작동하는 반면 NIO는 채널 및 버퍼를 기반으로 작동하며 데이터는 항상 채널에서 버퍼로 읽혀집니다. 채널에 대한 버퍼입니다. 선택기는 여러 채널 이벤트(예: 연결 요청, 데이터 도착 등)를 모니터링하는 데 사용되므로 단일 스레드가 여러 클라이언트 채널을 모니터링할 수 있습니다.

  • 7. NIO

  • 흐름도 설명:

선택기는 하나의 스레드에 해당하고 하나의 스레드는 여러 채널(연결)에 해당합니다.

Java NIO를 완전히 마스터할 수 있도록 안내합니다(요약 공유).
이 그림은 다음을 반영합니다. 세 가지 채널이 있습니다. //프로그램에 등록하세요.

  • 각 채널은 버퍼에 해당합니다.

  • 프로그램이 전환되는 채널은 이벤트에 따라 결정되며 이벤트는 중요한 개념입니다. 선택기는 각 채널에서 전환되는 다양한 이벤트에 따라 응답합니다.

  • 버퍼는 메모리 블록이고 맨 아래 레이어에 배열이 있습니다.

  • 데이터는 다음과 같습니다. BIO는 입력 스트림이거나 양방향일 수 없는 출력 스트림이지만 NIO 버퍼는 읽거나 쓸 수 있으며 플립 방법을 사용하여 전환해야 합니다.

  • 채널은 기본 운영 체제의 상태를 반환할 수 있습니다. 예를 들어 Linux의 경우 기본 운영 체제 채널은 양방향입니다.

8버퍼는 기본적으로 읽고 사용할 수 있는 버퍼입니다. 기록된 데이터의 메모리 블록은 컨테이너 개체(배열 포함)로 이해될 수 있습니다. 이 개체는 메모리 블록을 보다 쉽게 ​​사용할 수 있도록 하는 일련의 메서드를 제공합니다. 버퍼 개체에는 추적하고 기록할 수 있는 몇 가지 내장 메커니즘이 있습니다. 버퍼의 상태가 변경됩니다. 채널은 파일이나 네트워크에서 데이터를 읽기 위한 채널을 제공하지만, 읽거나 쓰는 데이터는 반드시 버퍼를 거쳐야 합니다.

NIO에서 Buffer는 추상 클래스인 최상위 상위 클래스입니다.


1. 일반적으로 사용되는 Buffer 하위 클래스 목록

    ByteBuffer는 버퍼에 바이트 데이터를 저장합니다.
  • ShortBuffer는 문자열 데이터를 버퍼에 저장합니다. Area;
  • IntBuffer는 버퍼에 정수 데이터를 저장합니다.
  • DoubleBuffer는 버퍼에 소수점을 저장합니다.
  • 2. 버퍼의 4가지 주요 속성

  • mark: mark

  • position: 위치, 버퍼 데이터가 읽힐 때마다 읽거나 쓸 다음 요소의 인덱스 읽기 또는 쓰기 다음 읽기 및 쓰기를 준비하기 위해 값이 매번 변경됩니다.

limit: 버퍼의 현재 끝점을 나타냅니다. 버퍼가 제한을 초과하는 위치에서는 읽기 및 쓰기 작업을 수행할 수 없습니다. 그리고 제한은 수정될 수 있습니다.

    capacity: 용량, 즉 수용할 수 있는 최대 데이터 양입니다. 버퍼가 생성될 때 설정되며 변경할 수 없습니다.
  • 3.버퍼가 api를 자주 사용할 때 도입된 API
  • JDK1.4
  • public final int 용량( )//이 버퍼의 용량을 반환합니다

  • public final int position( ) / /이 버퍼의 위치를 ​​반환합니다

public final Buffer position (int newPositio)//이 버퍼의 위치를 ​​설정합니다Java NIO를 완전히 마스터할 수 있도록 안내합니다(요약 공유).

public final intlimit()//이 버퍼의 제한을 반환합니다

public final Buffer Limit(int newLimit )//이 버퍼의 제한을 설정합니다.

public final Buffer mark( )//이 버퍼의 위치에 표시를 설정합니다.

public final Buffer Reset( )//이 버퍼의 위치를 ​​이전에 표시된 위치로 재설정합니다.
  • public final Bufferclear( )//이 버퍼를 지웁니다. 즉, 각 표시를 초기 상태로 복원하지만 데이터는 실제로 지워지지 않으며 후속 작업에서는 이를 덮어씁니다.
  • public final Buffer Flip( )//Invert this buffer
  • public final Buffer rewind( )//이 버퍼 되감기
  • public final int 남은( )//현재 위치와 한계 사이의 요소 수를 반환
  • public final boolean hasRemaining( )//현재를 알림 position 및 제한 사항 사이에 요소가 있는지 여부
  • public abstract boolean isReadOnly();//이 버퍼가 읽기 전용 버퍼인지 여부를 알려줍니다
  • JDK1.6에 도입된 API
  • public abstract boolean hasArray();// 이 버퍼에 액세스 가능한 기본 구현 배열이 있는지 여부를 알려줍니다
  • public abstract Object array();//이 버퍼의 기본 구현 배열을 반환합니다
  • public abstract int arrayOffset();//이 버퍼의 기본 구현 배열을 반환합니다 오프셋 첫 번째 버퍼 요소
  • public abstract boolean isDirect();//이 버퍼가 직접 버퍼인지 알려줍니다

9. 채널(channel)
  • 1 기본 소개
  • (1) NIO 채널은 스트림과 유사합니다
  • 채널은 동시에 읽고 쓸 수 있지만 스트림은 읽고 쓸 수만 있습니다.

채널은 비동기적으로 데이터를 읽고 쓸 수 있습니다.Java NIO를 완전히 마스터할 수 있도록 안내합니다(요약 공유).

채널은 버퍼에서 데이터를 읽을 수 있고 버퍼

(2) BIO의 스트림은 단방향입니다. 예를 들어 FileInputStream 개체는 데이터를 읽을 수만 있는 반면 NIO의 채널은 양방향이며 읽기 또는 쓰기 작업이 가능합니다. (3) 채널은 NIO의 인터페이스입니다. (4) 일반적으로 사용되는 채널 클래스는 FileChannel, DatagramChannel, ServerSocketChannel 및 SocketChannel입니다. ServerSocketChanne은 ServerSocket과 유사하고 SocketChannel은 Socket과 유사합니다.

(5) FileChannel은 파일 데이터 읽기 및 쓰기에 사용되고, DatagramChannel은 UDP 데이터 읽기 및 쓰기에 사용되며, ServerSocketChannel 및 SocketChannel은 TCP 데이터 읽기 및 쓰기에 사용됩니다.

    2. FileChannel
  • FileChannel은 주로 로컬 파일에 대한 IO 작업을 수행하는 데 사용됩니다. 일반적인 방법은 다음과 같습니다.
채널에서 데이터를 읽고 버퍼에 넣습니다. buffer 해당 영역의 데이터가 채널




에 기록됩니다.transferFrom, 대상 채널의 데이터를 현재 채널로 복사

transferTo, 현재 채널의 데이터를 대상 채널로 복사

3. 버퍼 및 채널에 대한 참고 사항

  • ByteBuffer는 입력된 데이터 유형에 관계없이 해당 데이터 유형을 사용하여 꺼내야 합니다. 그렇지 않으면 BufferUnderflowException이 발생할 수 있습니다. 예외.

  • 일반 버퍼를 읽기 전용 버퍼로 변환할 수 있습니다.

  • NIO는 파일을 메모리(힙 외부 메모리)에서 직접 수정할 수 있도록 하는 MappedByteBuffer도 제공하며, 파일 동기화 방법은 NIO에서 완료합니다.

  • NIO는 여러 버퍼(예: 버퍼 배열), 즉 분산 및 수집을 통한 읽기 및 쓰기 작업도 지원합니다.

10. 셀렉터(selector)

1. 기본 소개

  • Java의 NIO, Non-Blocking IO 방식. 하나의 스레드를 사용하여 여러 클라이언트 연결을 처리할 수 있으며 선택기를 사용하게 됩니다.

  • 선택기는 등록된 여러 채널에서 이벤트가 발생하는지 감지할 수 있습니다. 이벤트가 발생하면 이벤트를 획득하고 그에 따라 각 이벤트를 처리합니다. 이러한 방식으로 단일 스레드만 사용하여 여러 채널을 관리할 수 있습니다. 즉, 여러 연결과 요청을 관리할 수 있습니다.

  • 연결/채널에 실제로 읽기 또는 쓰기 이벤트가 있는 경우에만 읽기 및 쓰기가 수행되므로 시스템 오버헤드가 크게 줄어들고 연결마다 스레드를 생성하고 여러 스레드를 유지할 필요가 없습니다.

  • 여러 스레드 간 컨텍스트 전환으로 인한 오버헤드를 방지하세요.

2. 선택기 관련 메소드

  • open();//선택기 객체 가져오기

  • select(long timeout);//IO 작업이 있는 경우 등록된 모든 채널을 모니터링합니다. , 해당 SelectionKey를 내부 컬렉션에 추가하고 반환합니다. 매개변수는 시간 제한을 설정하는 데 사용됩니다.

  • selectedKeys(); //내부 컬렉션에서 모든 SelectionKey를 가져옵니다.

3. Notes

NIO의 ServerSocketChannel 기능은 ServerSocket과 유사하며, SocketChannel 기능은 Socket과 유사합니다.

11. NIO를 통한 간단한 서버-클라이언트 통신

1. 서버

package com.nezha.guor.nio;import java.io.IOException;import java.net.InetSocketAddress;import java.nio.ByteBuffer;import java.nio.channels.*;import java.util.Iterator;public class NioServer {
    private Selector selector;
    private ServerSocketChannel serverSocketChannel;
    private static final int PORT = 8080;

    public NioServer() {
        try {
            //获得选择器
            selector = Selector.open();
            serverSocketChannel =  ServerSocketChannel.open();
            //绑定端口
            serverSocketChannel.socket().bind(new InetSocketAddress(PORT));
            //设置非阻塞模式
            serverSocketChannel.configureBlocking(false);
            //将该ServerSocketChannel 注册到selector
            serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
        }catch (IOException e) {
            System.out.println("NioServer error:"+e.getMessage());
        }
    }

    public void listen() {

        System.out.println("监听线程启动: " + Thread.currentThread().getName());
        try {
            while (true) {
                int count = selector.select();
                if(count > 0) {
                    //遍历得到selectionKey集合
                    Iterator<selectionkey> iterator = selector.selectedKeys().iterator();
                    while (iterator.hasNext()) {
                        SelectionKey key = iterator.next();

                        if(key.isAcceptable()) {
                            SocketChannel sc = serverSocketChannel.accept();
                            sc.configureBlocking(false);
                            sc.register(selector, SelectionKey.OP_READ);
                            System.out.println(sc.getRemoteAddress() + " 上线 ");
                        }
                        //通道发送read事件,即通道是可读的状态
                        if(key.isReadable()) {
                            getDataFromChannel(key);
                        }
                        //当前的key 删除,防止重复处理
                        iterator.remove();
                    }
                } else {
                    System.out.println("等待中");
                }
            }
        }catch (Exception e) {
            System.out.println("listen error:"+e.getMessage());
        }
    }

    private void getDataFromChannel(SelectionKey key) {
        SocketChannel channel = null;
        try {
            channel = (SocketChannel) key.channel();
            ByteBuffer buffer = ByteBuffer.allocate(1024);

            int count = channel.read(buffer);
            //根据count的值做处理
            if(count > 0) {
                String msg = new String(buffer.array());
                System.out.println("来自客户端: " + msg);

                //向其它的客户端转发消息(排除自己)
                sendInfoToOtherClients(msg, channel);
            }
        }catch (IOException e) {
            try {
                System.out.println(channel.getRemoteAddress() + " 离线了");
                //取消注册
                key.cancel();
            }catch (IOException ex) {
                System.out.println("getDataFromChannel error:"+ex.getMessage());
            }
        }finally {
            try {
                channel.close();
            }catch (IOException ex) {
                System.out.println("channel.close() error:"+ex.getMessage());
            }
        }
    }

    //转发消息给其它客户(通道)
    private void sendInfoToOtherClients(String msg, SocketChannel self ) throws  IOException{
        System.out.println("服务器转发消息中...");
        System.out.println("服务器转发数据给客户端线程: " + Thread.currentThread().getName());
        //遍历 所有注册到selector 上的 SocketChannel,并排除 self
        for(SelectionKey key: selector.keys()) {
            Channel targetChannel = key.channel();

            //排除自己
            if(targetChannel instanceof  SocketChannel && targetChannel != self) {
                SocketChannel dest = (SocketChannel)targetChannel;
                //将信息存储到buffer
                ByteBuffer buffer = ByteBuffer.wrap(msg.getBytes());
                //将buffer数据写入通道
                dest.write(buffer);
            }
        }
    }

    public static void main(String[] args) {
        //创建服务器对象
        NioServer nioServer = new NioServer();
        nioServer.listen();
    }}</selectionkey>

3. 콘솔 출력

"Java NIO를 완전히 마스터할 수 있도록 안내합니다(요약 공유).자바 튜토리얼
"Java NIO를 완전히 마스터할 수 있도록 안내합니다(요약 공유).

위 내용은 Java NIO를 완전히 마스터할 수 있도록 안내합니다(요약 공유).의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
이 기사는 csdn.net에서 복제됩니다. 침해가 있는 경우 admin@php.cn으로 문의하시기 바랍니다. 삭제