欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Java NIO三大組件與ByteBuffer深入理解及使用

 更新時(shí)間:2023年01月16日 09:31:19   作者:不死鳥.亞歷山大.狼崽子  
這篇文章主要介紹了Java NIO三大組件與ByteBuffer,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)吧

1、三大組件

1.1 Channel & Buffer

channel 有一點(diǎn)類似于 stream,它就是讀寫數(shù)據(jù)的雙向通道,可以從 channel 將數(shù)據(jù)讀入 buffer,也可以將 buffer 的數(shù)據(jù)寫入 channel,而之前的 stream 要么是輸入,要么是輸出,channel 比 stream 更為底層

常見的 Channel 有

  • FileChannel
  • DatagramChannel
  • SocketChannel
  • ServerSocketChannel

buffer 則用來緩沖讀寫數(shù)據(jù),常見的 buffer 有

  • ByteBuffer
  • MappedByteBuffer
  • DirectByteBuffer
  • HeapByteBuffer
  • ShortBuffer
  • IntBuffer
  • LongBuffer
  • FloatBuffer
  • DoubleBuffer
  • CharBuffer

1.2 Selector

selector 單從字面意思不好理解,需要結(jié)合服務(wù)器的設(shè)計(jì)演化來理解它的用途

多線程版設(shè)計(jì)

多線程版缺點(diǎn)

  • 內(nèi)存占用高
  • 線程上下文切換成本高
  • 只適合連接數(shù)少的場(chǎng)景

線程池版設(shè)計(jì)

線程池版缺點(diǎn)

  • 阻塞模式下,線程僅能處理一個(gè) socket 連接
  • 僅適合短連接場(chǎng)景

selector 版設(shè)計(jì)

selector 的作用就是配合一個(gè)線程來管理多個(gè) channel,獲取這些 channel 上發(fā)生的事件,這些 channel 工作在非阻塞模式下,不會(huì)讓線程吊死在一個(gè) channel 上。適合連接數(shù)特別多,但流量低的場(chǎng)景(low traffic)

調(diào)用 selector 的 select() 會(huì)阻塞直到 channel 發(fā)生了讀寫就緒事件,這些事件發(fā)生,select 方法就會(huì)返回這些事件交給 thread 來處理

2、ByteBuffer

有一普通文本文件 data.txt,內(nèi)容為

1234567890abcd

使用 FileChannel 來讀取文件內(nèi)容

