文章目录
ByteBuffer
allocate(int capacity)
: 分配指定大小的缓冲区(非直接缓冲区)allocateDirect(int capacity)
: 分配指定大小的缓冲区(直接缓冲区)put()
:向缓冲区中存储数据get(byte[] dst)
:从缓冲区获取数据,这里的dst的容量必须和缓冲区的大小一致get(byte[] dst,int offest,int length)
:读取指定长度的内容到dst中,这里的dst容量没有要求flip()
: 缓冲区从写模式切换到读模式clear()
:清空缓冲区,数据依然存在,只是处于一个“被遗忘”状态,改变的只是limit
和position
array()
:返回实现此缓冲区的 byte 数组 mark()
: 标记当前位置(position)reset()
:恢复到mark的位置capacity
:容量,表示缓冲区的最大容量,一旦声明不能改变limit
: 界限,缓冲区中可以操作的数据的大小,实际存储数据的大小,limit之后的数据不能进行读写position
:位置,表示缓冲区中正在操作数据的位置position<=limit<=capacity
@Test
public void test1(){
String str="abcd";
ByteBuffer buffer=ByteBuffer.allocate(1024);//分配1024个字节大小的缓冲区
buffer.put(str.getBytes()); //写入数据
System.out.println(buffer.capacity()); //容量 1024
System.out.println(buffer.limit()); //界限,1024
System.out.println(buffer.position()); //正在操作数据的位置 0
buffer.flip(); //切换到读模式,读取数据的时候一定要切换,否则将会没有界限
System.out.println(buffer.capacity()); //容量 1024
System.out.println(buffer.limit()); //界限,4,允许读取的位置只能到4,因为就存储了这么多的数据
System.out.println(buffer.position()); //正在操作数据的位置 0
System.err.println(buffer.get(4)); //超出界限了,下标记从0开始,0<=index<limit
}
/**
* 读取缓冲区中的数据到指定的字节数组中
* 1、字节数组的大小一定要和buffer.limit()一样大小,否则会报错
*/
@Test
public void test2(){
String str="abcdefg";
ByteBuffer buffer=ByteBuffer.allocate(1024); //申请空间大小
buffer.put(str.getBytes()); //存入数据
buffer.flip(); //切换到读模式
//申请一个字节数组和实际数据一样大,这里必须和缓冲区的实际数据大小一样,否则将会报错
byte[] dst=new byte[buffer.limit()];
buffer.get(dst); //读取缓冲区的数据到dst字节数组中
System.out.println(new String(dst));
}
/**
* 读取一个字节
*/
@Test
public void test3(){
String str="abcdefg";
ByteBuffer buffer=ByteBuffer.allocate(10); //申请空间大小
buffer.put(str.getBytes()); //存入数据
buffer.flip(); //切换到读模式
System.out.println((char)buffer.get());
}
/**
* 测试remark和rest
*/
@Test
public void test1(){
ByteBuffer buffer=ByteBuffer.allocate(1024);
String str="abcdcdscdscds";
buffer.put(str.getBytes()); //向缓冲区中写入数据
buffer.flip(); //切换到读的模式
byte[] dst=new byte[1024]; //创建byte数组
System.out.println("---------------------读取两个字节的数据----------------------------");
buffer.get(dst,0,2); //读取两个字节长度的数据到dst中,此时的position的位置位2
System.out.println(new String(dst));
System.out.println("----------------------标记此时的位置------------------------------");
buffer.mark(); //标记位置,此时的position的位置位2
System.out.println("---------------------继续读取两个字节的数据----------------------------");
buffer.get(dst,buffer.position(),2); //继续从当前位置读取两个字节到dst中
System.out.println(new String(dst));
System.out.println(buffer.position()); //此时的position的位置为4
System.out.println("---------------------重置缓冲区到remark的位置----------------------------");
buffer.reset(); //重置缓冲区到rmark的位置
System.out.println(buffer.position()); //此时的position为2
}
allocateDirect()
工厂方法 来创建。此方法返回的 缓冲区进行分配和取消分配所需成本通常高于非直接缓冲区 。直接缓冲区的内容可以驻留在常规的垃圾回收堆之外,因此,它们对应用程序的内存需求量造成的影响可能并不明显。所以,建议将直接缓冲区主要分配给那些易受基础系统的机 本机 I/O 操作影响的大型、持久的缓冲区。一般情况下,最好仅在直接缓冲区能在程序性能方面带来明显好处时分配它们。FileChannel
:文件的操作SocketChannel
:TCPServerSocketChannel
:TCPDatagramChannel
:UDPgetChannel()
方法获取通道FileInputStream
FileOutputStram
RandomAccessFile
open()
newByteChannel()
/**
* 使用getChannel获取通道,实现文件的复制
* @throws IOException
*/
@Test
public void test1() throws IOException{
FileInputStream inputStream=new FileInputStream(new File("C:/images/lifecrystal.png"));
FileOutputStream outputStream=new FileOutputStream(new File("C:/images/2.png"));
FileChannel inchannel = inputStream.getChannel(); //获取通道,用于读取
FileChannel outchannel=outputStream.getChannel(); //获取通道,用于写入
ByteBuffer buffer=ByteBuffer.allocate(1024); //申请缓冲区
//将通道中的数据写入缓冲区
while (inchannel.read(buffer)!=-1) {
buffer.flip(); //切换到读模式
//将缓冲区中的数据写入通道
outchannel.write(buffer);
buffer.clear(); //清空缓冲区,继续读取数据
}
//关闭通道
inchannel.close();
outchannel.close();
inputStream.close();
outchannel.close();
}
/**
* 使用直接缓冲区完成文件的复制
* 使用open()的方法获取通道
* @throws IOException
*/
@Test
public void test2() throws IOException{
//获取一个读取数据的通道,使用的读模式
FileChannel inchannel=FileChannel.open(Paths.get("C:/images/2.png"), StandardOpenOption.READ);
/**
* StandardOpenOption.CREATE : 如果文件不存在,那么就创建,如果存在将会覆盖,不报错
* StandardOpenOption.CREATE_NEW : 如果不存在就创建,如果存在,将会报错
*/
FileChannel outchannel=FileChannel.open(Paths.get("C:/images/3.png"), StandardOpenOption.CREATE,StandardOpenOption.WRITE,StandardOpenOption.READ);
//创建一个内存映射文件,操作直接缓冲区,和allocatDirect()一样,MapMode.READ_ONLY表示只读的模式,用于读取
MappedByteBuffer inMappedBuff = inchannel.map(MapMode.READ_ONLY, 0, inchannel.size());
//创建一个内容映射文件,MapMode.READ_WRITE表示读写模式,可以读写
MappedByteBuffer outMappedBuffer = outchannel.map(MapMode.READ_WRITE, 0, inchannel.size());
byte[] dst=new byte[inMappedBuff.limit()];
//将数据读入到dst中
inMappedBuff.get(dst);
//将数据从dst中读取到outMappedBuffer
outMappedBuffer.put(dst);
}
transferTo(long position,long count,WritableByteChannel target)
:将数据从通道写入可写的通道target中transferFrom(ReadableByteChannel from,long position,long count)
:将数据从通道from中读取到通道中 /**
* 通道之间直接进行传输
* 1、transferTo(long position,long count,WritableByteChannel target):将数据从通道写入可写的通道target中
* 2、transferFrom(ReadableByteChannel from,long position,long count):将数据从通道from中读取到通道中
* @throws IOException
*/
@Test
public void test3() throws IOException{
//获取一个读取数据的通道,使用的读模式
FileChannel inchannel = FileChannel.open(Paths.get("C:/images/2.png"),
StandardOpenOption.READ);
/**
* StandardOpenOption.CREATE : 如果文件不存在,那么就创建,如果存在将会覆盖,不报错
* StandardOpenOption.CREATE_NEW : 如果不存在就创建,如果存在,将会报错
*/
FileChannel outchannel=FileChannel.open(Paths.get("C:/images/4.png"), StandardOpenOption.CREATE,StandardOpenOption.WRITE,StandardOpenOption.READ);
//将通道inchannel中的数据直接写入outchannel中
inchannel.transferTo(0, inchannel.size(), outchannel);
//和上面一样的效果
// outchannel.transferFrom(inchannel, 0, inchannel.size());
inchannel.close();
outchannel.close();
}
/**
* 分散读取:将通道中的数据写入各个缓冲区中,是按照顺序写入的,第一个缓冲区写满才会写入第二个缓冲区
* @throws IOException
*/
@Test
public void test4() throws IOException{
//创建读写模式的RandomAccessFile
RandomAccessFile accessFile=new RandomAccessFile(new File("C:/images/2.png"), "rw");
FileChannel inchannel=accessFile.getChannel(); //读取
ByteBuffer buffer1=ByteBuffer.allocate(10); //第一个缓冲区,10个字节大小
ByteBuffer buffer2=ByteBuffer.allocate(1024);//第二个缓冲区
ByteBuffer[] dst={buffer1,buffer2};
//分散读取
inchannel.read(dst);
for (ByteBuffer byteBuffer : dst) {
byteBuffer.flip(); //切换到读的模式
}
//输出第一个缓冲区的数据
System.out.println(new String(buffer1.array()));
//输出第二个缓冲区中的数据
System.out.println(new String(buffer2.array()));
}
@Test
public void test4() throws IOException{
//创建读写模式的RandomAccessFile
RandomAccessFile accessFile=new RandomAccessFile(new File("C:/images/2.png"), "rw");
FileChannel inchannel=accessFile.getChannel(); //读取
ByteBuffer buffer1=ByteBuffer.allocate(10); //第一个缓冲区,10个字节大小
ByteBuffer buffer2=ByteBuffer.allocate(1024);//第二个缓冲区
ByteBuffer[] dst={buffer1,buffer2};
//分散读取
inchannel.read(dst);
for (ByteBuffer byteBuffer : dst) {
byteBuffer.flip(); //切换到读的模式
}
//输出第一个缓冲区的数据
System.out.println(new String(buffer1.array()));
//输出第二个缓冲区中的数据
System.out.println(new String(buffer2.array()));
System.out.println("---------------------聚集写入-------------------");
RandomAccessFile accessFile2=new RandomAccessFile(new File("C:/images/6.png"), "rw");
FileChannel outChannel=accessFile2.getChannel(); //写入数据的通道
//聚集写入,将数据从各个缓冲区中写入到通道中
outChannel.write(dst);
inchannel.close();
outChannel.close();
}
SocketChannel
,ServerSocketChannel
/**
* 客户端使用SocketChannel
* 客户端使用SocketChannel中的write()方法向服务端发送数据,使用read()读取服务端返回的反馈
* 在数据发送完成之后如果不调用shutdownOutput告知服务端数据已传送完成,那么将会一直阻塞下去
* @throws Exception
*/
@Test
public void testClient()throws Exception{
//获取通道
SocketChannel clientChannel = SocketChannel.open(new InetSocketAddress("127.0.0.1",9898));
FileChannel inchannel = FileChannel.open(Paths.get("C:/images/2.png"), StandardOpenOption.READ);
ByteBuffer buffer=ByteBuffer.allocate(1024);
//循环读取本地图片,并且发送到服务端
//1、先使用FileChannel将数据读取到缓冲区中
//2、再使用SocketChannel的write方法将缓冲区的数据发送到服务端
while(inchannel.read(buffer)!=-1){
buffer.flip(); //切换读模式
clientChannel.write(buffer); //发送数据
buffer.clear(); //清空缓冲区
}
//告诉服务端数据已经传送完成,否则将会一直阻塞
clientChannel.shutdownOutput();
//接收服务端的反馈
//使用read()方法接收服务端的反馈,将其读入到缓冲区中
while(clientChannel.read(buffer)>0){
buffer.flip(); //切换读模式
System.out.println("服务端:"+new String(buffer.array()));
buffer.clear();
}
//关闭通道
inchannel.close();
clientChannel.close();
}
/**
* 服务端使用ServerSocketChannel
* 服务端使用SocketChannel的read()方法读取客户端发送的数据,使用write()方法向客户端返回数据
* @throws Exception
*/
@Test
public void testServer()throws Exception{
//获取服务端的通道
ServerSocketChannel serverChannel = ServerSocketChannel.open();
//绑定连接
serverChannel.bind(new InetSocketAddress(9898));
//获取客户端的连接通道
SocketChannel clientChannel = serverChannel.accept();
//申请缓冲区
ByteBuffer byteBuffer=ByteBuffer.allocate(1024);
FileChannel outChannel = FileChannel.open(Paths.get("C:/images/12.png"), StandardOpenOption.WRITE,StandardOpenOption.CREATE);
//循环接收客户端发送过来的数据,并且将其保存在本地
while(clientChannel.read(byteBuffer)>0){
byteBuffer.flip(); //切换读模式
outChannel.write(byteBuffer); //写入到本地
byteBuffer.clear(); //清空缓冲区
}
//服务端发送反馈信息给客户端,使用的还是SocketChannel的write方法
byteBuffer.put("服务端接收数据成功".getBytes());
byteBuffer.flip(); //切换模式
clientChannel.write(byteBuffer);
clientChannel.shutdownOutput(); //告知客户端传输完成
//关闭通道
outChannel.close();
clientChannel.close();
serverChannel.close();
}
SelectionKey.OP_CONNECT
:连接就绪SelectionKey.OP_ACCEPT
:接收就绪SelectionKey.OP_READ
:读就绪SelectionKey.OP_WRITE
:写就绪/**
* 客户端需要使用configureBlocking(false)设置成非阻塞模式的
* @throws Exception
*/
@Test
public void testClient()throws Exception{
//获取通道
SocketChannel client = SocketChannel.open(new InetSocketAddress("127.0.0.1",9898));
//切换成非阻塞模式
client.configureBlocking(false);
ByteBuffer buf=ByteBuffer.allocate(1024); //申请缓冲区
Scanner scanner=new Scanner(System.in);
while(scanner.hasNext()){
String line=scanner.next(); //读取控制台输入的内容
buf.put((new Date().toString()+"\n"+line).getBytes()); //向缓冲区写入数据
buf.flip(); //切换到读模式
client.write(buf); //向服务端发送数据
buf.clear(); //清空缓存区
}
scanner.close();
client.close();
}
/**
* 服务端
* 1、将通道注册到选择器中,并且指定监听的事件
* 2、程序每次都会轮询的从选择器中选择事件,可以选择不同状态的通道进行操作
* @throws Exception
*/
@Test
public void testServer()throws Exception{
//获取通道
ServerSocketChannel server = ServerSocketChannel.open();
//绑定端口
server.bind(new InetSocketAddress(9898));
//配置非阻塞
server.configureBlocking(false);
//获取选择器
Selector selector = Selector.open();
//将通道注册到选择器上,并且知道指定监听的事件为"接收就绪"
server.register(selector, SelectionKey.OP_ACCEPT);
//轮询式获取选择器上已经准备就绪的事件
while(selector.select()>0){
//获取当前选择器中所有的选择键(已经准备就绪的)
Set<SelectionKey> keys = selector.selectedKeys();
//获取迭代器
Iterator<SelectionKey> iterator = keys.iterator();
//迭代器遍历所有的选择键
while(iterator.hasNext()){
//获取当前选择键
SelectionKey key = iterator.next();
iterator.remove(); //删除选择键
if (key.isAcceptable()) { //如果接收就绪了
SocketChannel client = server.accept(); //获取SocketChannel
client.configureBlocking(false); //设置非阻塞模式
client.register(selector, SelectionKey.OP_READ); //将此通道注册到选择器中,指定监听的事件是读就绪
}else if(key.isReadable()){ //如果读就绪
SocketChannel client = (SocketChannel) key.channel(); //读就绪了,那么可以获取通道直接读取数据
ByteBuffer buf=ByteBuffer.allocate(1024); //声明一个缓冲区
//循环接收客户端的到缓冲区中
int len=0;
while((len=client.read(buf))>0){
buf.flip();
System.out.println(new String(buf.array(),0,len));
buf.clear();
}
}else if (key.isWritable()) { //如果写就绪
}else if (key.isConnectable()) { //如果连接就绪
}
}
}
server.close();
}