Java NIO 网络编程初探
1. Java NIO
Java 1.4 版本添加了一个新的IO API,称为NIO(New IO, Non-Block IO)。NIO拥有所有IO的功能,但是操作方法却完全不一样。NIO支持面向缓冲区的、基于通道的IO操作。能够更加高效的进行IO操作。NIO同样拥有文件读写,网络通信等IO操作,今天我们来看看NIO中的TCP网络通信的使用方法。
2. Java NIO 三大核心
Java NIO 有三大核心要素:Channel、Buffer和Selector。Java IO 的操作都是基于输入输出流的,而NIO则是基于Channel和Buffer的,数据先被读取到Buffer中,然后再进行处理。Selector作为NIO的辅助工具,能够使NIO的操作更加高效。
2.1 Buffer
NIO提供了多种Buffer,包括对应基本数据类型的ByteBuffer、CharBuffer、ShortBuffer、IntBuffer、LongBuffer、FloatBuffer和DoubleBuffer。
Buffer拥有四种属性:
- 容量(Capacity):缓冲区能够容纳的数据元素的最大数量。这一个容量在缓冲区创建时被设定,并且永远不能改变。
- 上界(Limit):缓冲区的第一个不能被读或写的元素。或者说,缓冲区中现存元素的计数。
- 位置(Position):下一个要被读或写的元素的索引。位置会自动由相应的 get( )和 put( )函数更新。
- 标记(Mark):下一个要被读或写的元素的索引。位置会自动由相应的 get( )和 put( )函数更新。
常用的Buffer方法有:
1. 获取方法
static XXXBuffer allocate(int capacity);
2. 操作方法
flip(): limit设为position的位置,position设为0,读写指针又移动到了开始的位置,调用flip()后,buffer为输出数据做好了准备。
rewind():将 position 重置为 0 ,一般用于重复读。
clear() :position 变成 0 , limit 变成 capacity (清空 buffer ,准备再次被写入) 。
compact(): 将未读取的数据拷贝到 buffer 的头部位。
mark(): reset():mark 可以标记一个位置, reset 可以重置到该位置。
put(),get() 放入,取出数据, 分为相对(Relative)和绝对(Absolute)。
2.2 Channel
Channel用于和Buffer交互,Channel能将数据的读写映射到Buffer,能够在两个实体中有效的传输数据,Channel是数据交互的通道,而Buffer则是通道的两个端点。通过这种方式,能够极大地利用操作系统的IO能力。
Java NIO 中的通道分为四种:
- FileChannel:用于读取、写入、映射和操作文件的通道。(用于本地文件操作)
- DatagramChannel:通过UDP 读写网络中的数据通道。
- SocketChannel:通过TCP 读写网络中的数据。
- ServerSocketChannel:可以监听新进来的TCP 连接,对每一个新进来的连接都会创建一个SocketChannel。
常用的Channel方法:
1. 获取方法
可以直接调用通道类提供的静态方法open来获取通道的实例
也可以通过IO流对象调用getChannel()方法来获取Channel实例
2. 操作方法
Channel通常是配合Buffer使用的,可以将Channel映射到Buffer中
也可以直接调用write(Buffer buf)和read(Buffer buf)
2.3 Selector
selector使用单线程处理多个channel,当应用打开了多个channel且每个channel的吞吐量不是很大时,使用channel就比较方便。使用selector分如下的几步:参考文章
- 创建:使用Selector selector = Selector.open()
- 将channel注册到selector:channel.register(selector, **),该方法可返回一个SelectorKey,代表该channel在selector中的”代号”。与selector一起使用时,channel一定处于非阻塞模式下。所以需要提前设置channel.configureBlocking(false)。
- 使用selector几个select的重载方法测试通道有多少已经准备好了:
- int select()阻塞到至少有一个对象在通道上准备好了。
- int select(long timeout)最长会阻塞timeout毫秒
- int selectNow()不会阻塞,不管什么情况会直接返回,如果自上次调用选择操作以来,没有就绪的通道则直接返回0。
- 调用select方法之后,如果有返回值,则说明已经有selector就绪了。此时可以通过selectedKey来选择已选择键集中就绪的通道。使用Set selectedKey = selector.selectedKeys();,之后在得到的SelectionKey的Set中可以通过SelectionKey提供的方法来操作channel。得到Channel的基本方法为selectionKey.channel() 。
3. Java NIO TCP网络通信
3.1 客户端实现
3.2 服务端实现