ByteBuffer 粘包和半包(重点)

104 阅读1分钟

概念

粘包

当用户消息通过 TCP 协议传输时,消息可能会被操作系统分组成多个的 TCP 报文,也就是一个完整的用户消息被拆分成多个 TCP 报文进行传输。这时,接收方的程序如果不知道发送方发送的消息的长度,也就是不知道消息的边界时,是无法读出一个有效的用户消息的,因为用户消息被拆分成多个 TCP 报文后,并不能像 UDP 那样,一个 UDP 报文就能代表一个完整的用户消息。发送端使用send 函数 后、数据并没有真正从网络上真正发出,而是从应用程序拷贝到了操作系统的应用程序栈中,至于什么时候发送,取决于发送窗口、拥塞窗口以及当前发送缓冲区的大小等条件。 所以不能认为 TCP 发送数据 会被作为一个整体发送出去。

粘包 : 两个消息被分到同一个TCP 报文。
半包 : 主要因素是由于缓冲区的因素,只能发送一半的数据。 image.png

image.png

解决 (使用换行符作为消息的边界)
public class StickPackage {
    public static void main(String[] args) {
        ByteBuffer allocate = ByteBuffer.allocate(32);
        allocate.put("hello,world\nI'm xiaolin\nHo".getBytes(StandardCharsets.UTF_8));
        split(allocate);
        allocate.put("w are you\n".getBytes(StandardCharsets.UTF_8));
        split(allocate);
    }

    private static void split(ByteBuffer allocate) {
        // byteBuffer 切换至读模式
        allocate.flip();

        for (int i = 0 ; i < allocate.limit(); i ++) {
            byte b = allocate.get(i);
            if (b == '\n') {
                // 存入新的 ByteBuffer
                int len = i - allocate.position() + 1;
                ByteBuffer target = ByteBuffer.allocate(len);
                for (int j = 0; j < len; j++) {
                //相对 获取 方法。读取该缓冲区当前位置的字节,然后增加位置。
                    target.put(allocate.get());
                }
                // 拆分
                System.out.println("postion    "+ allocate.position());
                debugAll(target);
            }
        }
        // 压缩已读,并切换到写模式
        allocate.compact();
    }
}