JavaNIO
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写到Bufferbuf.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() 类似设置书签一样,这里是标记positionreset() 重置到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
无 选择器