欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

JavaNIO

程序员文章站 2022-06-02 23:47:47
...

1、javaNIO概述

Java NIO核心 Channels、Buffers 、Selectors

类似Channels就是出水口、Selectors水闸,Buffers就是蓄水池

水闸可以选择不同出水口出水

2、Channel通过

如果要取水的话,从channel 读取到 Buffers(蓄水池)

channel的类型

  • FileChannel    文件
  • DatagramChannel  UDP
  • SocketChannel  TCP客户端
  • ServerSocketChannel   TCP服务端

3、Buffer (蓄水池)

既可以进水也可以放水

3.1 Buffer的基本用法

写入数据到Buffer

调用flip()方法

从Buffer中读取数据

调用clear()方法或者compact方法

RandomAccessFile aFile = new RandomAccessFile("data/nio-data.txt", "rw");
FileChannel inChannel = aFile.getChannel();

//create buffer with capacity of 48 bytes
ByteBuffer buf = ByteBuffer.allocate(48);

int bytesRead = inChannel.read(buf); //read into buffer.
while (bytesRead != -1) {

  buf.flip();  //make buffer ready for read

  while(buf.hasRemaining()){
      System.out.print((char) buf.get()); // read 1 byte at a time
  }

  buf.clear(); //make buffer ready for writing
  bytesRead = inChannel.read(buf);
}
aFile.close();

Buffer的capacity,position和limit

capacity:容量

position:写的时候当前位置,从0开始,如果又写转为读,position重置为0

limit:表示这个容量你能够用多少(写的时候就是capacity,读的时候就是写的最后位置)

Buffer的分配

ByteBuffer buf = ByteBuffer.allocate(48); 分配48个字节空间

向Buffer中写数据

int bytesRead = inChannel.read(buf); //从Channel写到Buffer

buf.put(127); //通过put的方法

flip()方法

切换读模式,主要是改变position重置为0,limit为写的最后的位置(写多少读多少)

从Buffer中读取数据

int bytesWritten = inChannel.write(buf);  //通过Channel的方法

byte aByte = buf.get(); 

rewind()方法

Buffer.rewind()将position,重读Buffer所有数据,(类似通电话的时候,没听清,重新说一遍)

clear()与compact()方法

表示读完了,进入写的状态

clear() 方法,position将会设回0,limit被设置成capacity的值

compact()方法,未被读取的数据不会被清除掉

mark()与reset()方法

mark() 类似设置书签一样,这里是标记position

reset() 重置到position位置

equals()与compareTo()方法

当满足下列条件时,表示两个Buffer相等:

  • 1、有相同的类型(byte char、int等)
  • 2、Buffer中剩余byte、char等的个数相等
  • 3、Buffer所剩余的byte、char等都相同

4、Scatter/Gather

从Channel中数据放入到多个Buffer 称为Scatter

从多个Buffer获取数据写入同一个渠道

5、通道之前的数据传输

transferFrom() 就是从一个渠道数据进入另一个渠道

RandomAccessFile fromFile = new RandomAccessFile("fromFile.txt", "rw");
FileChannel      fromChannel = fromFile.getChannel();

RandomAccessFile toFile = new RandomAccessFile("toFile.txt", "rw");
FileChannel      toChannel = toFile.getChannel();

long position = 0;
long count = fromChannel.size();

toChannel.transferFrom(position, count, fromChannel);

transferTo() 刚好相反

RandomAccessFile fromFile = new RandomAccessFile("fromFile.txt", "rw");
FileChannel      fromChannel = fromFile.getChannel();

RandomAccessFile toFile = new RandomAccessFile("toFile.txt", "rw");
FileChannel      toChannel = toFile.getChannel();

long position = 0;
long count = fromChannel.size();

fromChannel.transferTo(position, count, toChannel);

6、Selector

Selector的创建

Selector selector = Selector.open();

向Selector注册通道

channel.configureBlocking(false);  //非阻塞式

SelectionKey key = channel.register(selector, SelectionKey.OP_READ);

可以监听四种不同类型的事件

1、Connect、2.Accept、3、Read、4、Write

SelectionKey

当向Selector注册Channel时,register()方法会返回一个SelectionKey对象,包含一些属性:interest集合、ready集合、Channel、Selector、附加的对象(可选)

interest集合

int interestSet = selectionKey.interestOps();

boolean isInterestedInAccept  = (interestSet & SelectionKey.OP_ACCEPT) == SelectionKey.OP_ACCEPT;
boolean isInterestedInConnect = interestSet & SelectionKey.OP_CONNECT;
boolean isInterestedInRead    = interestSet & SelectionKey.OP_READ;
boolean isInterestedInWrite   = interestSet & SelectionKey.OP_WRITE;

ready集合

int readySet = selectionKey.readyOps();

Channel + Selector

Channel  channel  = selectionKey.channel();
Selector selector = selectionKey.selector();
Set selectedKeys = selector.selectedKeys();
Iterator keyIterator = selectedKeys.iterator();
while(keyIterator.hasNext()) {
    SelectionKey key = keyIterator.next();
    if(key.isAcceptable()) {
        // a connection was accepted by a ServerSocketChannel.
    } else if (key.isConnectable()) {
        // a connection was established with a remote server.
    } else if (key.isReadable()) {
        // a channel is ready for reading
    } else if (key.isWritable()) {
        // a channel is ready for writing
    }
    keyIterator.remove();
}

