>  기사  >  Java  >  Java NIO에 대한 자세한 소개

Java NIO에 대한 자세한 소개

王林
王林앞으로
2020-10-21 16:35:211795검색

Java NIO에 대한 자세한 소개

Java NIO는 대용량 데이터 전송의 효율성을 높이기 위해 주로 Java I/O에 대한 보완으로 버퍼, 채널, 선택기의 세 가지 핵심 개념을 이해해야 합니다.

(추천 튜토리얼: java 강좌)

NIO를 배우기 전에 기본적인 네트워크 프로그래밍 지식을 갖추는 것이 가장 좋습니다

Java I/O flow

Java 네트워크 프로그래밍

Java NIO: Buffer

Channel(Channel) As NIO의 세 가지 핵심 개념(버퍼, 채널, 선택기) 중 하나로서 바이트 버퍼와 채널 반대편의 엔터티(파일 또는 소켓) 간에 데이터를 효과적으로 전송하는 데 사용됩니다(코어는 데이터 전송)

NIO 프로그래밍의 일반적인 모드는 다음과 같습니다. 전송 바이트 버퍼에 데이터를 채웁니다.-> 채널을 통해 채널 피어 파일 또는 소켓으로 보냅니다

채널 기본

채널을 사용하는 목적은 데이터 전송을 위한 것입니다. 사용하기 전에 채널을 열어야 하고 사용 후에 채널을 닫아야 합니다

채널을 열어보세요

I/O에는 파일 IO와 스트림 I/O라는 두 가지 주요 범주가 있다는 것을 알고 있습니다. 채널에는 파일 채널(FileChannel)과 소켓 채널(SocketChannel, ServerSocketChannel, DatagramChannel)도 있습니다.

소켓 채널의 경우 정적 팩토리 메소드를 사용하여 엽니다.

SocketChannel sc = SocketChannel.open();
ServerSocketChannel sc = ServerSocketChannel.open();
DatagramChannel sc = DatagramChannel.open();

파일 채널의 경우 getChannel( () on a RandomAccessFile, FileInputStream, FileOutputStream 객체 ) 메서드를 사용하여

FileInputStream in = new FileInputStream("/tmp/a.txt");
FileChannel fc = in.getChannel();

데이터 전송을 위한 채널 사용

다음 코드는 먼저 쓸 데이터를 ByteBuffer에 넣은 다음 파일 채널을 열고 데이터를 파일 채널에 버퍼링합니다.

//准备数据并放入字节缓冲区
ByteBuffer bf = ByteBuffer.allocate(1024);
bf.put("i am cool".getBytes());
bf.flip();
//打开文件通道
FileOutputStream out = new FileOutputStream("/tmp/a.txt");
FileChannel fc = out.getChannel();
//数据传输
fc.write(bf);
//关闭通道
fc.close();

채널 닫기

Socket, FileInputStream 및 기타 개체를 사용 후 닫아야 하는 것처럼 채널도 사용 후 닫아야 합니다. 열린 채널은 특정 I/O 서비스에 대한 특정 연결을 나타내며 해당 연결 상태를 캡슐화합니다. 채널이 닫히면 연결이 끊어지고 아무 것도 연결되지 않습니다.

차단 및 비차단 모드

채널에는 차단 및 비차단의 두 가지 작동 모드가 있습니다. 비차단 모드의 채널은 요청된 작업이 즉시 완료되거나 다음을 나타내는 결과가 반환됩니다. 작업이 수행되지 않았습니다(구체적으로 소켓 채널 설명 참조). 스트림 지향 채널만 비차단 모드를 사용할 수 있습니다

파일 채널

파일 채널은 파일에 액세스하는 데 사용되며 RandomAccessFile, FileInputStream 및 FileOutputStream 객체에서 getChannel() 메서드를 호출하여 얻습니다. getChannel 메소드를 호출하면 파일 객체와 동일한 액세스 권한을 가진 동일한 파일에 연결된 FileChannel 객체가 반환됩니다.

파일 액세스

파일 채널을 사용하는 목적은 파일을 읽고 쓰는 것입니다. 채널의 읽기 및 쓰기 API는 다음과 같습니다.

public abstract int read(ByteBuffer dst) throws IOException;
public abstract int write(ByteBuffer src) throws IOException;

다음은 파일 읽기에 대한 데모입니다

//打开文件channel
RandomAccessFile f = new RandomAccessFile("/tmp/a.txt", "r");
FileChannel fc = f.getChannel();
//从channel中读取数据,直到文件尾
ByteBuffer bb = ByteBuffer.allocate(1024);
while (fc.read(bb) != -1) {
;
}
//翻转(读之前需要先进行翻转)
bb.flip();
StringBuilder builder = new StringBuilder();
//把每一个字节转为字符(ascii编码)
while (bb.hasRemaining()) {
builder.append((char) bb.get());
}
System.out.println(builder.toString());

가 있습니다. 위 데모의 문제점: 우리는 바이트를 읽을 수만 있고 애플리케이션에서 디코딩할 수 있습니다. 물론 채널 도구 클래스를 통해 채널을 리더 및 라이터로 패키징하여 이 문제를 해결할 수 있습니다. 문자를 조작하는 Java I/O 스트림 모드 작성자

파일 채널 위치 및 파일 구멍

파일 채널 위치(위치)는 일반 파일의 위치를 ​​결정합니다. 다음에 읽거나 씁니다

파일 끝을 넘어서 읽으면 데이터는 -1(파일 EOF)을 반환합니다.

파일 끝을 넘어서는 위치에 데이터를 쓰면 파일 홀이 발생합니다. 예를 들어 현재 파일에 10이 있습니다. 바이트이지만 현재 위치=20에 데이터를 쓰면 결과적으로 10에서 20 사이의 위치에는 데이터가 없습니다. 이것은 파일 홀입니다.

