其余功能介绍
看完以上陈述,详细大家对NIO有了一定的了解,下面主要通过几个案例,来说明NIO的其余功能,下面代码量偏多,功能性讲述偏少。
Scatter/Gatter
分散(scatter)从Channel中读取是指在读操作时将读取的数据写入多个buffer中。因此,Channel将从Channel中读取的数据“分散(scatter)”到多个Buffer中。
聚集(gather)写入Channel是指在写操作时将多个buffer的数据写入同一个Channel,因此,Channel 将多个Buffer中的数据“聚集(gather)”后发送到Channel。
scatter / gather经常用于需要将传输的数据分开处理的场合,例如传输一个由消息头和消息体组成的消息,你可能会将消息体和消息头分散到不同的buffer中,这样你可以方便的处理消息头和消息体。
案例:
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.ByteBuffer;
import java.nio.channels.Channel;
import java.nio.channels.FileChannel;
public class ScattingAndGather
{
public static void main(String args[]){
gather();
}
public static void gather()
{
ByteBuffer header = ByteBuffer.allocate(10);
ByteBuffer body = ByteBuffer.allocate(10);
byte [] b1 = {'0', '1'};
byte [] b2 = {'2', '3'};
header.put(b1);
body.put(b2);
ByteBuffer [] buffs = {header, body};
try
{
FileOutputStream os = new FileOutputStream("src/scattingAndGather.txt");
FileChannel channel = os.getChannel();
channel.write(buffs);
}
catch (IOException e)
{
e.printStackTrace();
}
}
}
transferFrom & transferTo
FileChannel的transferFrom()方法可以将数据从源通道传输到FileChannel中。
public static void method1(){
RandomAccessFile fromFile = null;
RandomAccessFile toFile = null;
try
{
fromFile = new RandomAccessFile("src/fromFile.xml","rw");
FileChannel fromChannel = fromFile.getChannel();
toFile = new RandomAccessFile("src/toFile.txt","rw");
FileChannel toChannel = toFile.getChannel();
long position = 0;
long count = fromChannel.size();
System.out.println(count);
toChannel.transferFrom(fromChannel, position, count);
}
catch (IOException e)
{
e.printStackTrace();
}
finally{
try{
if(fromFile != null){
fromFile.close();
}
if(toFile != null){
toFile.close();
}
}
catch(IOException e){
e.printStackTrace();
}
}
}
方法的输入参数position表示从position处开始向目标文件写入数据,count表示最多传输的字节数。如果源通道的剩余空间小于 count 个字节,则所传输的字节数要小于请求的字节数。此外要注意,在SoketChannel的实现中,SocketChannel只会传输此刻准备好的数据(可能不足count字节)。因此,SocketChannel可能不会将请求的所有数据(count个字节)全部传输到FileChannel中。
transferTo()方法将数据从FileChannel传输到其他的channel中。
public static void method2()
{
RandomAccessFile fromFile = null;
RandomAccessFile toFile = null;
try
{
fromFile = new RandomAccessFile("src/fromFile.txt","rw");
FileChannel fromChannel = fromFile.getChannel();
toFile = new RandomAccessFile("src/toFile.txt","rw");
FileChannel toChannel = toFile.getChannel();
long position = 0;
long count = fromChannel.size();
System.out.println(count);
fromChannel.transferTo(position, count,toChannel);
}
catch (IOException e)
{
e.printStackTrace();
}
finally{
try{
if(fromFile != null){
fromFile.close();
}
if(toFile != null){
toFile.close();
}
}
catch(IOException e){
e.printStackTrace();
}
}
}
上面所说的关于SocketChannel的问题在transferTo()方法中同样存在。SocketChannel会一直传输数据直到目标buffer被填满。
Pipe
Java NIO 管道是2个线程之间的单向数据连接。Pipe有一个source通道和一个sink通道。数据会被写到sink通道,从source通道读取。
public static void method1(){
Pipe pipe = null;
ExecutorService exec = Executors.newFixedThreadPool(2);
try{
pipe = Pipe.open();
final Pipe pipeTemp = pipe;
exec.submit(new Callable<Object>(){
@Override
public Object call() throws Exception
{
Pipe.SinkChannel sinkChannel = pipeTemp.sink();//向通道中写数据
while(true){
TimeUnit.SECONDS.sleep(1);
String newData = "Pipe Test At Time "+System.currentTimeMillis();
ByteBuffer buf = ByteBuffer.allocate(1024);
buf.clear();
buf.put(newData.getBytes());
buf.flip();
while(buf.hasRemaining()){
System.out.println(buf);
sinkChannel.write(buf);
}
}
}
});
exec.submit(new Callable<Object>(){
@Override
public Object call() throws Exception
{
Pipe.SourceChannel sourceChannel = pipeTemp.source();//向通道中读数据
while(true){
TimeUnit.SECONDS.sleep(1);
ByteBuffer buf = ByteBuffer.allocate(1024);
buf.clear();
int bytesRead = sourceChannel.read(buf);
System.out.println("bytesRead="+bytesRead);
while(bytesRead >0 ){
buf.flip();
byte b[] = new byte[bytesRead];
int i=0;
while(buf.hasRemaining()){
b[i]=buf.get();
System.out.printf("%X",b[i]);
i++;
}
String s = new String(b);
System.out.println("=================||"+s);
bytesRead = sourceChannel.read(buf);
}
}
}
});
}catch(IOException e){
e.printStackTrace();
}finally{
exec.shutdown();
}
}
DatagramChannel
Java NIO中的DatagramChannel是一个能收发UDP包的通道。因为UDP是无连接的网络协议,所以不能像其它通道那样读取和写入。它发送和接收的是数据包。
public static void reveive(){
DatagramChannel channel = null;
try{
channel = DatagramChannel.open();
channel.socket().bind(new InetSocketAddress(8888));
ByteBuffer buf = ByteBuffer.allocate(1024);
buf.clear();
channel.receive(buf);
buf.flip();
while(buf.hasRemaining()){
System.out.print((char)buf.get());
}
System.out.println();
}catch(IOException e){
e.printStackTrace();
}finally{
try{
if(channel!=null){
channel.close();
}
}catch(IOException e){
e.printStackTrace();
}
}
}
public static void send(){
DatagramChannel channel = null;
try{
channel = DatagramChannel.open();
String info = "I'm the Sender!";
ByteBuffer buf = ByteBuffer.allocate(1024);
buf.clear();
buf.put(info.getBytes());
buf.flip();
int bytesSent = channel.send(buf, new InetSocketAddress("10.10.195.115",8888));
System.out.println(bytesSent);
}catch(IOException e){
e.printStackTrace();
}finally{
try{
if(channel!=null){
channel.close();
}
}catch(IOException e){
e.printStackTrace();
}
}
}
可以通过阅读参考资料2和3了解更多的NIO细节知识,前人栽树后人乘凉,这里就不赘述啦。
参考资料
1. Java NIO
2. Java NIO 系列教程
3. Java NIO 详解
4. 《深入分析Java Web技术内幕》许令波 著
5. java大文件读写操作,java nio 之MappedByteBuffer,高效文件/内存映射
6. java nio 之MappedByteBuffer,高效文件/内存映射