版权声明:本文为博主原创文章,转载请说明出处。 https://blog.csdn.net/weixin_43549578/article/details/85110815
通道(Channel):由 java.nio.channels 包定义的。Channel 表示 IO 源与目标打开的连接。Channel 类似于传统的“流”。只不过 Channel本身不能直接访问数据,Channel 只能与Buffer 进行交互。
使用了通道进行连接,缓存使用了非直接缓存区形式。
Java为 Channel 接口提供的最主要实现类如下:
java.nio.channels.Channel 接口:
|--FileChannel 用于读取、写入、映射和操作文件的通道。
|--SocketChannel 通过 TCP 读写网络中的数据。
|--ServerSocketChannel 可以监听新进来的 TCP 连接,对每一个新进来的连接都会创建一个 SocketChannel。
|--DatagramChannel 通过 UDP 读写网络中的数据通道。
获取通道:
1. Java 针对支持通道的类提供了 getChannel() 方法
本地 IO:
FileInputStream/FileOutputStream
RandomAccessFile
网络IO:
Socket
ServerSocket
DatagramSocket
2. 在 JDK 1.7 中的 NIO.2 针对各个通道提供了静态方法 open()
3. 在 JDK 1.7 中的 NIO.2 的 Files 工具类的 newByteChannel()
数据传输:
1.将 Buffer 中数据写入 Channel:getChannel().write(bufs);
2.从 Channel 读取数据到 Buffer:getChannel().read(bufs)
3.transferFrom():将数据从源通道传输到其他 Channel 中。
4.transferTo():将数据从源通道传输他 到其他 Channel中。
通道模式:分散(Scatter)与聚集(Gather)
分散读取(Scattering Reads):将通道中的数据分散到多个缓冲区中。
注意:按照缓冲区的顺序,从 Channel 中读取的数据依次将 Buffer 填满。
聚集写入(Gathering Writes):将多个缓冲区中的数据聚集到通道中。
注意:按照缓冲区的顺序,写入 position 和 limit 之间的数据到 Channel 。
FileChannel 的常用方法:
int read(ByteBuffer dst) 从 从 Channel 到 中读取数据到 ByteBuffer
long read(ByteBuffer[] dsts) 将 将 Channel 到 中的数据“分散”到 ByteBuffer[]
int write(ByteBuffer src) 将 将 ByteBuffer 到 中的数据写入到 Channel
long write(ByteBuffer[] srcs) 将 将 ByteBuffer[] 到 中的数据“聚集”到 Channel
long position() 返回此通道的文件位置
FileChannel position(long p) 设置此通道的文件位置
long size() 返回此通道的文件的当前大小
FileChannel truncate(long s) 将此通道的文件截取为给定大小
void force(boolean metaData) 强制将所有对此通道的文件更新写入到存储设备中
字符集:Charset 也支持通道。
编码:字符串 -> 字节数组
解码:字节数组 -> 字符串
案例:
FileChannel中的transferTo方法并不一定能完整传输所有数据。在文档中解释如下:
/**
* 将字节从此通道的文件传输到给定的可写入字节通道。
* 试图读取从此通道的文件中给定 position 处开始的 count 个字节,并将其写入目标通道。
* 此方法的调用不一定传输所有请求的字节;是否传输取决于通道的性质和状态。
* 如果此通道的文件从给定的 position 处开始所包含的字节数小于 count 个字节,
* 或者如果目标通道是非阻塞的并且其输出缓冲区中的自由空间少于 count 个字节,则所传输的字节数要小于请求的字节数。
* 此方法不修改此通道的位置。如果给定的位置大于该文件的当前大小,则不传输任何字节
* 。如果目标通道中有该位置,则从该位置开始写入各字节,然后将该位置增加写入的字节数。
* 与从此通道读取并将内容写入目标通道的简单循环语句相比,此方法可能高效得多。
* 很多操作系统可将字节直接从文件系统缓存传输到目标通道,而无需实际复制各字节。
* 参数:
* position - 文件中的位置,从此位置开始传输;必须为非负数
* count - 要传输的最大字节数;必须为非负数
* target - 目标通道
* 返回:
* 实际已传输的字节数,可能为零
*/
public abstract long transferTo(long position, long count,
WritableByteChannel target)
throws IOException;
transferTo/transferFrom测试:
@Test
public void test3() throws IOException{
FileChannel inChannel = FileChannel.open(Paths.get("d:/1.txt"), StandardOpenOption.READ);
FileChannel outChannel = FileChannel.open(Paths.get("d:/2.txt"), StandardOpenOption.WRITE, StandardOpenOption.READ, StandardOpenOption.CREATE);
// inChannel.transferTo(0, inChannel.size(), outChannel);
outChannel.transferFrom(inChannel, 0, inChannel.size());
inChannel.close();
outChannel.close();
}
执行结果:
利用通道完成文件的复制(非直接缓冲区):
@Test
public void test1(){
long start = System.currentTimeMillis();
FileInputStream fis = null;
FileOutputStream fos = null;
//①获取通道
FileChannel inChannel = null;
FileChannel outChannel = null;
try {
fis = new FileInputStream("d:/1.txt");
fos = new FileOutputStream("d:/2.txt");
inChannel = fis.getChannel();
outChannel = fos.getChannel();
//②分配指定大小的缓冲区
ByteBuffer buf = ByteBuffer.allocate(1024);
//③将通道中的数据存入缓冲区中
while(inChannel.read(buf) != -1){
buf.flip(); //切换读取数据的模式
//④将缓冲区中的数据写入通道中
outChannel.write(buf);
buf.clear(); //清空缓冲区
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if(outChannel != null){
try {
outChannel.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if(inChannel != null){
try {
inChannel.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if(fos != null){
try {
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if(fis != null){
try {
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
long end = System.currentTimeMillis();
System.out.println("耗费时间为:" + (end - start));
}
使用直接缓冲区完成文件的复制(内存映射文件):
@Test
public void test2() throws IOException{
long start = System.currentTimeMillis();
FileChannel inChannel = FileChannel.open(Paths.get("d:/1.txt"), StandardOpenOption.READ);
FileChannel outChannel = FileChannel.open(Paths.get("d:/2.txt"), StandardOpenOption.WRITE, StandardOpenOption.READ, StandardOpenOption.CREATE);
//内存映射文件
MappedByteBuffer inMappedBuf = inChannel.map(MapMode.READ_ONLY, 0, inChannel.size());
MappedByteBuffer outMappedBuf = outChannel.map(MapMode.READ_WRITE, 0, inChannel.size());
//直接对缓冲区进行数据的读写操作
byte[] dst = new byte[inMappedBuf.limit()];
inMappedBuf.get(dst);
outMappedBuf.put(dst);
inChannel.close();
outChannel.close();
long end = System.currentTimeMillis();
System.out.println("耗费时间为:" + (end - start));
}
分散和聚集:
@Test
public void test4() throws IOException{
RandomAccessFile raf1 = new RandomAccessFile("1.txt", "rw");
//1. 获取通道
FileChannel channel1 = raf1.getChannel();
//2. 分配指定大小的缓冲区
ByteBuffer buf1 = ByteBuffer.allocate(100);
ByteBuffer buf2 = ByteBuffer.allocate(1024);
//3. 分散读取
ByteBuffer[] bufs = {buf1, buf2};
channel1.read(bufs);
for (ByteBuffer byteBuffer : bufs) {
//切换模式
byteBuffer.flip();
}
System.out.println(new String(bufs[0].array(), 0, bufs[0].limit()));
System.out.println("-----------------");
System.out.println(new String(bufs[1].array(), 0, bufs[1].limit()));
//4. 聚集写入
RandomAccessFile raf2 = new RandomAccessFile("2.txt", "rw");
FileChannel channel2 = raf2.getChannel();
channel2.write(bufs);
}
获取当前所有的编码格式:
Map<String, Charset> map = Charset.availableCharsets();
Set<Entry<String, Charset>> set = map.entrySet();
for (Entry<String, Charset> entry : set) {
System.out.println(entry.getKey() + "=" + entry.getValue());
}
编码与解码:
@Test
public void test6() throws IOException{
Charset cs1 = Charset.forName("GBK");
//获取编码器
CharsetEncoder ce = cs1.newEncoder();
//获取解码器
CharsetDecoder cd = cs1.newDecoder();
//创建缓存
CharBuffer cBuf = CharBuffer.allocate(1024);
cBuf.put("大吉大利,今晚吃鸡!");
cBuf.flip();
//编码
ByteBuffer bBuf = ce.encode(cBuf);
for (int i = 0; i < bBuf.array().length; i++) {
System.out.println(bBuf.get());
}
//解码
bBuf.flip();
CharBuffer cBuf2 = cd.decode(bBuf);
System.out.println(cBuf2.toString());
System.out.println("------------------------------------------------------");
//第二种解码:按照GBK解码 使用当前解码器(GBK)
Charset cs2 = Charset.forName("GBK");
bBuf.flip();
CharBuffer cBuf3 = cs2.decode(bBuf);
System.out.println(cBuf3.toString());
}