Java第一阶段-22Java NIO

一、NIO

1.定义

  • NIO是面向缓冲区的流, 我们将数据和缓冲区通过一根管道连接起来,然后我们对缓冲区中的数据进行操作了

  • NIO是双向的流, 也就是说,这个缓冲区既可以存储又可以输出

  • NIO是非阻塞的, 通道建立之后,就会自动的读或取了,这就意味着一个线程可以管理多个流通道

  • NIO在解析数据的时候非常麻烦, 但适用于高并发小流量的场景,如聊天服务器

二、Buffer(缓冲区)

1.定义

因为NIO主要就是对缓冲区进行操作,所以,这个至关重要

2.分类

  • 除了boolean外的基本数据类型,都提供了对应的缓冲区

  • ByteBuffer , CharBuffer, ShortBuffer, IntBuffer, LongBuffer, FloatBuffer, DoubleBuffer

  • 常用的就是ByteBuffer , CharBuffe

3.重要属性

  • capacity : 缓冲区容量, 表示缓冲区中最大存储数据的容量, 一旦声明,不能改变

  • limit : 界限, 表示缓冲区中可以操作数据的大小(limit和limit后的数据不能进行读写)

  • position : 位置, 表示缓冲区中正在操作数据的位置

  • mark : 标记, 可以标记position的位置 ,可以使用reset()方法,将position回到标记位置

4.常用方法

  • allocate(int capacity) : 指定缓冲区的大小

  • put() : 存储数据

  • get() : 获取数据

  • flip() : 切换成输出模式

  • rewind() : 切换回输出模式的初始化位置,重复读

  • clear() : 清空缓冲区, 所有标记回到最初状态, 其中的数据并没有被清空,只是处于"被遗忘"状态

  • mark() : 标记position的位置

  • reset() : 将position回到标记的位置

5.演示

public static void main(String[] args) throws Exception {
	//创建指定容量的字节缓冲区
	ByteBuffer buffer = ByteBuffer.allocate(10);
	
	System.out.println(buffer.position());
	System.out.println(buffer.limit());
	System.out.println(buffer.capacity());
	
	System.out.println("..........put.........");
	//添加
	buffer.put("abc".getBytes());
	System.out.println(buffer.position());
	System.out.println(buffer.limit());
	System.out.println(buffer.capacity());
	
	//切换成输出模式 position位置归0 limit移动到position原来的位置
	System.out.println("..........flip.........");
	buffer.flip();
	System.out.println(buffer.position());
	System.out.println(buffer.limit());
	System.out.println(buffer.capacity());
	
	//获取当前position位置的值 position位置+1
	System.out.println("..........get.........");
	byte b = buffer.get();
	System.out.println(b);
	System.out.println(buffer.position());
	System.out.println(buffer.limit());
	System.out.println(buffer.capacity());
	
	//切换回输出模式的初始化位置,重复读
	System.out.println("..........get.........");
	buffer.rewind();
	System.out.println(buffer.position());
	System.out.println(buffer.limit());
	System.out.println(buffer.capacity());
	
	//清空缓冲区,一切还原,为再次写入做准备
	System.out.println("..........get.........");
	buffer.clear();
	System.out.println(buffer.position());
	System.out.println(buffer.limit());
	System.out.println(buffer.capacity());
	
	//表示postion的位置  使用reset()将position的位置回归到mark标记位置
	System.out.println("..........mark()和reset().........");
	buffer.put("abc".getBytes());//添加三个字节
	buffer.mark();
	buffer.put("df".getBytes());
	System.out.println(buffer.position());
	System.out.println(buffer.limit());
	System.out.println(buffer.capacity());
	buffer.reset();
	System.out.println(buffer.position());
	System.out.println(buffer.limit());
	System.out.println(buffer.capacity());
}

三、通道(Channel)

1.定义

用于读取、写入、映射和操作文件的通道,可以将程序和数据实体建立连接

java的流都提供了获取通道的方法

Channel是双向的,既可以读又可以写,而流是单向的

Channel可以进行异步的读写

对Channel的读写必须通过Buffer对象

2.常用方法

read(Buffer b):将数据写入到缓冲区

write(Buffer b):从缓冲区输出数据

四、FileChannel(不推荐使用)

1.定义

  • 用于读取、写入、映射和操作文件的通道

  • 将数据读取存储到缓冲区, 也可以将缓冲区的数据写入到本地

  • 这个类无法直接关联到文件,必须通过IO的流进行获取, 预留的方法,但是还没有启用

2.演示

public static void main(String[] args) throws Exception {
	ByteBuffer bf = ByteBuffer.allocate(1024);
	//从字节数据流中获取文本FileChannel
	FileInputStream fis = new FileInputStream("d:\\骑在银龙的背上.mp3");
	FileChannel fcr = fis.getChannel();
	
	//从字节输出流中获取文本FileChannel
	FileOutputStream fos = new FileOutputStream("d:\\音乐.mp3");
	FileChannel fcw = fos.getChannel();

	while(fcr.read(bf)!=-1){
		bf.flip();
		fcw.write(bf);
		bf.clear();
	}
	fcr.close();
	fcw.close();
	fis.close();
	fos.close();

	//快速复制
	//fcr.transferTo(0, fcr.size(), fcw);
}

五、DatagramChannel

1定义

  • 针对面向数据报套接字的可选择通道

  • 操作UDP的NIO流

2.演示

接收端

