NIO网络编程


三大核心组件:buffer缓冲区,channel通道,selector选择器

Buffer缓冲区

缓冲区本质上是一个可以写入数据的内存块(类似数组),然后可以再次读取。此内存块包含在NIO Buffer对象中,该对象提供了一组方法,可以更轻松地使用内存块。
相比较直接对数组的操作,Buffer API更加容易操作和管理。

使用Buffer进行数据写入与读取,需要进行如下四个步骤:
1、将数据写入缓冲区
2、调用buffer.flip(),转换成读取模式
3、缓冲区读取数据
4、调用buffer.clear()或buffer.compact()清除缓冲区

Buffer工作原理

三个重要属性:
1、capacity容量:作为一个内存块,Buffer具有一定的固定大小,也成为“容量”。
2、position位置:写入模式时代表写数据的位置。读取模式时代表读取数据的位置。
3、limit限制:写入模式,限制等于buffer的容量。读取模式下,limit等于写入的数据量。
在这里插入图片描述

//需要使用ByteBuffer创建
//ByteBuffer源码:堆内存申请缓冲区,是byte[]数组形式,本质上就是对数组的封装。
ByteBuffer byteBuffer = ByteBuffer.allocate(4);//创建了容量为4的ByteBuffer对象

byteBuffer.put((byte) 1);//写入数据

byteBuffer.flip();//读模式的转换,不转换读模式,会导致postion出问题,读取会出问题

byte a = byteBuffer.get();//读取数据

byteBuffer.compact();//仅清除已阅读的数据,转为写入模式

//byteBuffer.clear();清除整个缓冲区

ByteBuffer内存类型

ByteBuffer为性能关键型代码提供了直接内存(direct堆外)和非直接内存(heap堆)两种实现。使用上是没有区别,但是需要显示申请。堆外内存获取的方式:

ByteBuffer directByteBuffer = ByteBuffer.allocateDirect(noBytes);

好处:
1、进行网络IO或者文件IO时,比heapBuffer少一次拷贝。(file/socket ---- OS memory ---- jvm heap)
GC会移动对象内存,在写file或socket的过程中,JVM的实现中,会先把数据复制到堆外,再进行写入。
2、GC范围之外,降低GC压力,但实现了自动管理。DirectByteBuffer中有一个Cleaner对象(PhantomReference),Cleaner被GC前会执行clean方法,触发DirectByteBuffer中定义的Deallocator。
建议:
1、性能确实可观的时候才取使用;分配给大型、长寿命;(网络传输、文件读写场景)。
2、通过虚拟机参数MaxDirectMemorySize限制大小,防止耗尽整个机器的内存。

Channel通道

在这里插入图片描述
在这里插入图片描述
channel可以创建连接,和传输数据(BIO需要Socket+Stream)

SocketChannel

SocketChannel用于建立TCP网络连接,类似java.net.Socket。有两种创建socketChannel形式:
1、客户端主动发起和服务器的连接。
2、服务端获取的新链接。

//客户端主动发起连接的方式
SocketChannel socketChannel = SocketChannel.open();
socketChannel.configureBlocking(false);//设置为非阻塞模式
socketChannel.connect(new InetSocketAddress("http://163.com", 80));

channel.write(byteBuffer);//发送请求数据--向通道写入数据

int bytesRead = socketCheannel.read(byteBuffer);//读取服务端返回-==读取缓冲区的数据

socketChannel.close();//关闭连接

write写:非阻塞模式,可能没有写就返回。write()在尚未写入任何内容时,就可能返回了。需要在循环中调用write()。
read读:非阻塞模式,可能没有读到数据。read()方法可能直接返回而根本不读取任何数据,根据返回的int值判断读取了多少字节。(-1:通道关闭,网络连接关闭。0:没有读到数据。>0:有数据。)

ServerSocketChannel

ServerSocketChannel可以监听新建的TCP连接通道,类似ServerSocket。

//创建网络服务端
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
serverSocketChannel.configureBlocking(false);//设置为非阻塞模式,需要手动设置,默认是阻塞模式
serverSocketChannel.socket().bind(new InetSocketAddress(8080));//绑定端口
//serverSocketChannel只是为了绑定端口
while(true){
	SocketChannel socketChannel = serverSocketChannel.accept();//获取新tcp连接通道
	if(socketChannel != null){
		//tcp请求 读取/响应
	}
}

serverSocketChannel.accept():如果该通道处于非阻塞模式,那么如果没有挂起的连接,该方法将立即返回null。必须检查返回的socketChannel是否为null。

Selector选择器

Selector是一个Java NIO组件,可以检查一个或多个NIO通道,并确定哪些通道已经准备好进行读取或写入。实现单个线程可以管理多个通道,从而管理多个网络连接。
一个线程使用Selector监听多个channel的不同事件:
四个事件分别对应SelectionKey四个常量:
1、Connet连接(SelectionKey.OP_CONNECT)
2、Accept准备就绪(OP_ACCRPT)
3、Read读取(OP_READ)
4、Write写入(OP_WRITE)

实现一个线程处理多个通道的核心概念:事件驱动机制
非阻塞的网络通道下,开发者通过Selector注册对于通道感兴趣的事件类型,线程通过监听事件来触发相应的代码执行。(拓展:更底层是操作系统的多路复用机制)

Selector selector = Selector.open();
channel.configureBlocking(false);
SelectorKey key = channel.register(selector, SelectionKey.OP_READ);//注册感兴趣的事件
while(true){//由accept轮询,变成了事件通知的方式
	int readyChannels = selector.select();//select收到新的事件,方法才会返回
	if(readyChannels == 0) continue;
	Set<SelectionKey> selectedKeys = selector.selectedKeys();
	Iterator<SelectionKey> keyIterator = selectedKeys.iterator();
	while(keyIterator.hasNext()){
		SelectionKey key = keyIterator.next();
		//判断不同的事件类型,执行对应的逻辑处理
		//key.isAcceptable()
		//key.isConnectable()
		//key.isReadable()
		//key.isWritable()
		keyIterator.remove();
	}
}

NIO和BIO对比

在这里插入图片描述

NIO与多线程结合的改进方案

链接:Scalable IO in Java

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值