wakeUp()

某个线程调用select()方法阻塞了,只要wakeUp()立马会返回

close()

用完Selector后调用其close()方法会关闭该Selector,不会关闭通道

Selector selector = Selector.open();
channel.configureBlocking(false);
SelectionKey key = channel.register(selector, SelectionKey.OP_READ);
while(true) {
  int readyChannels = selector.select();
  if(readyChannels == 0) continue;
  Set selectedKeys = selector.selectedKeys();
  Iterator keyIterator = selectedKeys.iterator();
  while(keyIterator.hasNext()) {
    SelectionKey key = keyIterator.next();
    if(key.isAcceptable()) {
        // a connection was accepted by a ServerSocketChannel.
    } else if (key.isConnectable()) {
        // a connection was established with a remote server.
    } else if (key.isReadable()) {
        // a channel is ready for reading
    } else if (key.isWritable()) {
        // a channel is ready for writing
    }
    keyIterator.remove();
  }
}


7、FileChannel

FileChannel 无法设置非阻塞模式,它总是运行在阻塞模式下

打开FileChannel

RandomAccessFile aFile = new RandomAccessFile("data/nio-data.txt", "rw");
FileChannel inChannel = aFile.getChannel();

从FileChannel读取数据

ByteBuffer buf = ByteBuffer.allocate(48);
int bytesRead = inChannel.read(buf);

向FileChannel写数据

String newData = "New String to write to file..." + System.currentTimeMillis();

ByteBuffer buf = ByteBuffer.allocate(48);
buf.clear();
buf.put(newData.getBytes());

buf.flip();

while(buf.hasRemaining()) {
	channel.write(buf);
}

关闭FileChannel

channel.close();

FileChannel的position方法

long pos = channel.position();
channel.position(pos +123);

FileChannel的size方法

long fileSize = channel.size();

FileChannel的truncate方法

截取文件

channel.truncate(1024);

FileChannel的force方法

强制将内存的数据写入到磁盘中

channel.force(true);

8、SocketChannel

打开 SocketChannel

SocketChannel socketChannel = SocketChannel.open();
socketChannel.connect(new InetSocketAddress("http://jenkov.com", 80));

关闭 SocketChannel

关闭 SocketChannel

从 SocketChannel 读取数据

ByteBuffer buf = ByteBuffer.allocate(48);
int bytesRead = socketChannel.read(buf);

写入 SocketChannel

String newData = "New String to write to file..." + System.currentTimeMillis();

ByteBuffer buf = ByteBuffer.allocate(48);
buf.clear();
buf.put(newData.getBytes());

buf.flip();

while(buf.hasRemaining()) {
    channel.write(buf);
}

非阻塞模式

connect()

socketChannel.configureBlocking(false);
socketChannel.connect(new InetSocketAddress("http://jenkov.com", 80));

while(! socketChannel.finishConnect() ){
    //wait, or do something else...
}

write()

read()

9、ServerSocketChannel

ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();

serverSocketChannel.socket().bind(new InetSocketAddress(9999));

while(true){
    SocketChannel socketChannel =
            serverSocketChannel.accept();

    //do something with socketChannel...
}

打开 ServerSocketChannel

ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();

关闭 ServerSocketChannel

serverSocketChannel.close();

监听新进来的连接

while(true){
    SocketChannel socketChannel =
            serverSocketChannel.accept();

    //do something with socketChannel...
}

非阻塞模式

ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();

serverSocketChannel.socket().bind(new InetSocketAddress(9999));
serverSocketChannel.configureBlocking(false);

while(true){
    SocketChannel socketChannel =
            serverSocketChannel.accept();

    if(socketChannel != null){
        //do something with socketChannel...
    }
}

10、Java NIO DatagramChannel

打开 DatagramChannel

DatagramChannel channel = DatagramChannel.open();
channel.socket().bind(new InetSocketAddress(9999));

接收数据

ByteBuffer buf = ByteBuffer.allocate(48);
buf.clear();
channel.receive(buf);

发送数据

String newData = "New String to write to file..." + System.currentTimeMillis();

ByteBuffer buf = ByteBuffer.allocate(48);
buf.clear();
buf.put(newData.getBytes());
buf.flip();

int bytesSent = channel.send(buf, new InetSocketAddress("jenkov.com", 80));

连接到特定的地址

channel.connect(new InetSocketAddress("jenkov.com", 80));
int bytesRead = channel.read(buf);
int bytesWritten = channel.write(but);


11、Pipe  (线程之间的单向传输数据)

创建管道

Pipe pipe = Pipe.open();

向管道写数据

Pipe.SinkChannel sinkChannel = pipe.sink();

String newData = "New String to write to file..." + System.currentTimeMillis();
ByteBuffer buf = ByteBuffer.allocate(48);
buf.clear();
buf.put(newData.getBytes());

buf.flip();

while(buf.hasRemaining()) {
    sinkChannel.write(buf);
}

从管道读取数据

Pipe.SourceChannel sourceChannel = pipe.source();
ByteBuffer buf = ByteBuffer.allocate(48);

int bytesRead = sourceChannel.read(buf);

12、Java NIO和IO

java NIO 和IO的主要区别

IO                           NIO

面向流       面向缓冲

阻塞IO      非阻塞IO

    选择器

原文地址:http://tutorials.jenkov.com/java-nio/index.html

相关标签: nio java