force 연산

force 연산은 채널을 강제로 모든 수정 사항을 디스크 파일에 즉시 적용합니다(시스템 가동 중지 시간 및 수정 손실 방지)

public abstract void force(boolean metaData) throws IOException;

메모리 파일 매핑

FileChannel은 열려 있는 파일과 특수 유형 간의 가상 메모리 매핑을 설정할 수 있는 map() 메서드를 제공합니다. ByteBuffer(MappedByteBuffer)입니다.

map 메소드에서 반환하는 MappedByteBuffer 객체는 다이렉트 버퍼이므로 MappedByteBuffer를 통해 파일을 조작하는 것이 매우 효율적입니다. (특히 데이터 전송량이 많은 경우)

MappedByteBuffer 사용

MappedByteBuffer를 통해 파일 읽기

FileInputStream in = new FileInputStream("/tmp/a.txt");
FileChannel fc = in.getChannel();
MappedByteBuffer mbb = fc.map(MapMode.READ_ONLY, 0, fc.size());
StringBuilder builder = new StringBuilder();
while (mbb.hasRemaining()) {
  builder.append((char) mbb.get());
}
System.out.println(builder.toString());

MappedByteBuffer 세 가지 모드

READ_ONLY

READ_WRITE

PRIVATE

읽기 전용 모드와 읽기-쓰기 모드는 이해하기 쉽습니다. PRIVATE 모드에서는 쓰기 작업이 임시 버퍼를 쓰지만 실제로는 쓰지 않습니다. 파일. (기록 중 복사 아이디어)

소켓 채널

소켓 채널은 비차단 모드에서 실행될 수 있으며 선택 사항입니다. 이 두 가지 점을 사용하면 더 이상 네트워크 프로그래밍을 위해 각 소켓 연결에 대한 스레드를 만들 필요가 없습니다. 하나의 스레드를 사용하여 수백 또는 수천 개의 소켓 연결을 관리합니다.

모든 소켓 채널은 인스턴스화될 때 개체의 소켓 개체를 생성합니다. 소켓 채널은 프로토콜 관련 작업을 담당하지 않습니다. 프로토콜 관련 작업은 피어 소켓 개체에 위임됩니다(예: SocketChannel 개체는 소켓에 위임됨). 객체)

非阻塞模式

相较于传统Java Socket的阻塞模式,SocketChannel提供了非阻塞模式,以构建高性能的网络应用程序

非阻塞模式下,几乎所有的操作都是立刻返回的。比如下面的SocketChannel运行在非阻塞模式下,connect操作会立即返回,如果success为true代表连接已经建立成功了, 如果success为false, 代表连接还在建立中(tcp连接需要一些时间)。

 //打开Socket通道
 SocketChannel ch = SocketChannel.open();
 //非阻塞模式
 ch.configureBlocking(false);
 //连接服务器 
 boolean success = ch.connect(InetSocketAddress.createUnresolved("127.0.0.1", 7001));
 //轮训连接状态, 如果连接还未建立就可以做一些别的工作
 while (!ch.finishConnect()){
    //dosomething else
 }
 //连接建立, 做正事
 //do something;

ServerSocketChannel

ServerSocketChannel与ServerSocket类似,只是可以运行在非阻塞模式下

下为一个通过ServerSocketChannel构建服务器的简单例子,主要体现了非阻塞模式,核心思想与ServerSocket类似

ServerSocketChannel ssc = ServerSocketChannel.open();
ssc.configureBlocking(false);
ssc.bind(new InetSocketAddress(7001));
while (true){
  SocketChannel sc = ssc.accept();
  if(sc != null){
    handle(sc);
  }else {
    Thread.sleep(1000);
  }
}

SocketChannel 与 DatagramChannel

SocketChannel 对应 Socket, 模拟TCP协议;DatagramChannel对应DatagramSocket, 模拟UDP协议

二者的使用与SeverSocketChannel大同小异,看API即可

工具类

文体通道那里我们提到过, 通过只能操作字节缓冲区, 编解码需要应用程序自己实现。如果我们想在通道上直接操作字符,我们就需要使用工具类Channels,工具类Channels提供了通道与流互相转换、通道转换为阅读器书写器的能力,具体API入下

//通道 --> 输入输出流
public static OutputStream newOutputStream(final WritableByteChannel ch);
public static InputStream newInputStream(final AsynchronousByteChannel ch);
//输入输出流 --> 通道
public static ReadableByteChannel newChannel(final InputStream in);
public static WritableByteChannel newChannel(final OutputStream out);
//通道  --> 阅读器书写器
public static Reader newReader(ReadableByteChannel ch, String csName);
public static Writer newWriter(WritableByteChannel ch, String csName);

通过将通道转换为阅读器、书写器我们就可以直接在通道上操作字符。

    RandomAccessFile f = new RandomAccessFile("/tmp/a.txt", "r");
  FileChannel fc = f.getChannel();
  //通道转换为阅读器,UTF-8编码
  Reader reader = Channels.newReader(fc, "UTF-8");
  int i = 0, s = 0;
  char[] buff = new char[1024];
  while ((i = reader.read(buff, s, 1024 - s)) != -1) {
    s += i;
  }
  for (i = 0; i < s; i++) {
    System.out.print(buff[i]);
  }

总结

通道主要分为文件通道和套接字通道。

对于文件操作:如果是大文件使用通道的文件内存映射特性(MappedByteBuffer)来有利于提升传输性能, 否则我更倾向传统的I/O流模式(字符API);对于套接字操作, 使用通道可以运行在非阻塞模式并且是可选择的,利于构建高性能网络应用程序。

相关推荐:java入门

위 내용은 Java NIO에 대한 자세한 소개의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

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