I/O 모델의 핵심은 데이터를 보내고 받는 데 어떤 채널이 사용되는지에 따라 결정됩니다. 프로그램 통신 성능이 크게 향상됩니다.
Java는 BIO, NIO, AIO
BIO의 총 세 가지 네트워크 프로그래밍 모델을 지원합니다. 동기화 및 차단, 서비스 구현 모드는 하나의 연결과 하나의 스레드입니다. 즉, 클라이언트가 연결 요청을 하면 서버가 처리를 위해 스레드를 시작해야 합니다.
NIO: 동기식 및 비차단형 서버 구현 모드는 하나의 스레드가 여러 요청 연결을 처리하는 것입니다. 즉, 클라이언트가 보낸 모든 요청은 멀티플렉서에 등록되고 멀티플렉서는 연결이 완료될 때까지 폴링합니다. I/O O 요청이 처리됩니다.
AIO: 비동기 비차단, AIO는 비동기 채널 개념을 도입하고 Proactor 모드를 채택하며 프로그램 작성을 단순화하고 유효한 요청 후에만 스레드를 시작하는 것이 특징입니다. 서버.
BIO 방식은 상대적으로 적고 연결 수가 고정된 아키텍처에 적합합니다. 이 방식은 서버 리소스에 대한 요구 사항이 상대적으로 높으며 동시성은 애플리케이션으로 제한됩니다. JDK1.4 이전에는 유일한 옵션이지만 절차가 간단하고 이해하기 쉽습니다.
NIO 방식은 채팅 서버, 사격 시스템, 서버 간 통신 등과 같이 연결 수가 많고 상대적으로 연결이 짧은(가벼운 작업) 아키텍처에 적합합니다. 프로그래밍은 더욱 복잡해졌으며 JDK1.4에서는 이를 지원하기 시작했습니다.
AIO 방법은 동시 작업에 참여하기 위해 OS를 완전히 호출하는 사진 앨범 서버와 같이 연결 수가 많고 연결이 상대적으로 긴 아키텍처에 사용됩니다. JDK7이 이를 지원하기 시작합니다
서버는 ServerSocket을 시작합니다.
클라이언트는 서버와 통신하기 위해 소켓을 시작합니다.
클라이언트가 요청을 보낸 후 먼저 서버에 문의하여 스레드 응답이 있는지 확인합니다. 그렇지 않으면 기다리거나 거부됩니다. 응답이 있으면 클라이언트 스레드는 실행을 계속하기 전에 요청이 끝날 때까지 기다립니다.
5. NIO 코어
6. BIO와 NIO의 비교
BIO는 바이트 스트림과 문자 스트림을 기반으로 작동하는 반면 NIO는 채널 및 버퍼를 기반으로 작동하며 데이터는 항상 채널에서 버퍼로 읽혀집니다. 채널에 대한 버퍼입니다. 선택기는 여러 채널 이벤트(예: 연결 요청, 데이터 도착 등)를 모니터링하는 데 사용되므로 단일 스레드가 여러 클라이언트 채널을 모니터링할 수 있습니다.
7. NIO의 세 가지 핵심 원칙 다이어그램
흐름도 설명:
이 그림은 다음을 반영합니다. 세 가지 채널이 있습니다. //프로그램에 등록하세요.
각 채널은 버퍼에 해당합니다.
프로그램이 전환되는 채널은 이벤트에 따라 결정되며 이벤트는 중요한 개념입니다. 선택기는 각 채널에서 전환되는 다양한 이벤트에 따라 응답합니다.
버퍼는 메모리 블록이고 맨 아래 레이어에 배열이 있습니다.
데이터는 다음과 같습니다. BIO는 입력 스트림이거나 양방향이 불가능한 출력 스트림이지만 NIO의 버퍼는 읽거나 쓸 수 있으며
채널은 양방향이며 반환할 수 있습니다. Linux와 같은 기본 운영 체제의 상태는 양방향입니다.
버퍼는 본질적으로 데이터를 읽고 쓸 수 있는 메모리 블록입니다. 이는 컨테이너 개체(배열 포함)로 이해될 수 있습니다. 이 개체는 사용을 더 쉽게 할 수 있는 일련의 메서드를 제공합니다. 메모리 블록, 버퍼 객체에는 버퍼 상태 변경을 추적하고 기록할 수 있는 메커니즘이 내장되어 있습니다. 채널은 파일이나 네트워크에서 데이터를 읽기 위한 채널을 제공하지만, 읽거나 쓰는 데이터는 반드시 버퍼를 거쳐야 합니다.
NIO에서 Buffer는 추상 클래스인 최상위 상위 클래스입니다.
ByteBuffer는 버퍼에 바이트 데이터를 저장합니다.
ShortBuffer는 문자열 데이터를 버퍼에 저장합니다. Area;
IntBuffer는 버퍼에 정수 데이터를 저장합니다.
DoubleBuffer는 버퍼에 소수점을 저장합니다.
capacity: 용량, 즉 수용할 수 있는 최대 데이터 양입니다. 버퍼가 생성될 때 설정되며 변경할 수 없습니다.
3.버퍼가 api를 자주 사용할 때 도입된 API
public final Buffer position (int newPositio)//이 버퍼의 위치를 설정합니다
public final intlimit( )//이 버퍼의 제한을 반환합니다
public final Buffer Limit (int newLimit)//이 버퍼의 제한을 설정합니다
public final Buffer mark( )//이 버퍼의 위치에 마크를 설정합니다
public final Buffer Reset( ) // 이 버퍼의 위치를 이전 마크 위치로 재설정
public final Bufferclear()//이 버퍼를 지웁니다. 즉, 각 마크를 초기 상태로 복원하지만 데이터가 실제로 지워지지는 않습니다. 후속 작업으로 덮어쓰게 됩니다
public final Buffer Flip( )//이 버퍼를 반전
public final Buffer rewind( )//이 버퍼 되감기
public final int 남은( )//Return 한계 사이의 요소 수를 포함한 현재 위치
public final boolean hasRemaining( ) // 현재 위치와 한계 사이에 요소가 있는지 여부를 알려줍니다.
public abstract boolean isReadOnly( ) // 이 버퍼가 있는지 여부를 알려줍니다. 읽기 전용 Buffer
JDK1.6에 도입된 API
public abstract boolean hasArray();//이 버퍼에 액세스 가능한 기본 구현 배열이 있는지 여부를 알려줍니다
public abstract Object array();/ /이 버퍼의 기본 구현 배열을 반환합니다
public abstract int arrayOffset();//이 버퍼의 기본 구현 배열에서 첫 번째 버퍼 요소의 오프셋을 반환합니다
public abstract boolean isDirect( ); / 이 버퍼가 다이렉트 버퍼인지 알려줍니다
채널 읽기 및 쓰기가 가능합니다. 동시에 스트림은 읽거나 쓸 수만 있습니다.
채널은 버퍼에서 데이터를 읽거나 버퍼에 데이터를 쓸 수 있습니다.
(2) 스트림 예를 들어 BIO의 채널은 단방향이며 FileInputStream 개체는 데이터를 읽을 수만 있고 NIO의 채널은 양방향이며 읽거나 쓸 수 있습니다.
(3) 채널은 NIO의 인터페이스입니다.
채널에서 데이터를 읽고 버퍼에 넣습니다. buffer 해당 영역의 데이터가 채널
transferTo, 현재 채널의 데이터를 대상 채널로 복사
ByteBuffer는 입력된 데이터 유형에 관계없이 해당 데이터 유형을 사용하여 꺼내야 합니다. 그렇지 않으면 BufferUnderflowException이 발생할 수 있습니다. 예외.
일반 버퍼를 읽기 전용 버퍼로 변환할 수 있습니다.
NIO는 파일을 메모리(힙 외부 메모리)에서 직접 수정할 수 있도록 하는 MappedByteBuffer도 제공하며, 파일 동기화 방법은 NIO에서 완료합니다.
NIO는 여러 버퍼(예: 버퍼 배열), 즉 분산 및 수집을 통한 읽기 및 쓰기 작업도 지원합니다.
Java의 NIO, Non-Blocking IO 방식. 하나의 스레드를 사용하여 여러 클라이언트 연결을 처리할 수 있으며 선택기를 사용하게 됩니다.
선택기는 등록된 여러 채널에서 이벤트가 발생하는지 감지할 수 있습니다. 이벤트가 발생하면 이벤트를 획득하고 그에 따라 각 이벤트를 처리합니다. 이러한 방식으로 단일 스레드만 사용하여 여러 채널을 관리할 수 있습니다. 즉, 여러 연결과 요청을 관리할 수 있습니다.
연결/채널에 실제로 읽기 또는 쓰기 이벤트가 있는 경우에만 읽기 및 쓰기가 수행되므로 시스템 오버헤드가 크게 줄어들고 연결마다 스레드를 생성하고 여러 스레드를 유지할 필요가 없습니다.
여러 스레드 간 컨텍스트 전환으로 인한 오버헤드를 방지하세요.
open();//선택기 객체 가져오기
select(long timeout);//IO 작업이 있는 경우 등록된 모든 채널을 모니터링합니다. , 해당 SelectionKey를 내부 컬렉션에 추가하고 반환합니다. 매개변수는 시간 제한을 설정하는 데 사용됩니다.
selectedKeys(); //내부 컬렉션에서 모든 SelectionKey를 가져옵니다.
NIO의 ServerSocketChannel 기능은 ServerSocket과 유사하며, SocketChannel 기능은 Socket과 유사합니다.
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(); }}
위 내용은 Java NIO 예제의 사용법과 특징을 분석합니다.의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!