public static void main(String[] args) throws Exception {
	//获取DatagramChannel
	DatagramChannel channel = DatagramChannel.open();
	//创建socket
	channel.socket().bind(new InetSocketAddress(9999));
	//创建缓冲区
	ByteBuffer buf = ByteBuffer.allocate(100);
	
	Scanner scanner = new Scanner(System.in);
	while(true){
		//清空缓冲区,准备接收数据
		buf.clear();
		//接收网络数据
		channel.receive(buf);
		System.out.println(new String(buf.array(),0,buf.position()));
		
		
		String str = scanner.nextLine();
		//情况缓冲区,准备存入数据
		buf.clear();
		buf.put(str.getBytes());
		//将缓冲区切换成输出模式
		buf.flip();
		//发送数据
		channel.send(buf, new InetSocketAddress("127.0.0.1", 6666));
	}
}

发送端

public static void main(String[] args) throws Exception {
	//创建获取DatagramChannel
	DatagramChannel channel = DatagramChannel.open();
	//创建socket
	channel.socket().bind(new InetSocketAddress(6666));;
	
	ByteBuffer buffer = ByteBuffer.allocate(1024);
	buffer.put("黑色星期五".getBytes());
	
	SocketAddress socket = new InetSocketAddress("127.0.0.1",9999);
	
	Scanner scanner = new Scanner(System.in);
	while(true){
		
		buffer.clear();
		
		String str = scanner.nextLine();
		//将数据装入缓冲区
		buffer.put(str.getBytes());
		//将缓冲区切换为输出模式
		buffer.flip();
		channel.send(buffer, socket);
		//清空缓冲区,为接受数据做准备
		buffer.clear();
		//接收数据
		channel.receive(buffer);
		System.out.println(new String(buffer.array(),0,buffer.position()));
	}
}

六、SocketChannel和ServerSocketChannel

1、定义

  • 对应着TCP协议

  • 打开一个SocketChannel并连接到互联网上的某台服务器

  • 一个新连接到达ServerSocketChannel时,会创建一个SocketChannel

  • 用法和Socket,ServerSocket完全

2.演示

客户端

public static void main(String[] args) throws Exception {
	//打开通道
	SocketChannel channel = SocketChannel.open();
	//建立连接
	channel.connect(new InetSocketAddress("127.0.0.1", 9999));
	//设置缓冲区
	ByteBuffer buffer = ByteBuffer.allocate(1024);
	
	buffer.put("I LOVE YOU".getBytes());
	//将缓冲区设置为输出模式
	buffer.flip();
	channel.write(buffer);
}

服务端

public static void main(String[] args) throws Exception {
	//打开通道
	ServerSocketChannel channel = ServerSocketChannel.open();
	//建立服务端
	channel.socket().bind(new InetSocketAddress(9999));
	//获取socket
	SocketChannel socket = channel.accept();
	
	ByteBuffer bf = ByteBuffer.allocate(1024);
	//接收数据
	socket.read(bf);
	
	System.out.println(new String(bf.array(),0,bf.position()));
}

七、Selector

1.定义

  • Selector是一个通道管理器

  • 我们知道,NIO具有非阻塞的能力, 可以在一个线程内同时执行多个操作, 节省了线程间切换的开销

  • 但是, 当启动非阻塞的时候,输入和输出方法就完全独立运行了, 这可能导致读的时候对面还没有把信息发送过来, 写的时候,对方还没有完全准备好

  • 所有, 我们使用Selector类对通道进行管理,当某个操作准备好了之后, Selector会提醒我们,这时,我们就可以进行操作了

2.Selector监视的状态分类

 

  • SelectionKey.OP_CONNECT 连接准备就绪

  • SelectionKey.OP_ACCEPT 客户端已经连接

  • SelectionKey.OP_READ 要读的数据已经准备好

  • SelectionKey.OP_WRITE 可以进行写入了

3.常用方法

  • select() 获取所有已经准备好的通道,仅仅是这次的,上一次调用这个方法获取的通道不算

  • selectedKeys() 获取上一次select()方法获取到的通道

4.编码步骤

public static void main(String[] args) throws Exception {
	//打开通道
	SocketChannel channel = SocketChannel.open();
	//建立连接
	channel.connect(new InetSocketAddress("127.0.0.1", 9999));
	//将当前通道设置为非阻塞
	channel.configureBlocking(false);
	//获取通道选择器
	Selector selector = Selector.open();
	//将通道注册进通道选择器中,这里设置通道选择器需要监视的状态是"可读取"
	channel.register(selector, SelectionKey.OP_READ);
	
	//往服务端发送一条数据
	ByteBuffer bs = ByteBuffer.allocate(1024);
	bs.put("黑色星期五".getBytes());
	bs.flip();
	channel.write(bs);
	
	//控制循环,时刻检测通道选择器
	while(true){
		//查看通道选择监视的状态时候有通道符合要求了
		//select 方法获取所有符合状态的通道
		if(selector.select()>0){
			//遍历符合状态的通道
			for (SelectionKey key : selector.selectedKeys()) {
				//判断当前通道是否可读
				if (key.isReadable()) {
					//读取内容
					ByteBuffer buffer = ByteBuffer.allocate(1024);
					SocketChannel socket = (SocketChannel)key.channel();
					int len = socket.read(buffer);
					System.out.println(len);
					System.out.println(new String(buffer.array(),0,buffer.position()));
					//改变通道的需要监视的状态
					//key.interestOps(SelectionKey.OP_READ);
				}
				//将键从已经选择的集合中去除
				//这个里获取到的通道都是上一次select()方法已经执行到的,如果不去除的话,下一次调用select()方法就无法获取到了
				selector.selectedKeys().remove(key);
			}
		}
	}
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值