ByteBuffer解决拆包粘包问题一个小案例

解决拆包问题

1、按行读取文件;

2、拆包、粘包问题;

3、引入 ByteArrayOutputStream 可变字节数组;

4、需要了解ByteBuffer中的position、limit的意义;

需要适配兼容如下场景

1、ByteBuffer 可长可短(1-2048);

2、ByteBuffer中的最后一二个字节可能是一个中文字符的部分字节(一个汉字三个字节);

3、ByteBuffer中的前面一二个字节可能是上次读取最后一个字符的部分字节;

4、某一行字符特别长,ByteBuffer可能不存在换行符号(\n);

5、行字符数特别少,ByteBuffer中可能存中多个换行符号;

目标文件 test.txt

有中文,行有长有短;

📎test.txt

开始

自从

黑化黑灰化肥黑化黑灰化肥黑化黑灰化肥黑化黑灰化肥黑化黑灰化肥黑化黑灰化肥黑化

my name is 化肥黑化黑灰化肥黑化黑灰化肥黑化黑灰化肥黑化黑灰化肥黑化

a

b

cccccc

dd

无所事事

结束

案例代码如下

方案1:

public static void main2() {

        String filePath = "F:\\workspace\\_java\\netty-basic\\src\\main\\java\\com\\io\\basic\\nio\\test.txt";

        // 1. 创建 RandomAccessFile 对象
        try (FileChannel channel = new RandomAccessFile(filePath, "r").getChannel()) {

            // byteBuffer 可长可短
            ByteBuffer byteBuffer = ByteBuffer.allocate(2);

            Charset charset = StandardCharsets.UTF_8;

            ByteBuffer beforeBuffer = null;

            while (channel.read(byteBuffer) > 0) {
                // 切换为读模式
                byteBuffer.flip();

                boolean hasGapCode = false;

                for (int i = 0; i < byteBuffer.limit(); i++) {
                    //
                    char b = (char)byteBuffer.get(i);
                    if (b == '\n') {
                        // byteBuffer 存在换行符
                        hasGapCode = true;
                        int len = i + 1 - byteBuffer.position();
                        ByteBuffer newBuffer = null;
                        if (beforeBuffer != null) {
                            if (beforeBuffer.hasRemaining()) {
                                newBuffer = ByteBuffer.allocate(len + beforeBuffer.limit());
                                for (int j = 0; j < beforeBuffer.limit(); j++) {
                                    newBuffer.put(beforeBuffer.get());
                                }
                                beforeBuffer = null;
                            }
                        }
                        if (newBuffer == null) {
                            newBuffer = ByteBuffer.allocate(len);
                        }
                        for (int j = 0; j < len; j++) {
                            newBuffer.put(byteBuffer.get());
                        }
                        newBuffer.flip();
                        System.out.print(charset.decode(newBuffer));

                    }
                }

                if (hasGapCode) {
                    byteBuffer.compact();
                } else {
                    if(beforeBuffer == null || !beforeBuffer.hasRemaining()) {
                        beforeBuffer = ByteBuffer.allocate(byteBuffer.limit());
                    } else {
                        ByteBuffer temp = beforeBuffer;
                        beforeBuffer = ByteBuffer.allocate(byteBuffer.limit() + temp.limit());
                        for (int i = 0; i < temp.limit(); i++) {
                            beforeBuffer.put(temp.get());
                        }
                    }
                    for (int i = 0; i < byteBuffer.limit(); i++) {
                        beforeBuffer.put(byteBuffer.get());
                    }
                    beforeBuffer.flip();
                    byteBuffer.clear();
                }

            }

            if(beforeBuffer!=null) {
                if (beforeBuffer.hasRemaining()) {
                    System.out.println(charset.decode(beforeBuffer));
                }
            }

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

方案2:

package com.io.basic.nio;

import java.io.ByteArrayOutputStream;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;

/**
 * @author gy
 * @date 2024-02-25
 * 解决按行读取文件内容,粘包拆包的问题
 */
public class ByteBufferDemo {

    public static void main(String[] args) {

        String filePath = "F:\\workspace\\_java\\netty-basic\\src\\main\\java\\com\\io\\basic\\nio\\test.txt";

        // 1. 创建 RandomAccessFile 对象
        try (FileChannel channel = new RandomAccessFile(filePath, "r").getChannel()) {
            
            // 容量是随意设置的
            ByteBuffer byteBuffer = ByteBuffer.allocate(2);

            Charset charset = StandardCharsets.UTF_8;

            // 可变字节数组
            ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();

            while (channel.read(byteBuffer) > 0) {

                // 切换为读模式
                byteBuffer.flip();

                while (byteBuffer.hasRemaining()) {
                    byte b = byteBuffer.get();
                    // 解决拆包(行)问题
                    byteArrayOutputStream.write(b);
                    if (b == '\n') {
                        // 按 换行符 进行拆分
                        System.out.print(new String(byteArrayOutputStream.toByteArray(), charset));
                        byteArrayOutputStream.reset();
                    }
                }

                // 切换写
                byteBuffer.clear();

            }

            if (byteArrayOutputStream.size() > 0) {
                System.out.println(new String(byteArrayOutputStream.toByteArray(), charset));
            }

            byteArrayOutputStream.close();

        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

进窄门见微光行远路

如果对你有比较大的帮助

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值