Nio最核心的组成部分就是Selector与Channe,还有Buffer,一个选择器中可以有多个通道和缓冲。
Buffer 是一个顶级抽象类,所在java.nio.Buffer 中,里面提供了一些属性和方法来提供缓冲的功能操作。
private int mark = -1; //标记
private int position = 0;//位置
private int limit;//最大的长度
private int capacity;//容量
buffer 通过以上变量来实现 读写的转换,后面我们在代码中调试会看出来。
//获取当前Buffer的容量
public final int capacity() {
return capacity;
}
//获取当前Buffer数组的位置
public final int position() {
return position;
}
获取当前Buffer数组最大长度
public final int limit() {
return limit;
}
//将缓冲区的位置设置到标记变量中
public final Buffer mark() {
mark = position;
return this;
}
//读写转换,把当前位置设置到最大数量变量中,然后位置设置为0,在接下的读或写操作的时候,
//再次进行位置的计数
public final Buffer flip() {
limit = position;
position = 0;
mark = -1;
return this;
}
//读取buffer中的数据
public abstract Object array();
//清空此缓冲区。位置设置为零,标记被丢弃
public final Buffer rewind() {
position = 0;
mark = -1;
return this;
}
以上大概就是这buffer中的部分抽象类方法,接下来我们来看看下面的子类:
IntBuffer,FloatBuffer,CharBuffer,DoubleBuffer,ShortBuffer,LongBuffer,ByteBuffer
/**
* 创建一个buff 大小为5
*/
IntBuffer allocate = IntBuffer.allocate(5);
//或者可以使用上面的字类去创建对应类型的buffer
//FloatBuffer floatbuffer = FloatBuffer.allocate(5);
//使用里面常用见的方法:
for(int i=0;i
上面就是我们非常常见的buffer 使用了,put 一个数据进去,然后get 取出一个值,值得注意的就是一定要使用
flip() 去进行读写的转换,如果不加,那么是无法读取出数据。
当我们执行创建buffer的时候,可以看到里面的属性 limit 最大数是5,说明后面添加的数据不能超过5个,最大的容量也是5
当我们put一个值进去的时候,发现 position的值加了1个数,说明我们添加进去了一个值,可以理解这是数组的下标一样的东西。
当我们全部添加完成的时候,可以看到position的值是5,与创建的容量一致的时候,这时候我们执行 flip 方法来读写转换的时候,会发生什么呢?
可以看到,指针数被设置了为0,然后在读取数据的时候,他会再次作为读取的指针,一直读取到等limit等值的时候,就不会再次读取数据了,
读取完毕。
1.使用buffer的时候,存进去的数据类型和取出来的类型顺序必须保持一致,否则会抛出异常 BufferUnderflowException
代码实例:
public static void main(String[] args) {
ByteBuffer allocate = ByteBuffer.allocate(64);
allocate.putInt(100);
allocate.putLong(9);
allocate.putChar('尚');
allocate.putShort((short)4);
allocate.flip();
//这里按照上面的顺序get 出来是没有问题的,如果顺序不对,那么就会抛出异常 BufferUnderflowException
System.out.println(allocate.getShort());
System.out.println(allocate.getInt());
System.out.println(allocate.getLong());
System.out.println(allocate.getLong());
}
2.使用asReadOnlyBuffer() 可以把 buffer 转为只读的buffer,无法修改的buffer
ByteBuffer allocate = ByteBuffer.allocate(64);
//buff 转为只读的
ByteBuffer byteBuffer = allocate.asReadOnlyBuffer();
// byteBuffer.putInt(1); 如果继续写 会抛出 ReadOnlyBufferException
通过最重要的字类是 NetworkChannel,WritableByteChannel。
channel 接口中只有两个方法,我们来看一下:
//当前通道是否开启
public boolean isOpen();
//关闭当前通道
public void close() throws IOException;
1.通过 FileChannel 通道,把一个内容写入到文件中:
/**
* @Author qrn
* @Date 2021/5/5 下午10:50
* @Version 1.0
* @blog https://blog.csdn.net/qq_41971087
* FileChannel 写入数据到文件中,demo实例
* 1.读取文件
* 2.获取通道
* 3.定义缓冲,把要写入文件的数据,放到缓冲中去
* 4.把缓冲中到数据写入到通道中
* 5.关闭流
*
*/
public class NioFileChannel {
public static void main(String[] args) throws Exception{
String hell= "hello,你好netty";
//获取文件
FileOutputStream fileOutputStream = new FileOutputStream("/Users/qurenneng/hello.txt");
//获取nio中到通道
FileChannel channel = fileOutputStream.getChannel();
//定义缓冲区,bytebuffrt
ByteBuffer allocate = ByteBuffer.allocate(1024);
//缓冲区中写入字节数据
allocate.put(hell.getBytes());
//读写转换
allocate.flip();
//写入到file 通道中去
channel.write(allocate);
//关闭流
fileOutputStream.close();
}
}
2.读取文件中的内容,打印在控制台:
/**
* @Author qrn
* @Date 2021/5/5 下午11:09
* @Version 1.0
* @blog https://blog.csdn.net/qq_41971087
* 使用 nio fiechannel 读取文件数据打印出来:
*/
public class NioFileChannelRean {
public static void main(String[] args) throws Exception{
File file = new File("/Users/qurenneng/hello.txt");
//读取到文件
FileInputStream fileInputStream = new FileInputStream(file);
FileChannel channel = fileInputStream.getChannel();
ByteBuffer allocate = ByteBuffer.allocate((int) file.length());
channel.read(allocate);
System.out.println(new String(allocate.array()));
}
}
3.文件拷贝,从一个文件读取内容后,写入到另外一个文件当中,使用同一个buffer,读写转换。
/**
* @Author qrn
* @Date 2021/5/7 下午10:46
* @Version 1.0
* @blog https://blog.csdn.net/qq_41971087
* nio 实例 文件拷贝:
*/
public class NioFileChannelCobe {
public static void main(String[] args) throws Exception{
FileInputStream fileInputStream = new FileInputStream("1");
FileChannel fileChannel = fileInputStream.getChannel();
FileOutputStream fileOutputStream = new FileOutputStream("2");
FileChannel channel = fileOutputStream.getChannel();
ByteBuffer allocate = ByteBuffer.allocate(512);
while (true){
allocate.clear();//清空缓存
int read = fileChannel.read(allocate);
if(read == -1)break;
allocate.flip(); //反转
channel.write(allocate);
}
fileInputStream.close();
}
}
4.使用transferFrom 完成图片的复制:
public class NioFileChannelimage {
public static void main(String[] args) throws Exception{
FileInputStream fileInputStream = new FileInputStream("a.png");
FileChannel fileChannel = fileInputStream.getChannel();
FileOutputStream fileOutputStream = new FileOutputStream("b.png");
FileChannel channel = fileOutputStream.getChannel();
//文件拷贝:拷贝那个通道 从什么位置开始,文件有多大
channel.transferFrom(fileChannel,0,fileChannel.size());
fileChannel.close();
channel.close();
fileInputStream.close();
fileOutputStream.close();
}
}
选择器,是一个顶级的抽象类,里面最重要的就是 SelectionKey,大致可以理解为,没个通道注册进去选择器的时候,都是会得到一个
SelectionKey在选择器当中,这样我们就可以根据SelectionKey 来获取channel,来看下SelectionKey中的方法:
//返回创建了当前SelectionKey 的通道
public abstract SelectableChannel channel();
//读的
public static final int OP_READ = 1 << 0;
//写的
public static final int OP_WRITE = 1 << 2;
// 提交的
public static final int OP_CONNECT = 1 << 3;
//正在连接
public static final int OP_ACCEPT = 1 << 4;
//通过这写常量的定义,就能知道,当前通道是读,还是写,还在连接中,等等,这样我们就可以在通信中,获取到连接用户的状态。
于此在Selector中,会有一个set 去存储 SelectionKey,去重。
public abstract Set keys();
/**
* @Author qrn
* @Date 2021/5/9 下午3:29
* @Version 1.0
* @blog https://blog.csdn.net/qq_41971087
*
* nio 服务端:
*/
public class NioService {
public static void main(String[] args) throws Exception{
ServerSocketChannel open = ServerSocketChannel.open();
Selector selector = Selector.open();
open.socket().bind(new InetSocketAddress(6666));
//设置为非阻塞
open.configureBlocking(false);
open.register(selector, SelectionKey.OP_ACCEPT);
while (true){
if(selector.select(1000) == 0){
System.out.println("服务器等待了1秒,无连接");
continue;
}
Set selectionKeys = selector.selectedKeys();
Iterator iterator = selectionKeys.iterator();
while (iterator.hasNext()){
SelectionKey next = iterator.next();
if(next.isAcceptable()){
SocketChannel accept = open.accept();
accept.configureBlocking(false);//非阻塞的
accept.register(selector,SelectionKey.OP_READ, ByteBuffer.allocate(1024));
}
if(next.isReadable()){
SocketChannel channel = (SocketChannel)next.channel();
ByteBuffer attachment = (ByteBuffer)next.attachment();
channel.read(attachment);
System.out.println(new String(attachment.array()));
}
//删除key
iterator.remove();
}
}
}
}
/**
* @Author qrn
* @Date 2021/5/9 下午4:02
* @Version 1.0
* @blog https://blog.csdn.net/qq_41971087
* nio 客户端
*/
public class NioClient {
public static void main(String[] args) throws Exception{
SocketChannel socketChannel = SocketChannel.open();
socketChannel.configureBlocking(false);
InetSocketAddress inetSocketAddress = new InetSocketAddress("127.0.0.1",6666);
//连接服务器
if(!socketChannel.connect(inetSocketAddress)){
while (!socketChannel.finishConnect()){
System.out.println("因为连接需要时间");
}
}
String string = "小明";
ByteBuffer wrap = ByteBuffer.wrap(string.getBytes());
socketChannel.write(wrap);
}
}
如果这篇文章,有帮助到大家的,请给作者一个一键三连,谢谢。