NIO non-blocking ,Sun官方标榜的特征是:为原始的数据类型提供缓存服务
Channel 一个新的原始IO抽象
缓存区Buffer
一个存储原始数据类型的容器,它的底层就是一个数组,它用4个核心属性对数组进行了封装限制。
缓冲区的4个核心技术
capacity limit position mark
缓冲区可以用两个静态方法创建 wrap allocate
capacity代表了这个底层数组的长度大小,从1开始
limit 表示返回一个 index,从0开始,表示从这个index开始,后面的元素都是不可读不可写的。 即如果index=3,那么访问array[3]会返回异常。
position 表示返回下一个可读可写的index,也是从0开始。
mark标记,它记录当前position的位置,源码是 this.marl=position
获取缓冲区剩余空间的大小,remaining() ,源代码是 return limit - position ;
reset()方法前,标识mark必须不等于-1,它会让position = mark ;
直接缓存区
一般而言,在JVM会缓存硬盘的内容,然后再传递给Buffer ;而直接缓存区 就是省去JVM的中间缓存 ,直接把硬盘的内容读取到 Buffer 中。
isDirect()
还原缓存区的内容 一般这个方法用在往缓存区写入内容前使用
clear()
{
position=0; limit=capacity ; mark =-1;
}
缩小缓存区,一般用在写完缓存区后,要读取之前调用
flip()
{
limit=position ; position=0 ; mark =-1;
}
判断缓存区是否有底层实现数组
hasArray()
判断是否还有剩余的元素读取
hasRemaining { position <limit}
一般用在重写写入缓存区
rewind()
{
position=0 ; mark =-1;
}
arrayOffset 返回缓存区第一个元素在 底层数组的偏移量,即第一个缓存元素在数组的下标index。
List.toArray(T[])
ByteBuffer CharBuffer
slice方法,是创建新的缓存区,不过它还是共用底层数组,它的position是 原来的缓存区的limit 的下标
使用ByteBuffer CharBuffer处理中文输入
ByteBuffer默认使用的是utf8,CharBuffer使用的是utf-16BE
如果读取中文的时候,使用的编码格式是utf8,或者GBK,那么就需要经过特殊的转换。
使用获取 字节数组 getBytes("utf-16BE") ; 用byteBuffer.asCharBuffer() 将UTF-16BE的ByteBuffer对象转换成CharBuffer对象
使用Charset.forName("UTF-8").decode(bytebuffer)
order() 定义CPU读取字符的顺序,默认是从高位读取,但是有的CPU可能会从地位读取。
通道Channel
传输 数据 的 通路。
Channel接口 它的父接口是 Closeable接口,而Closeable接口 的父接口是 AutoCloseable接口
AutoCloseable接口的close方法,具有自动关闭资源的功能,强调在try块中使用。
而 Closeable接口重写的close方法,具有幂等性,用于关闭I/O流。
AsynchronousChannel子接口
具有异步I/O操作,它的方法是operation , 这个方法具有两个重载,能与异步的线程进行交流,返回值是 Future接口
NetworkChannel 接口,主要用于和Socket交互
AbstractInterruptibleChannel 接口,是提供一个可以被中断的通道实现类
FileChannel 类
该个通道永远是阻塞的。
它在内部一个当前文件的position,可以对其进行查询和修改,该个文件本身包含一个可读,可写的字节序列
不太常用的方法
truncate(long size ) 通道的文件截取给定大小,如果文件的大小大于 size ,那么丢弃 多余的。
transferTo() transferFrom()
FileLock lock(long position , long size , boolean shared )
共享锁,只能进行读操作;独占锁,自己能进行读写操作。
force(boolean meta) 强制所有对此通道的文件更新重新写入包含该文件的存储设备,如果传入值是true ,那么对文件内容和元数据进行更新。
MappedByteBuffer map(FileChannel.MapMode mode ,long position , long size )
将文件区域直接映射到内存。
open 打开一个文件
ioOpen
获取网络设备信息
NetworkInterface 类 表示一个有名称和分配给这个接口的IP列表的网络接口
getName
getDisplayName
getIndex
isUp 网卡是否开启
isLoopback 回调/回环接口 ,它是一个虚拟的,永远工作的虚拟接口
getMTU
获取子接口
getSubInterfaces
isVirtural
getParent
getHardwareAddress 硬件地址
getInetAddress 获取IP地址
InetAddress类的方法
getCanonicalHostName
getHostName
getHostAddress
getAddress
getLocalHost 返回主机的IP 如果有多个,返回第一个IP
getAllByName 通过名字放回主机IP
InetAddress getByName(host )
InterfaceAddress 类 可以获取网络接口的IP,子网掩码,广播地址等信息,对于IP6还能得到网络前缀
判断设备是否为点对点设备,支持多播等方法
基于TCP的 Socket通信
ServerSocket类
accept 方法具有阻塞性
三次握手的 时机 创建ServerSocket 对象,并且绑定指定的端口和IP地址
ServerSocket类的使用
设置超时时间 setSoTimeout(ms)客户端在超时时间之内没有 成果连接,那么会发生异常
构造函数的 参数 backlog , 接受客户端的连接的个数,默认是50,多余的请求会被忽略
bind(SocketAddress ) 服务端绑定指定的socket地址
SocketAddress 和 InetAddress 是两个不同的类,前面的类是socket地址,包括了IP地址和端口号,而后面的地址仅有IP地址。
SocketAddress 的子类是 InetSocketAddress
获取服务端本地绑定的socket地址 getLocalSocketAddress getLocalPort
获取主机名
getHostName 如果是IP地址创建的,那么它会通过DNS反向查找,返回主机的名字
getHostString 直接返回主机名或者IP地址
isUnresolved 如果域名无法解析,那么返回true,解析成功,返回false
Socket 的ReuseAddress选项
setReuseAddress() 端口是否允许复用
在TCP发起关闭请求后,一端进入TIME_WAIT 状态后,等待一段时间才关闭连接,在等待时间内,可以允许端口进行使用。
Socket 的ReceiveBuffer选项
ServerSocket接受套接字的SO_RCVBUF选项设置新值, 即服务端的接收缓存区的大小
Socket类的使用
bind 绑定本地的套接字
connect(SocketAddress ) 连接远程服务端的套接字
getPort获取远程端口
getLocalPort获取本地接口
Socket TcpNoDelay选项
控制是否采用Nagle算法,会将数据压缩到一个大包后,再传输。
Socket SendBufferSize选项
设置发生缓存区的大小 SO_SNDBUF
Socket Linger选项
控制socket关闭close行为,close后,但是底层套接字可能还没有关闭,因为留在缓存区的数据还没有发生完成。
Socket OOBinline
开启这个选项,套接字将接收所以TCP紧急数据,通过输入流接收。
关闭这个选项,紧急数据将会被丢弃
Socket KeepAlive选项
设置true时,如果某个端长时间内没有数据传输,那么就会发送一个ACK探测包,如果没有接收到ACK包,那么这个连接将会被关闭。
Socket TrafficClass选项
定性描述TCP连接的传输质量
UDP
客户端和服务端使用的是 DatagramSocket类 ,数据发送使用的是DatagramPacket类
UDP理论上的最大一个包只能传输 2的16次方 -1 =65535字节,但实际上 65535-20-8 =65507字节
组播 MulticastSocket
选择器
Selector 多路复用选择器
SelectorKey register方法后,返回的注册凭借
SelectableChannel 通道,使用regster方法,向选择器注册
一个通道可以向多个选择器注册,一个通道对一个选择器多次注册返回的凭借是 一样的。
一个多路选择器可以 处理1023个通道
SelectableChannel 是一个抽象类,它的平行类是FileChannel , 它有三个实现子类
分别是ServerSocketChannel SocketChannel DatagramChannel
这些子类都是通过静态的open方法创建实例对象
register(selector, int pos) 返回的是一个SelectorKey 代表该个通道已经向选择器注册了,返回凭证。 服务端的操作是接收,而客户端的可选操作,是可连接,可读,可写。
可以通过SelectorKey cancel方法显示地取消注册
新创建的ServerSocketChannel 都是非阻塞的,需要调用configureBlocking(true) 设置成阻塞的。
非阻塞模式下,accept方法如果没有客户端连接,返回null
如果accept方法,返回了一个 SocketChannel ,那么需要将它注册到 Selector上,实现多路复用。
注意:如果要注册到选择器,那么需要将通道设置成非阻塞模式
判断通道的状态
isRegister
isOpen
blockingLock 获取阻塞锁对象
supportOptions 返回通道支持的Socket Option
isBlocking
SelectionKey keyFor(selector ) 返回通道向选择器注册的凭证
validOps 返回一个通道支持的操作集
SelectorProvider provider()
SelectorProvider 用于选择器和可选择通道的服务提供者
执行connect
connect(SocketAddress )
如果通道处于非阻塞模式,发起连接后,随后某个时间点发起连接;如果是阻塞的,那么立即发起连接,如果连接成功返回真,不成功返回假;
如果连接时立即建立的,说明是阻塞的,返回true,而且连接不成功出现异常
如果连接不是立即建立但,返回fasle,那么说明是非阻塞模式,随后必须通过 finishConnect 验证
在非阻塞通道下,read和 write 也具有非阻塞特性
Selector类
多路复用选择器
通过SelectionKey对象 表示通道的注册
选择器维护了三个集合
- 键集,本身不可修改,通过注册方法注册一个通道,就会增加一个元素,通过keys() 方法返回
- 已选择键集,首先 先调用select()方法查看 键集代表的通道是否已经做好了 相关操作准备,如果准备好了 通过 selectedKeys() 方法返回一个 就绪键集,就绪键集始终是键集的一个子集
- 已取消键集, 表示已被取消 但通道尚未注销的键 的集合,不可直接访问,在select()方法期间,从键集 中 移除 已取消的键
通过已选择键集,或者迭代器的 remove 方法,可以将键 从已选择器中移出。
使用cancel方法能将 键 取消,键会被添加到 已取消键集,在下一次select 操作期间,这个已取消键集中的所有键 会从 全部的键集中移出,并注销该个通道。
select () select(long) selectNow()
选择器是线程安全的,但是 选择器的键 和 已选择键集不是线程安全的。
select方法一般是阻塞的,它会返回已经就绪的键集数量
面对重复消费的问题,一般采用remove方法 将 键 从 已选择键集 中移出。
唤醒操作
wakeup
SelectionKey的使用
isValid 方法 测试这个键 是否还有用
选择键包含两个集
interest
ready 表示就绪
isAccptable
isConnectable
isReadable
isWritable
设置attach 附加
Pipe.SinkChannel
Pipe.SourceChannel
AIO
AsynchronousFileChannel 是一个读取,写入和操作文件的异步通道
通过open方法打开一个文件
异步文件通道在文件没有当前位置,而是将文件位置指定给启动异步操作的每个读取,写入方法
ComletionHandler 被指定为参数,被调用以消耗I/O操作的结果
AsynchronousFileChannel.open(path, StandardOpenOption.ENUM)
使用lock() 方法获取文件锁 , 这个方法有 多个重载
Future <FileLock > future =channel.lock()
FileLock lock=future.lock()
lock(A attchment , CompletionHandler<Future , ? super A > handler>
CompletionHandler 需要实现两个方法,一个是成功的completed方法,这个方法内需要释放锁;一个是失败的falied方法