package org.example.demo1;
import lombok.extern.slf4j.Slf4j;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
@Slf4j
public class ChannelDemo1 {
    public static void main(String[] args) {
        try (FileChannel channel = new FileInputStream("data.txt").getChannel()) {
            ByteBuffer buffer = ByteBuffer.allocate(10);
            do {
                // 向 buffer 寫入
                int len = channel.read(buffer);
                log.debug("讀到字節(jié)數(shù):{}", len);
                if (len == -1) {
                    break;
                }
                // 切換 buffer 讀模式
                buffer.flip();
                while(buffer.hasRemaining()) {
                    byte b = buffer.get();
                    log.debug("實(shí)際字節(jié){}", (char)b);
                }
                // 切換 buffer 寫模式
                buffer.clear();
            } while (true);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

輸出

15:03:39.467 [main] DEBUG org.example.demo1.ChannelDemo1 - 讀到字節(jié)數(shù):10
15:03:39.475 [main] DEBUG org.example.demo1.ChannelDemo1 - 實(shí)際字節(jié)1
15:03:39.475 [main] DEBUG org.example.demo1.ChannelDemo1 - 實(shí)際字節(jié)2
15:03:39.476 [main] DEBUG org.example.demo1.ChannelDemo1 - 實(shí)際字節(jié)3
15:03:39.476 [main] DEBUG org.example.demo1.ChannelDemo1 - 實(shí)際字節(jié)4
15:03:39.476 [main] DEBUG org.example.demo1.ChannelDemo1 - 實(shí)際字節(jié)5
15:03:39.476 [main] DEBUG org.example.demo1.ChannelDemo1 - 實(shí)際字節(jié)6
15:03:39.476 [main] DEBUG org.example.demo1.ChannelDemo1 - 實(shí)際字節(jié)7
15:03:39.476 [main] DEBUG org.example.demo1.ChannelDemo1 - 實(shí)際字節(jié)8
15:03:39.476 [main] DEBUG org.example.demo1.ChannelDemo1 - 實(shí)際字節(jié)9
15:03:39.476 [main] DEBUG org.example.demo1.ChannelDemo1 - 實(shí)際字節(jié)0
15:03:39.476 [main] DEBUG org.example.demo1.ChannelDemo1 - 讀到字節(jié)數(shù):4
15:03:39.477 [main] DEBUG org.example.demo1.ChannelDemo1 - 實(shí)際字節(jié)a
15:03:39.477 [main] DEBUG org.example.demo1.ChannelDemo1 - 實(shí)際字節(jié)b
15:03:39.477 [main] DEBUG org.example.demo1.ChannelDemo1 - 實(shí)際字節(jié)c
15:03:39.477 [main] DEBUG org.example.demo1.ChannelDemo1 - 實(shí)際字節(jié)d
15:03:39.477 [main] DEBUG org.example.demo1.ChannelDemo1 - 讀到字節(jié)數(shù):-1

2.1 ByteBuffer 正確使用姿勢(shì)

  • 向 buffer 寫入數(shù)據(jù),例如調(diào)用 channel.read(buffer)
  • 調(diào)用 flip() 切換至讀模式
  • 從 buffer 讀取數(shù)據(jù),例如調(diào)用 buffer.get()
  • 調(diào)用 clear() 或 compact() 切換至寫模式
  • 重復(fù) 1~4 步驟

2.2 ByteBuffer 結(jié)構(gòu)

ByteBuffer 有以下重要屬性

  • capacity
  • position
  • limit

一開始

寫模式下,position 是寫入位置,limit 等于容量,下圖表示寫入了 4 個(gè)字節(jié)后的狀態(tài)

flip 動(dòng)作發(fā)生后,position 切換為讀取位置,limit 切換為讀取限制

讀取 4 個(gè)字節(jié)后,狀態(tài)

clear 動(dòng)作發(fā)生后,狀態(tài)

compact 方法,是把未讀完的部分向前壓縮,然后切換至寫模式

調(diào)試工具類

package org.example.utils;
import io.netty.util.internal.StringUtil;
import java.nio.ByteBuffer;
import static io.netty.util.internal.MathUtil.isOutOfBounds;
import static io.netty.util.internal.StringUtil.NEWLINE;
public class ByteBufferUtil {
    private static final char[] BYTE2CHAR = new char[256];
    private static final char[] HEXDUMP_TABLE = new char[256 * 4];
    private static final String[] HEXPADDING = new String[16];
    private static final String[] HEXDUMP_ROWPREFIXES = new String[65536 >>> 4];
    private static final String[] BYTE2HEX = new String[256];
    private static final String[] BYTEPADDING = new String[16];
    static {
        final char[] DIGITS = "0123456789abcdef".toCharArray();
        for (int i = 0; i < 256; i++) {
            HEXDUMP_TABLE[i << 1] = DIGITS[i >>> 4 & 0x0F];
            HEXDUMP_TABLE[(i << 1) + 1] = DIGITS[i & 0x0F];
        }
        int i;
        // Generate the lookup table for hex dump paddings
        for (i = 0; i < HEXPADDING.length; i++) {
            int padding = HEXPADDING.length - i;
            StringBuilder buf = new StringBuilder(padding * 3);
            for (int j = 0; j < padding; j++) {
                buf.append("   ");
            }
            HEXPADDING[i] = buf.toString();
        }
        // Generate the lookup table for the start-offset header in each row (up to 64KiB).
        for (i = 0; i < HEXDUMP_ROWPREFIXES.length; i++) {
            StringBuilder buf = new StringBuilder(12);
            buf.append(NEWLINE);
            buf.append(Long.toHexString(i << 4 & 0xFFFFFFFFL | 0x100000000L));
            buf.setCharAt(buf.length() - 9, '|');
            buf.append('|');
            HEXDUMP_ROWPREFIXES[i] = buf.toString();
        }
        // Generate the lookup table for byte-to-hex-dump conversion
        for (i = 0; i < BYTE2HEX.length; i++) {
            BYTE2HEX[i] = ' ' + StringUtil.byteToHexStringPadded(i);
        }
        // Generate the lookup table for byte dump paddings
        for (i = 0; i < BYTEPADDING.length; i++) {
            int padding = BYTEPADDING.length - i;
            StringBuilder buf = new StringBuilder(padding);
            for (int j = 0; j < padding; j++) {
                buf.append(' ');
            }
            BYTEPADDING[i] = buf.toString();
        }
        // Generate the lookup table for byte-to-char conversion
        for (i = 0; i < BYTE2CHAR.length; i++) {
            if (i <= 0x1f || i >= 0x7f) {
                BYTE2CHAR[i] = '.';
            } else {
                BYTE2CHAR[i] = (char) i;
            }
        }
    }
    /**
     * 打印所有內(nèi)容
     * @param buffer
     */
    public static void debugAll(ByteBuffer buffer) {
        int oldlimit = buffer.limit();
        buffer.limit(buffer.capacity());
        StringBuilder origin = new StringBuilder(256);
        appendPrettyHexDump(origin, buffer, 0, buffer.capacity());
        System.out.println("+--------+-------------------- all ------------------------+----------------+");
        System.out.printf("position: [%d], limit: [%d]\n", buffer.position(), oldlimit);
        System.out.println(origin);
        buffer.limit(oldlimit);
    }
    /**
     * 打印可讀取內(nèi)容
     * @param buffer
     */
    public static void debugRead(ByteBuffer buffer) {
        StringBuilder builder = new StringBuilder(256);
        appendPrettyHexDump(builder, buffer, buffer.position(), buffer.limit() - buffer.position());
        System.out.println("+--------+-------------------- read -----------------------+----------------+");
        System.out.printf("position: [%d], limit: [%d]\n", buffer.position(), buffer.limit());
        System.out.println(builder);
    }
    private static void appendPrettyHexDump(StringBuilder dump, ByteBuffer buf, int offset, int length) {
        if (isOutOfBounds(offset, length, buf.capacity())) {
            throw new IndexOutOfBoundsException(
                    "expected: " + "0 <= offset(" + offset + ") <= offset + length(" + length
                            + ") <= " + "buf.capacity(" + buf.capacity() + ')');
        }
        if (length == 0) {
            return;
        }
        dump.append(
                "         +-------------------------------------------------+" +
                        NEWLINE + "         |  0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f |" +
                        NEWLINE + "+--------+-------------------------------------------------+----------------+");
        final int startIndex = offset;
        final int fullRows = length >>> 4;
        final int remainder = length & 0xF;
        // Dump the rows which have 16 bytes.
        for (int row = 0; row < fullRows; row++) {
            int rowStartIndex = (row << 4) + startIndex;
            // Per-row prefix.
            appendHexDumpRowPrefix(dump, row, rowStartIndex);
            // Hex dump
            int rowEndIndex = rowStartIndex + 16;
            for (int j = rowStartIndex; j < rowEndIndex; j++) {
                dump.append(BYTE2HEX[getUnsignedByte(buf, j)]);
            }
            dump.append(" |");
            // ASCII dump
            for (int j = rowStartIndex; j < rowEndIndex; j++) {
                dump.append(BYTE2CHAR[getUnsignedByte(buf, j)]);
            }
            dump.append('|');
        }
        // Dump the last row which has less than 16 bytes.
        if (remainder != 0) {
            int rowStartIndex = (fullRows << 4) + startIndex;
            appendHexDumpRowPrefix(dump, fullRows, rowStartIndex);
            // Hex dump
            int rowEndIndex = rowStartIndex + remainder;
            for (int j = rowStartIndex; j < rowEndIndex; j++) {
                dump.append(BYTE2HEX[getUnsignedByte(buf, j)]);
            }
            dump.append(HEXPADDING[remainder]);
            dump.append(" |");
            // Ascii dump
            for (int j = rowStartIndex; j < rowEndIndex; j++) {
                dump.append(BYTE2CHAR[getUnsignedByte(buf, j)]);
            }
            dump.append(BYTEPADDING[remainder]);
            dump.append('|');
        }
        dump.append(NEWLINE +
                "+--------+-------------------------------------------------+----------------+");
    }
    private static void appendHexDumpRowPrefix(StringBuilder dump, int row, int rowStartIndex) {
        if (row < HEXDUMP_ROWPREFIXES.length) {
            dump.append(HEXDUMP_ROWPREFIXES[row]);
        } else {
            dump.append(NEWLINE);
            dump.append(Long.toHexString(rowStartIndex & 0xFFFFFFFFL | 0x100000000L));
            dump.setCharAt(dump.length() - 9, '|');
            dump.append('|');
        }
    }
    public static short getUnsignedByte(ByteBuffer buffer, int index) {
        return (short) (buffer.get(index) & 0xFF);
    }
}

測(cè)試如下:

package org.example.demo1;
import java.nio.ByteBuffer;
import static org.example.utils.ByteBufferUtil.debugAll;
public class TestByteBufferReadWrite {
    public static void main(String[] args){
        ByteBuffer byteBuffer = ByteBuffer.allocate(10);
        byteBuffer.put((byte) 0x61);// a
        debugAll(byteBuffer);
        byteBuffer.put(new byte[]{0x62,0x63,0x64});
        debugAll(byteBuffer);
        byteBuffer.get();
        debugAll(byteBuffer);
        //切換為讀的狀態(tài)
        byteBuffer.flip();
        byteBuffer.get();
        debugAll(byteBuffer);
        byteBuffer.compact();
        debugAll(byteBuffer);
    }
}

運(yùn)行結(jié)果如下:

18:12:55.063 [main] DEBUG io.netty.util.internal.logging.InternalLoggerFactory - Using SLF4J as the default logging framework
+--------+-------------------- all ------------------------+----------------+
position: [1], limit: [10]
         +-------------------------------------------------+
         |  0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f |
+--------+-------------------------------------------------+----------------+
|00000000| 61 00 00 00 00 00 00 00 00 00                   |a.........      |
+--------+-------------------------------------------------+----------------+
+--------+-------------------- all ------------------------+----------------+
position: [4], limit: [10]
         +-------------------------------------------------+
         |  0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f |
+--------+-------------------------------------------------+----------------+
|00000000| 61 62 63 64 00 00 00 00 00 00                   |abcd......      |
+--------+-------------------------------------------------+----------------+
+--------+-------------------- all ------------------------+----------------+
position: [5], limit: [10]
         +-------------------------------------------------+
         |  0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f |
+--------+-------------------------------------------------+----------------+
|00000000| 61 62 63 64 00 00 00 00 00 00                   |abcd......      |
+--------+-------------------------------------------------+----------------+
+--------+-------------------- all ------------------------+----------------+
position: [1], limit: [5]
         +-------------------------------------------------+
         |  0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f |
+--------+-------------------------------------------------+----------------+
|00000000| 61 62 63 64 00 00 00 00 00 00                   |abcd......      |
+--------+-------------------------------------------------+----------------+
+--------+-------------------- all ------------------------+----------------+
position: [4], limit: [10]
         +-------------------------------------------------+
         |  0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f |
+--------+-------------------------------------------------+----------------+
|00000000| 62 63 64 00 00 00 00 00 00 00                   |bcd.......      |
+--------+-------------------------------------------------+----------------+
 
Process finished with exit code 0

2.3 ByteBuffer 常見方法

分配空間

可以使用 allocate 方法為 ByteBuffer 分配空間,其它 buffer 類也有該方法

Bytebuffer buf = ByteBuffer.allocate(16);

例子:

package org.example.demo1;
import java.nio.ByteBuffer;
public class TestByteBufferAllocate {
    public static void main(String[] args){
        System.out.println(ByteBuffer.allocate(16).getClass());
        System.out.println(ByteBuffer.allocateDirect(16).getClass());
    }
}

運(yùn)行結(jié)果如下:

注意:

class java.nio.HeapByteBuffer -java 堆內(nèi)存,讀寫效率低,受到GC的影響 class java.nio.DirectByteBuffer -直接內(nèi)存,讀寫效率高(少一次拷貝),不會(huì)受GC影響,分配的效率低

向 buffer 寫入數(shù)據(jù)

有兩種辦法

  • 調(diào)用 channel 的 read 方法
  • 調(diào)用 buffer 自己的 put 方法
int readBytes = channel.read(buf);

buf.put((byte)127);

從 buffer 讀取數(shù)據(jù)

同樣有兩種辦法

  • 調(diào)用channel的write方法
  • 調(diào)用buffer自己的get方法
int writeBytes = channel.write(buf);

byte b = buf.get();

get 方法會(huì)讓 position 讀指針向后走,如果想重復(fù)讀取數(shù)據(jù)

可以調(diào)用 rewind 方法將 position 重新置為 0

package org.example.demo1;
import java.nio.ByteBuffer;
import static org.example.utils.ByteBufferUtil.debugAll;
public class TestByteBufferRead {
    public static void main(String[] args){
        ByteBuffer buffer = ByteBuffer.allocate(10);
        buffer.put(new byte[]{'a','b','c','d'});
        buffer.flip();
        //rewind 從頭開始讀
        buffer.get(new  byte[4]);
        debugAll(buffer);
        System.out.println("===============================rewind================================");
        buffer.rewind();
        System.out.println((char)buffer.get());
    }
}

調(diào)用結(jié)果:

+--------+-------------------- all ------------------------+----------------+
position: [4], limit: [4]
         +-------------------------------------------------+
         |  0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f |
+--------+-------------------------------------------------+----------------+
|00000000| 61 62 63 64 00 00 00 00 00 00                   |abcd......      |
+--------+-------------------------------------------------+----------------+
===============================rewind================================
a

或者調(diào)用 get(int i) 方法獲取索引 i 的內(nèi)容,它不會(huì)移動(dòng)讀指針

package org.example.demo1;
import java.nio.ByteBuffer;
import static org.example.utils.ByteBufferUtil.debugAll;
public class TestByteBufferRead {
    public static void main(String[] args){
        ByteBuffer buffer = ByteBuffer.allocate(10);
        buffer.put(new byte[]{'a','b','c','d'});
        buffer.flip();
        //get(i) 不會(huì)改變讀索引的位置
        System.out.println((char) buffer.get(3));
        debugAll(buffer);
    }
}

調(diào)用結(jié)果:

mark 和 reset

mark 是在讀取時(shí),做一個(gè)標(biāo)記,即使 position 改變,只要調(diào)用 reset 就能回到 mark 的位置

package org.example.demo1;
import java.nio.ByteBuffer;
import static org.example.utils.ByteBufferUtil.debugAll;
public class TestByteBufferRead {
    public static void main(String[] args){
        ByteBuffer buffer = ByteBuffer.allocate(10);
        buffer.put(new byte[]{'a','b','c','d'});
        buffer.flip();
        //mark & reset
        //mark 做一個(gè)標(biāo)記,記錄position位置,reset 是將position重置到mark的位置
        System.out.println((char) buffer.get());
        System.out.println((char) buffer.get());
        buffer.mark();//加標(biāo)記,索引2的位置
        System.out.println((char) buffer.get());
        System.out.println((char) buffer.get());
        buffer.reset();//將position重置到索引2
        System.out.println((char) buffer.get());
        System.out.println((char) buffer.get());
    }
}

測(cè)試結(jié)果:

a
b
c
d
c
d

注意

rewind 和 flip 都會(huì)清除 mark 位置

字符串與ByteBuffer互轉(zhuǎn)

package org.example.demo1;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import static org.example.utils.ByteBufferUtil.debugAll;
public class TestByteBufferString {
    public static void main(String[] args){
        ByteBuffer buffer = ByteBuffer.allocate(16);
        buffer.put("hello".getBytes());
        debugAll(buffer);
        buffer.flip();
        CharBuffer charBuffer = StandardCharsets.UTF_8.decode(buffer);
        String charBufferstr = charBuffer.toString();
        System.out.println(charBufferstr);
        //2.Charset
        ByteBuffer buffer2 = StandardCharsets.UTF_8.encode("hello");
        debugAll(buffer2);
        CharBuffer charBuffer1 = StandardCharsets.UTF_8.decode(buffer2);
        String buffer1 = charBuffer1.toString();
        System.out.println(buffer1);
        //3.wrap
        ByteBuffer buffer3 = ByteBuffer.wrap("hello".getBytes());
        debugAll(buffer3);
        CharBuffer charBuffer3 = StandardCharsets.UTF_8.decode(buffer3);
        String bufferstr3 = charBuffer3.toString();
        System.out.println(bufferstr3);
    }
}

輸出:

+--------+-------------------- all ------------------------+----------------+
position: [5], limit: [16]
         +-------------------------------------------------+
         |  0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f |
+--------+-------------------------------------------------+----------------+
|00000000| 68 65 6c 6c 6f 00 00 00 00 00 00 00 00 00 00 00 |hello...........|
+--------+-------------------------------------------------+----------------+
hello
+--------+-------------------- all ------------------------+----------------+
position: [0], limit: [5]
         +-------------------------------------------------+
         |  0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f |
+--------+-------------------------------------------------+----------------+
|00000000| 68 65 6c 6c 6f                                  |hello           |
+--------+-------------------------------------------------+----------------+
+--------+-------------------- all ------------------------+----------------+
position: [0], limit: [5]
         +-------------------------------------------------+
         |  0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f |
+--------+-------------------------------------------------+----------------+
|00000000| 68 65 6c 6c 6f                                  |hello           |
+--------+-------------------------------------------------+----------------+
hello

Buffer的線程安全

Buffer是非線程安全的

2.4 Scattering Reads

分散讀取,有一個(gè)文本文件parts.txt

onetwothree

使用如下方式讀取,可以將數(shù)據(jù)填充至多個(gè) buffer

package org.example.demo1;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import static org.example.utils.ByteBufferUtil.debugAll;
public class TestByteBufferReads {
    public static void main(String[] args){
        try (RandomAccessFile file = new RandomAccessFile("parts.txt", "r")) {
            FileChannel channel = file.getChannel();
            ByteBuffer a = ByteBuffer.allocate(3);
            ByteBuffer b = ByteBuffer.allocate(3);
            ByteBuffer c = ByteBuffer.allocate(5);
            channel.read(new ByteBuffer[]{a, b, c});
            a.flip();
            b.flip();
            c.flip();
            debugAll(a);
            debugAll(b);
            debugAll(c);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

結(jié)果:

+--------+-------------------- all ------------------------+----------------+
position: [0], limit: [3]
         +-------------------------------------------------+
         |  0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f |
+--------+-------------------------------------------------+----------------+
|00000000| 6f 6e 65                                        |one             |
+--------+-------------------------------------------------+----------------+
+--------+-------------------- all ------------------------+----------------+
position: [0], limit: [3]
         +-------------------------------------------------+
         |  0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f |
+--------+-------------------------------------------------+----------------+
|00000000| 74 77 6f                                        |two             |
+--------+-------------------------------------------------+----------------+
+--------+-------------------- all ------------------------+----------------+
position: [0], limit: [5]
         +-------------------------------------------------+
         |  0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f |
+--------+-------------------------------------------------+----------------+
|00000000| 74 68 72 65 65                                  |three           |
+--------+-------------------------------------------------+----------------+

2.5 Gathering Writes

使用如下方式寫入,可以將多個(gè) buffer 的數(shù)據(jù)填充至 channel

package org.example.demo1;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.charset.StandardCharsets;
public class TestGatheringWrites {
    public static void main(String[] args){
        ByteBuffer b1 = StandardCharsets.UTF_8.encode("hello");
        ByteBuffer b2 = StandardCharsets.UTF_8.encode("world");
        ByteBuffer b3 = StandardCharsets.UTF_8.encode("你好");
        try(FileChannel channel = new RandomAccessFile("words2.txt","rw").getChannel()){
            channel.write(new ByteBuffer[]{b1,b2,b3});
        }catch (IOException ex){
        }
    }
}

輸出結(jié)果:

2.6 黏包半包現(xiàn)象

網(wǎng)絡(luò)上有多條數(shù)據(jù)發(fā)送給服務(wù)端,數(shù)據(jù)之間使用 \n 進(jìn)行分隔 但由于某種原因這些數(shù)據(jù)在接收時(shí),被進(jìn)行了重新組合,例如原始數(shù)據(jù)有3條為

  • Hello,world\n
  • I'm zhangsan\n
  • How are you?\n

變成了下面的兩個(gè) byteBuffer (黏包,半包)

  • Hello,world\nI'm zhangsan\nHo
  • w are you?\n

現(xiàn)在要求你編寫程序,將錯(cuò)亂的數(shù)據(jù)恢復(fù)成原始的按 \n 分隔的數(shù)據(jù)

public static void main(String[] args) {
    ByteBuffer source = ByteBuffer.allocate(32);
    //                     11            24
    source.put("Hello,world\nI'm zhangsan\nHo".getBytes());
    split(source);
    source.put("w are you?\nhaha!\n".getBytes());
    split(source);
}
private static void split(ByteBuffer source) {
    source.flip();
    int oldLimit = source.limit();
    for (int i = 0; i < oldLimit; i++) {
        if (source.get(i) == '\n') {
            System.out.println(i);
            ByteBuffer target = ByteBuffer.allocate(i + 1 - source.position());
            // 0 ~ limit
            source.limit(i + 1);
            target.put(source); // 從source 讀,向 target 寫
            debugAll(target);
            source.limit(oldLimit);
        }
    }
    source.compact();
}

到此這篇關(guān)于Java NIO三大組件與ByteBuffer深入理解及使用的文章就介紹到這了,更多相關(guān)Java NIO三大組件內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Java類中static{}的具體使用

    Java類中static{}的具體使用

    static{}(即static塊),會(huì)在類被加載的時(shí)候執(zhí)行且僅會(huì)被執(zhí)行一次,一般用來初始化靜態(tài)變量和調(diào)用靜態(tài)方法,本文主要介紹了Java類中static{}的具體使用,感興趣的可以了解一下
    2024-07-07
  • Springboot如何實(shí)現(xiàn)代理服務(wù)器

    Springboot如何實(shí)現(xiàn)代理服務(wù)器

    這篇文章主要介紹了Springboot如何實(shí)現(xiàn)代理服務(wù)器問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-06-06
  • Spring如何基于Proxy及cglib實(shí)現(xiàn)動(dòng)態(tài)代理

    Spring如何基于Proxy及cglib實(shí)現(xiàn)動(dòng)態(tài)代理

    這篇文章主要介紹了Spring如何基于Proxy及cglib實(shí)現(xiàn)動(dòng)態(tài)代理,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-06-06
  • 如何剔除eureka無效和down狀態(tài)的問題

    如何剔除eureka無效和down狀態(tài)的問題

    這篇文章主要介紹了如何剔除eureka無效和down狀態(tài)的問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-07-07
  • springboot文件虛擬路徑映射方式

    springboot文件虛擬路徑映射方式

    這篇文章主要介紹了springboot文件虛擬路徑映射方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-08-08
  • Java基于Netty實(shí)現(xiàn)Http server的實(shí)戰(zhàn)

    Java基于Netty實(shí)現(xiàn)Http server的實(shí)戰(zhàn)

    本文主要介紹了Java基于Netty實(shí)現(xiàn)Http server的實(shí)戰(zhàn),文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2022-02-02
  • java final 和instanceof 關(guān)鍵字的區(qū)別

    java final 和instanceof 關(guān)鍵字的區(qū)別

    這篇文章介紹了java final 和instanceof 關(guān)鍵字的區(qū)別,有需要的朋友可以參考一下
    2013-09-09
  • SpringBoot圖文并茂講解登錄攔截器

    SpringBoot圖文并茂講解登錄攔截器

    其實(shí)spring boot攔截器的配置方式和springMVC差不多,只有一些小的改變需要注意下就ok了,下面這篇文章主要給大家介紹了關(guān)于如何在Springboot實(shí)現(xiàn)登陸攔截器功能的相關(guān)資料,需要的朋友可以參考下
    2022-06-06
  • 手把手教你寫一個(gè)spring IOC容器的方法

    手把手教你寫一個(gè)spring IOC容器的方法

    這篇文章主要介紹了手把手教你寫一個(gè)spring IOC容器的方法,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2021-04-04
  • java后端如何獲取完整url的代碼

    java后端如何獲取完整url的代碼

    這篇文章主要介紹了java后端如何獲取完整url的代碼問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-12-12

最新評(píng)論