简介
核心API:
Channels:channel像一种管道,数据可以从channel到Buffer,其主要实现FileChannel(文件读取数据)、DatagramChannel(UDP读取数据)、SocketChannel(TCP读取数据)、ServiceSocketChannel(监听TCP连接)
Buffers:缓冲区,一次性把数据读到缓冲区,通过Channel进行数据处理,其主要实现ByteBuffer、CharBuffer、DoubleBuffer、FloatBuffer、IntBuffer、LongBuffer、ShortBuffer
Selectors:处理器,可以运行单个线程处理多个Channel
工具类:
Pipe:两个线程之间的单向数据连接,有一个sources通道和sink通道、数据会被写到sink通道、从source通道读取
FileLock:只有一个进程可以拿到文件锁
详解
Channel
特性
即可从通道中读取数据、也可以写数据到通道
通道可以异步读写
通道中的数据总要先读到一个buffer、或者总要写入一个buffer
实现
FileChannel
无法设置为非阻塞模式,它总是运行在阻塞模式下
读数据:
RandomAccessFile:打开文件
file.getChannel():获取FileChannel实例
ByteBuffer.allocate(1024):设置一个缓冲区
inChannel.read():从fileChannel中读取数据到Buffer,如果是-1,表示文件到了末尾
buffer.flip():将Buffer从写模式切换到读模式
buffer.hasRemaining():是否达到缓冲区上界
buffer.compact():清空缓冲区
inChannel.close():关闭通道
public static void main(String[] args) {
RandomAccessFile file;
try {
file = new RandomAccessFile("D:\\aaa.txt", "rw");
FileChannel inChannel = file.getChannel();
ByteBuffer buffer = ByteBuffer.allocate(1024);
while(true) {
int count = inChannel.read(buffer);
if (count <= -1) {
break;
}
buffer.flip();
while (buffer.hasRemaining()) {
char ch = (char) buffer.get();
System.out.print(ch);
}
buffer.compact();
inChannel.close();
}
} catch (Exception e) {
e.printStackTrace();
}
}
写数据:
buffer.put:设置数据
inChannel.write(buffer):向通道中写数据,需要循环调用,因为无法保证write()方法一次向FileChannel写入多少个字节
public static void main(String[] args) {
RandomAccessFile file;
try {
String data = "hello fileChannel";
file = new RandomAccessFile("D:\\aaa.txt", "rw");
FileChannel inChannel = file.getChannel();
ByteBuffer buffer = ByteBuffer.allocate(1024);
buffer.clear();
buffer.put(data.getBytes());
buffer.flip();
while(buffer.hasRemaining()) {
inChannel.write(buffer);
}
inChannel.close();
} catch (Exception e) {
e.printStackTrace();
}
}
DatagramChannel
DatagramChannel是一个能收发UDP包的通道。因为UDP是无连接的网络协议,所以不能像其它通道那样读取和写入。它发送和接收的是数据包。
接收数据
DatagramChannel.open():打开通道
channel.receive():接收数据
public static void main(String[] args) {
DatagramChannel channel;
try {
channel = DatagramChannel.open();
channel.socket().bind(new InetSocketAddress(9999));
ByteBuffer buffer = ByteBuffer.allocate(48);
buffer.clear();
channel.receive(buffer);
} catch (IOException e) {
e.printStackTrace();
}
}
发送数据
channel.send():发送数据
public static void main(String[] args) {
DatagramChannel channel;
try {
channel = DatagramChannel.open();
channel.socket().bind(new InetSocketAddress(9999));
String data = "hello DatagramChannel ";
ByteBuffer buf = ByteBuffer.allocate(48);
buf.clear();
buf.put(data.getBytes());
buf.flip();
int bytesSent = channel.send(buf, new InetSocketAddress("ccc.com", 80));
} catch (IOException e) {
e.printStackTrace();
}
}
SocketChannel
读数据
SocketChannel.open():打开socketChannel通道
socketChannel.connect:连接socket地址
public static void main(String[] args) {
SocketChannel socketChannel;
try {
socketChannel = SocketChannel.open();
socketChannel.connect(new InetSocketAddress("http://cc.com", 80));
ByteBuffer buffer = ByteBuffer.allocate(48);
int bytesRead = socketChannel.read(buffer);
socketChannel.close();
} catch (IOException e) {
e.printStackTrace();
}
}
写数据
public static void main(String[] args) {
SocketChannel socketChannel;
RandomAccessFile file;
try {
String data = "hello fileChannel";
file = new RandomAccessFile("D:\\aaa.txt", "rw");
socketChannel = SocketChannel.open();
socketChannel.connect(new InetSocketAddress("http://cc.com", 80));
ByteBuffer buffer = ByteBuffer.allocate(1024);
buffer.clear();
buffer.put(data.getBytes());
buffer.flip();
while(buffer.hasRemaining()) {
socketChannel.write(buffer);
}
socketChannel.close();
} catch (IOException e) {
e.printStackTrace();
}
}
Selector
创建
调用open创建一个Selector
Selectorselector=Selector.open();
注册通道
Channel必须处于非阻塞模式下,FileChannel是阻塞模式不能与Selector一起使用
register:第二个参数可以监听四种不同的类型Connect、Accept、Read、Write
channel.configureBlocking(false);
SelectionKey key = channel.register(selector,Selectionkey.OP_READ);
示例
selector.select():阻塞到至少有一个通道在你注册的事件上就绪了
selectedKeys():访问注册的channel
public static void main(String[] args) {
SocketChannel channel;
try {
channel = SocketChannel.open();
channel.connect(new InetSocketAddress("http://cc.com", 80));
Selector selector = Selector.open();
channel.configureBlocking(false);
SelectionKey key = channel.register(selector, SelectionKey.OP_READ);
while(true) {
int readyChannels = selector.select();
if(readyChannels == 0) continue;
Set selectedKeys = selector.selectedKeys();
Iterator keyIterator = selectedKeys.iterator();
while(keyIterator.hasNext()) {
SelectionKey key1 = (SelectionKey) keyIterator.next();
if(key1.isAcceptable()) {
} else if (key1.isConnectable()) {
} else if (key1.isReadable()) {
} else if (key1.isWritable()) {
}
keyIterator.remove();
}
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
Buffer
基本用法
写入数据到buffer
调用flip()方法,写模式切换到读模式
从buffer中读取数据
调用clear或者compact清楚数据
capacity,position和limit
capacity:内存块,固定大小,满了之后需要清楚数据之后才能写数据
position:当前位置,初始值为0,当一个byte、long等数据写到Buffer后, position会向前移动到下一个可插入数据的Buffer单元。position最大可为capacity – 1。
limit:写模式下可以控制buffer写多少数据,读模式下控制读多少数据