1. ByteBuffer基本使用
data.txt文件内容
public static void main(String[] args) {
//FileChannel
//1.输入输出流 2.RandomAccessFile
try (FileChannel channel = new FileInputStream("data.txt").getChannel()) {
//准备缓冲区
ByteBuffer buffer = ByteBuffer.allocate(10);
//从Channel读
//channel.read(buffer);
//打印buffer的内容
//buffer.flip();//切换至读数据
// while (buffer.hasRemaining()){
// byte b = buffer.get();
// System.out.println((char) b);
// }
//要把文件读完整,需要循环读
while (channel.read(buffer) != -1) {//len指独到的字节数,如果为-1,则读到文件末尾
buffer.flip();//切换为读模式
while (buffer.hasRemaining()) {
byte b = buffer.get();
System.out.println((char) b);
}
buffer.clear();//切换为写模式
}
} catch (IOException e) {
}
}
2.ByteBuffer结构
- capacity
- position
- limit
一开始为写模式
flip()之后,切换为读模式,position重置到起点,limit改为写数据的末尾
clear()发生后,buffer清空,回到原始状态
调用compact()方法,也可以切换为写模式,区别是不会清空buffer,而是将未读过的数据向前移,把已经读过的数据覆盖掉,叫做压缩
allocate与allocateDirect
System.out.println(ByteBuffer.allocate(10).getClass());
System.out.println(ByteBuffer.allocateDirect(10).getClass());
allocate得到的是堆内存ByteBuffer,会受到GC影响,但是分配速度较快
allocateDirect得到的是直接内存ByteBuffer,直接使用内存,少一次将内存数据转移到堆内存的过程,读写效率更高,但是由于分配的操作系统的内存,分配速度较慢,如果使用不当会造成内存泄漏
3.ByteBuffer常见方法
public static void main(String[] args) {
ByteBuffer buffer = ByteBuffer.allocate(10);
buffer.put(new byte[]{'1','2','3','4','5'});
buffer.flip();
//get(索引),不会改变position
System.out.println((char)buffer.get(1));
while (buffer.hasRemaining()){
System.out.println( (char) buffer.get());
}
//读完之后,可以使用rewind函数,将position置为0;
buffer.rewind();
while (buffer.hasRemaining()){
System.out.println((char) buffer.get());
}
//mark & reset 标记position & 重置到mark的position的位置
buffer.rewind();
System.out.println((char) buffer.get());
System.out.println((char) buffer.get());
buffer.mark();//标记接下来的索引为2的位置
System.out.println((char) buffer.get());
System.out.println((char) buffer.get());
buffer.reset();//将position重置到2
System.out.println((char) buffer.get());
}
ByteBuffer与String的相互转换
public static void main(String[] args) {
//1.字符串转变为ByteBuffer,ByteBuffer的put方法,将字符串转换为byte数组
ByteBuffer buffer = ByteBuffer.allocate(10);
buffer.put("hello".getBytes());
//2.Charset,自动切换为读模式
ByteBuffer encode = StandardCharsets.UTF_8.encode("ghc无敌");
//3,Wrap,自动切换为读模式
ByteBuffer wrap = ByteBuffer.wrap("ghc无敌".getBytes());
//ByteBuffer转为字符串
//1.Charset的decode方法
System.out.println(StandardCharsets.UTF_8.decode(encode));
//注意,如果ByteBuffer还是写模式,直接转会有问题
System.out.println(StandardCharsets.UTF_8.decode(buffer));
//切换为读模式
buffer.flip();
System.out.println(StandardCharsets.UTF_8.decode(buffer));
打印:
Channel分散读:
public static void main(String[] args) {
try (FileChannel channel = new RandomAccessFile("abc.txt", "r").getChannel()) {
ByteBuffer buffer1 = ByteBuffer.allocate(3);
ByteBuffer buffer2 = ByteBuffer.allocate(3);
ByteBuffer buffer3 = ByteBuffer.allocate(5);
channel.read(new ByteBuffer[]{buffer1,buffer2,buffer3});
buffer1.flip();
buffer2.flip();
buffer3.flip();
while (buffer1.hasRemaining()){
System.out.print((char) buffer1.get());
}
System.out.println();
while (buffer2.hasRemaining()){
System.out.print((char) buffer2.get());
}
System.out.println();
while (buffer3.hasRemaining()){
System.out.print((char) buffer3.get());
}
} catch (IOException e) {
}
}
Channel集中写
public static void main(String[] args) {
ByteBuffer hello = StandardCharsets.UTF_8.encode("hello");
ByteBuffer world = StandardCharsets.UTF_8.encode("world");
ByteBuffer hello2 = StandardCharsets.UTF_8.encode("你好");
try (FileChannel channel = new RandomAccessFile("words.txt", "rw").getChannel()) {
channel.write(new ByteBuffer[]{hello,world,hello2});
} catch (IOException e) {
}
}
文件内容:
helloworld你好
粘包半包问题分析
网络传输中,要传输
Hello world\n
I'm Zhangsan\n
How are you\n
三条数据
为了尽可能提高效率,发送方可能一次发生多条数据,即粘包
Hello world\nI'm Zhangsan\nHo
由于接收方的缓存区大小可能有限制,会导致半包
w are you\n
public static void main(String[] args) {
ByteBuffer source = ByteBuffer.allocate(32);
source.put("Hello world\nI'm Zhangsan\nHo".getBytes());
split(source);
source.put("w are you?\n".getBytes());
split(source);
}
static void split(ByteBuffer source){
source.flip();
for (int i = 0; i < source.limit(); i++) {
if(source.get(i)=='\n'){
int length = i+1-source.position();//position是当前读指针的索引,即单词的起点
ByteBuffer target = ByteBuffer.allocate(length);
//从source读到target
for (int j = 0; j < length; j++) {
target.put(source.get());
}
//读完之后,position变为新单词的起点
target.flip();
while (target.hasRemaining()){
System.out.print((char) target.get());
}
}
}
//切换为写模式,注意不能用clear,因为要将未读完的内容继续保留,所以用compact
source.compact();
}