NIO和Netty全文
Buffer
- 缓冲区,数据从通道读入缓冲区,从缓冲区写入到通道中,可以理解为是一个容器对象
- 本质上一块可以写入数据和读取数据的内存
flip()模式转换,写转换为读,将position设为0,limit为原先的positionrewind()仅仅将postion设置为0,可以重新读compact()只清除读过的数据,将未读的数据拷贝到起始处,将position设置到最后一个未读元素的后面mark()标记position;reset()恢复position- 存放数据的数组
- 类型化put时,放入的类型和取出的类型要相等,否则抛出异常
BufferUnderflowException
ByteBuffer bb = ByteBuffer.allocate(1024);
bb.putInt(10);
bb.putLong(9);
bb.putChar('a');
bb.putShort((short) 1);
bb.flip();
System.out.println(bb.getInt());
System.out.println(bb.getLong());
System.out.println(bb.getChar());
读
RandomAccessFile raf = new RandomAccessFile("/Users/mzx/Desktop/java/nio/test1.txt", "rw");
FileChannel fc = raf.getChannel();
ByteBuffer bb = ByteBuffer.allocate(1);
//读,向buffer中写
int len = fc.read(bb);
while (len != -1) {
System.out.println("开始读");
//模式转换
bb.flip();
while (bb.hasRemaining()) {
System.out.println((char)bb.get());
}
bb.clear();
//循环读
len = fc.read(bb);
}
fc.close();
raf.close();
IntBuffer
IntBuffer ib = IntBuffer.allocate(1024);
//往buffer中放数据
for (int i = 0; i < ib.capacity(); i++) {
int j = 2 * (i + 1);
//类似加入数组中的效果
ib.put(j);
}
//模式转换,几个指针切换
ib.flip();
//获取
while (ib.hasRemaining()) {
//每次get,`position`指针后移动
int value = ib.get();
System.out.println(value);
}
- get方法
capacity、position和limit
capacity容量大小,可容纳的最大数据量position
- 写数据时,表示数据写入的当前位置,初始值为0;写入后,移动到下一个可插入的单元
- 读数据时,读取数据的位置;读取后,移动到下一个可读取的单元
limit
- 写数据时,表示可对buffer写入多少个数据,就是
capacity,写入的结束为止- 读数据时,表示有多少可读的数据,对应写模式下的
position
- mark,标记,用于记录当前position的位置
mark和reset
int temp = 0;
boolean flag = false;
while (ib.hasRemaining()) {
temp ++;
int value = ib.get();
System.out.println(value);
if (temp == 100) {
ib.mark();
System.out.println("ib.mark()" + ib.mark());
}
//只恢复一次
if (temp == 200 && !flag) {
ib.reset();
flag = !flag;
System.out.println("ib.reset()" + ib.reset());
}
}
- mark
- reset
缓冲区
- 缓冲区分片
在现有缓冲区上,创建一个子缓冲区,底层数据共享
ByteBuffer bb = ByteBuffer.allocate(10);
for (int i = 0; i < bb.capacity(); i++) {
bb.put((byte) i);
}
//创建子缓冲区 [3,7)
bb.position(3);
bb.limit(7);
ByteBuffer slice = bb.slice();
System.out.println(slice.capacity());
//改变子缓冲区内容
for (int i = 0; i < slice.capacity(); i++) {
byte b = slice.get(i);
b *= 10;
slice.put(i, b);
}
//将position设置为0
bb.rewind();
//将limit设置为容量
bb.limit(bb.capacity());
while (bb.remaining() > 0) {
System.out.println(bb.get());
}
- 只读缓冲区,
HeapByteBufferR
获得一个与原缓冲区相同的缓冲区,数据共享,但是只能读取
ByteBuffer bb = ByteBuffer.allocate(10);
for (int i = 0; i < bb.capacity(); i++) {
bb.put((byte) i);
}
//创建只读缓存区
ByteBuffer readOnly = bb.asReadOnlyBuffer();
for (int i = 0; i < bb.capacity(); i++) {
byte b = bb.get(i);
b *= 10;
bb.put(i, b);
}
readOnly.flip();
while (readOnly.remaining() > 0) {
System.out.println(readOnly.get());
}
- 直接缓冲区,加快io速度,减少中间操作
String infile = "/Users/mzx/Desktop/java/训练营笔记.pdf";
FileInputStream fis = new FileInputStream(infile);
FileChannel fic = fis.getChannel();
String tofile = "/Users/mzx/Desktop/java/nio/训练营笔记.pdf";
FileOutputStream fos = new FileOutputStream(tofile);
FileChannel foc = fos.getChannel();
ByteBuffer bb = ByteBuffer.allocateDirect(1024);
while(true) {
//底层也是移动position和limit
//position到0,limit到capacity
bb.clear();
int len = fic.read(bb);
if (len == -1) {
break;
}
bb.flip();
foc.write(bb);
}
- 内存映射文件I/O,读写文件的一种方式,只将文件中实际读取或者写入的部分才会映射到内存(
队外内存),操作系统不需要拷贝一次
刷新后,才会改变
int start = 5;
int size = 1024;
RandomAccessFile raf = new RandomAccessFile("/Users/mzx/Desktop/java/nio/test3.txt", "rw");
FileChannel fc = raf.getChannel();
//截取文件start开始,size长度的内容进内存
MappedByteBuffer mbb = fc.map(FileChannel.MapMode.READ_WRITE, start, size);
//改变内存中,索引位置的内容
mbb.put(0, (byte)97);
mbb.put(4,(byte) 122);
fc.close();
raf.close();
实际类型
分散和聚合
- Scattering,分散,将数据写入到buffer,可以采用buffer数组,依次写入
- Gathering,聚合,从buffer读取数据时,可以采用buffer数组,依次读
ServerSocketChannel ssc = ServerSocketChannel.open();
ssc.socket().bind(new InetSocketAddress(7777));
//创建buffer数组
ByteBuffer[] bbs = new ByteBuffer[2];
bbs[0] = ByteBuffer.allocate(5);
bbs[1] = ByteBuffer.allocate(3);
//等待客户端
SocketChannel sc = ssc.accept();
//循环读取
int messageLength = 8; //假定接收8个
while (true) {
int byteRead = 0;
while (byteRead < messageLength) {
//读到bbs中
long l = sc.read(bbs);
byteRead += l;
System.out.println("byteRead: " + byteRead);
Arrays.asList(bbs).stream().map(bb -> "position: " + bb.position() + " limit: " + bb.limit()).forEach(System.out::println);
}
//回显到客户端
Arrays.asList(bbs).forEach(bb -> bb.flip());
long byteWrite = 0;
while (byteWrite < messageLength) {
//写到bbs中
long l = sc.write(bbs);
byteWrite += l;
System.out.println("byteWrite: " + byteWrite);
}
Arrays.asList(bbs).forEach(ByteBuffer::clear);
System.out.println("byteRead: " + byteRead + " byteWrite: " + byteWrite);
}