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

Netty NIO之ByteBuffer類基礎學習

 更新時間:2023年06月09日 09:08:41   作者:s?????  
這篇文章主要為大家介紹了Netty NIO之ByteBuffer類基礎學習,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪

什么是 Netty?

Netty 是一個高性能、異步事件驅動的網絡應用程序框架,主要用于快速開發(fā)可維護、可擴展的高性能服務器和客戶端。Netty 提供了簡單易用的 API,支持多種協(xié)議和傳輸方式,并且有著高度靈活的擴展和自定義能力。

Netty 的設計目標是提供一種易于使用、高效、可擴展的異步 IO 網絡編程框架。它采用了 NIO(Non-blocking IO)的方式來進行網絡操作,避免了傳統(tǒng)的阻塞式 IO 常常面臨的性能瓶頸。同時,Netty 還提供了優(yōu)秀的線程模型和內存管理機制,保證了高并發(fā)下的穩(wěn)定性和性能。

通過 Netty,開發(fā)者可以方便地實現(xiàn)基于 TCP、UDP、HTTP、WebSocket 等多種協(xié)議的通信應用。同時,Netty 還提供了編解碼器、SSL 支持等組件,使得開發(fā)者可以更加專注于業(yè)務邏輯的實現(xiàn)。

什么是 ByteBuffer?

ByteBuffer 是 Java 中的一個類,它提供了一種方便的方式來處理原始字節(jié)數(shù)據(jù)。ByteBuffer 可以被看作是一個緩沖區(qū),它可以容納一定數(shù)量的字節(jié)數(shù)據(jù),并提供了一系列方法來操作這些數(shù)據(jù)。

使用 ByteBuffer,可以輕松地讀取和寫入二進制數(shù)據(jù)。它還提供了對不同類型數(shù)據(jù)的支持,如整數(shù)、浮點數(shù)等。ByteBuffer 還支持對數(shù)據(jù)進行切片,以及對緩沖區(qū)中的數(shù)據(jù)進行復制、壓縮、解壓等操作。

在 Java 中,ByteBuffer 通常用于處理 I/O 操作,例如從文件或網絡中讀取和寫入數(shù)據(jù)。它也可以用于處理加密和解密數(shù)據(jù),以及處理圖像和音頻文件等二進制數(shù)據(jù)??傊?,ByteBuffer 是 Java 中非常有用的一個類,可以幫助開發(fā)人員更輕松地處理二進制數(shù)據(jù)。

基本使用

  • 向 buffer 寫入數(shù)據(jù),例如調用 channel.read(buffer);
  • 調用 flip() 切換至讀模式

    • flip 會使得 buffer 中的 limit 變?yōu)?position,position 變?yōu)?0;
  • 從 buffer 讀取數(shù)據(jù),例如調用 buffer.get();
  • 調用 clear() 或者 compact() 切換至寫模式;

    • 調用 clear() 方法時,position=0,limit 變?yōu)?capacity
    • 調用 compact() 方法時,會將緩沖區(qū)中的未讀數(shù)據(jù)壓縮到緩沖區(qū)前面;
  • 重復 1~4 的步驟;

編寫代碼進行測試:

@Slf4j
public class TestByteBuffer {

    public static void main(String[] args) {

        try (FileChannel channel = new FileInputStream("data.txt").getChannel()) {
            // 準備緩沖區(qū)
            ByteBuffer buffer = ByteBuffer.allocate(10);
            while (true) {
                // 從 channel 讀取數(shù)據(jù)寫入到 buffer
                int len = channel.read(buffer);
                log.debug("讀取到的字節(jié)數(shù) {}", len);
                if (len == -1) break;

                // 打印 buffer 內容
                buffer.flip();  // 切換至讀模式
                while(buffer.hasRemaining()) {  // 是否還有剩余未讀數(shù)據(jù)
                    byte b = buffer.get();
                    log.debug("實際字節(jié) {}", (char)b);
                }
                buffer.clear();
            }
        } catch (IOException e) {

        }

    }

}

運行結果:

注意,日志需要進行配置,在 /src/main/resources/ 路徑下,創(chuàng)建 logback.xml,其中的內容如下:

<?xml version="1.0" encoding="utf-8" ?>
<configuration
        xmlns="http://ch.qos.logback/xml/ns/logback"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://ch.qos.logback/xml/ns/logback logback.xsd">

    <!-- 輸出控制,格式控制 -->
    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <pattern>%date{HH:mm:ss} [%-5level] [%thread] %logger{17} - %m%n </pattern>
        </encoder>
    </appender>
    <appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <!-- 日志文件名稱 -->
        <file>logFile.log</file>
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <!-- 每天產生一個新的日志文件 -->
            <fileNamePattern>logFile.%d{yyyy-MM-dd}.log</fileNamePattern>
            <!-- 保留 15 天的日志 -->
            <maxHistory>15</maxHistory>
        </rollingPolicy>
        <encoder>
            <pattern>%date{HH:mm:ss} [%-5level] [%thread] %logger{17} - %m%n </pattern>
        </encoder>
    </appender>

    <!-- 用來控制查看哪個類的日志內容(對 mybatis name 代表命名空間)-->
    <logger name="com.sidiot.netty" level="DEBUG" additivity="false">
        <appender-ref ref="STDOUT" />
    </logger>

    <root level="ERROR">
        <appender-ref ref="STDOUT" />
    </root>
</configuration>

將末尾部分的 <logger name="com.sidiot.netty" level="DEBUG" additivity="false"> 中的 name 的屬性值改成自己的包名即可。

部分讀者可能會遇到如下問題:

這是由于 lombok 引起的,需要檢查一下是否安裝了 lombok 的插件,以及是否是最新版的 lombok,博主這里用的版本如下:

&lt;dependency&gt;  
    &lt;groupId&gt;org.projectlombok&lt;/groupId&gt;  
    &lt;artifactId&gt;lombok&lt;/artifactId&gt;  
    &lt;version&gt;1.18.26&lt;/version&gt;  
&lt;/dependency&gt;

內部結構

字節(jié)緩沖區(qū)的父類 Buffer 中有四個核心屬性,從以下源碼中可以清晰獲知:

// Invariants: mark &lt;= position &lt;= limit &lt;= capacity  
private int mark = -1;  
private int position = 0;  
private int limit;  
private int capacity;
  • position:表示當前緩沖區(qū)中下一個要被讀或寫的字節(jié)索引位置,默認值為 0。當我們調用 put() 方法往緩沖區(qū)中寫入數(shù)據(jù)時,position 會自動向后移動,指向下一個可寫的位置;當我們調用 get() 方法從緩沖區(qū)中讀取數(shù)據(jù)時,position 也會自動向后移動,指向下一個可讀的位置。
  • limit:表示當前緩沖區(qū)的限制大小,默認值為 capacity。在寫模式下,limit 表示緩沖區(qū)最多能夠寫入的字節(jié)數(shù);在讀模式下,limit 表示緩沖區(qū)最多能夠讀取的字節(jié)數(shù)。在一些場景下,我們可以通過設置 limit 來防止越界訪問緩沖區(qū)。
  • capacity:表示緩沖區(qū)的容量大小,默認創(chuàng)建 Buffer 對象時指定。capacity 只能在創(chuàng)建緩沖區(qū)時指定,并且不能改變。例如,我們可以創(chuàng)建一個容量為 1024 字節(jié)的 Buffer 對象,然后往里面寫入不超過 1024 字節(jié)的數(shù)據(jù)。
  • mark:mark 和 reset 方法一起使用,用于記錄和恢復 position 的值。在 ByteBuffer 中,我們可以通過調用 mark() 方法來記錄當前 position 的值,然后隨意移動 position,最后再通過調用 reset() 方法將 position 恢復到 mark 記錄的位置。使用 mark 和 reset 可以在某些情況下提高代碼的效率,避免頻繁地重新計算或查詢某個值。

這些屬性一起組成了 Buffer 的狀態(tài),我們可以根據(jù)它們的值來確定當前緩沖區(qū)的狀態(tài)和可操作范圍。

初始化時,position,limit,capacity 的位置如下:

寫模式下,position 代表寫入位置,limit 代表寫入容量,寫入3個字節(jié)后的狀態(tài)如下圖所示:

當使用 flip() 函數(shù)切換至讀模式后,position 切換為讀取位置,limit 切換為讀取限制:

這個變換也可以從 flip() 的源碼清晰的獲知:

public Buffer flip() {
    limit = position;
    position = 0;
    mark = -1;
    return this;
}

當讀完之后,使用 clean() 函數(shù)清空緩存區(qū),可從源碼獲知,緩沖區(qū)又變成了初始化時的狀態(tài):

public Buffer clear() {
    position = 0;
    limit = capacity;
    mark = -1;
    return this;
}

這里還有一種方法 compact(),其作用是將未讀完的部分向前壓縮,然后切換至寫模式,不過需要注意的是,這是 ByteBuffer 中的方法:

接下來,將要結合代碼對上述內容進行深入理解;

這里用到了一個自定義的工具類 ByteBufferUtil,由于篇幅原因,自行從我的 Github 上進行獲?。?a rel="external nofollow" target="_blank"> ByteBufferUtil.java;

編寫一個測試類,對 ByteBuffer 的常用方法進行測試:

public class TestByteBufferReadWrite {
    public static void main(String[] args) {
        ByteBuffer buffer = ByteBuffer.allocate(10);

        // 寫入一個字節(jié)的數(shù)據(jù)
        buffer.put((byte) 0x73);
        debugAll(buffer);

        // 寫入一組五個字節(jié)的數(shù)據(jù)
        buffer.put(new byte[]{0x69, 0x64, 0x69, 0x6f, 0x74});
        debugAll(buffer);

        // 獲取數(shù)據(jù)
        buffer.flip();
        ByteBufferUtil.debugAll(buffer);
        System.out.println((char) buffer.get());
        System.out.println((char) buffer.get());
        ByteBufferUtil.debugAll(buffer);

        // 使用 compact 切換寫模式
        buffer.compact();
        ByteBufferUtil.debugAll(buffer);

        // 再次寫入
        buffer.put((byte) 102);
        buffer.put((byte) 103);
        ByteBufferUtil.debugAll(buffer);
    }
}

運行結果:

// 向緩沖區(qū)寫入了一個字節(jié)的數(shù)據(jù),此時 postition 為 1;
+--------+-------------------- all ------------------------+----------------+
position: [1], limit: [10]
         +-------------------------------------------------+
         |  0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f |
+--------+-------------------------------------------------+----------------+
|00000000| 73 00 00 00 00 00 00 00 00 00                   |s.........      |
+--------+-------------------------------------------------+----------------+

// 向緩沖區(qū)寫入了五個字節(jié)的數(shù)據(jù),此時 postition 為 6;
+--------+-------------------- all ------------------------+----------------+
position: [6], limit: [10]
         +-------------------------------------------------+
         |  0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f |
+--------+-------------------------------------------------+----------------+
|00000000| 73 69 64 69 6f 74 00 00 00 00                   |sidiot....      |
+--------+-------------------------------------------------+----------------+

// 調用 flip() 切換至讀模式,此時 position 為 0,表示從第 0 個數(shù)據(jù)開始讀取;
// 同時要注意,此時的 limit 為 6,表示 position=6 時內容就讀完了;
+--------+-------------------- all ------------------------+----------------+
position: [0], limit: [6]
         +-------------------------------------------------+
         |  0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f |
+--------+-------------------------------------------------+----------------+
|00000000| 73 69 64 69 6f 74 00 00 00 00                   |sidiot....      |
+--------+-------------------------------------------------+----------------+

// 讀取兩個字節(jié)的數(shù)據(jù);
s
i

// 此時 position 變?yōu)?2; 
+--------+-------------------- all ------------------------+----------------+
position: [2], limit: [6]
         +-------------------------------------------------+
         |  0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f |
+--------+-------------------------------------------------+----------------+
|00000000| 73 69 64 69 6f 74 00 00 00 00                   |sidiot....      |
+--------+-------------------------------------------------+----------------+

// 調用 compact() 切換至寫模式,此時 position 及其后面的數(shù)據(jù)被壓縮到 ByteBuffer 的前面;
// 此時 position 為 4,會覆蓋之前的數(shù)據(jù); 
+--------+-------------------- all ------------------------+----------------+
position: [4], limit: [10]
         +-------------------------------------------------+
         |  0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f |
+--------+-------------------------------------------------+----------------+
|00000000| 64 69 6f 74 6f 74 00 00 00 00                   |diotot....      |
+--------+-------------------------------------------------+----------------+

// 再次寫入兩個字節(jié)的數(shù)據(jù),之前的 0x6f 0x74 被覆蓋;  
+--------+-------------------- all ------------------------+----------------+
position: [6], limit: [10]
         +-------------------------------------------------+
         |  0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f |
+--------+-------------------------------------------------+----------------+
|00000000| 64 69 6f 74 66 67 00 00 00 00                   |diotfg....      |
+--------+-------------------------------------------------+----------------+

Process finished with exit code 0

空間分配

在上述內容中,我們使用 allocate() 方法來為 ByteBuffer 分配空間,當然還有其他方法也可以為 ByteBuffer 分配空間;

public class TestByteBufferAllocate {
    public static void main(String[] args) {

        System.out.println(ByteBuffer.allocate(16).getClass());
        System.out.println(ByteBuffer.allocateDirect(16).getClass());
        /*
            class java.nio.HeapByteBuffer    - java 堆內存, 讀寫效率低, 受垃圾回收 GC 的影響;
            class java.nio.DirectByteBuffer  - 直接內存,讀寫效率高(少一次拷貝),不會受 GC 的影響;
                                             - 使用完后 需要徹底的釋放,以免內存泄露;
         */

    }
}

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

  • 調用 channel 的 read() 方法:channel.read(buf)
  • 調用 buffer 的 put() 方法:buffer.put((byte) 127);

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

rewind

public Buffer rewind() {
    position = 0;
    mark = -1;
    return this;
}

rewind() 的作用是將 position 設置為0,這意味著下一次讀取或寫入操作將從緩沖區(qū)的開頭開始。

@Test
public void testRewind() {
    // rewind 從頭開始讀
    ByteBuffer buffer = ByteBuffer.allocate(16);
    buffer.put(new byte[]{'s', 'i', 'd', 'i', 'o', 't'});
    buffer.flip();
    buffer.get(new byte[6]);
    debugAll(buffer);
    buffer.rewind();
    System.out.println((char) buffer.get());
}

運行結果:

+--------+-------------------- all ------------------------+----------------+
position: [6], limit: [6]
         +-------------------------------------------------+
         |  0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f |
+--------+-------------------------------------------------+----------------+
|00000000| 73 69 64 69 6f 74 00 00 00 00 00 00 00 00 00 00 |sidiot..........|
+--------+-------------------------------------------------+----------------+

// 從頭讀到第一個字符 's';
s

Process finished with exit code 0

mark 和 reset

public Buffer mark() {
    mark = position;
    return this;
}

mark() 用于在緩沖區(qū)中設置標記;

public Buffer reset() {
    int m = mark;
    if (m < 0)
        throw new InvalidMarkException();
    position = m;
    return this;
}

reset() 用于返回到標記位置;

@Test
public void testMarkAndReset() {
    // mark 做一個標記,用于記錄 position 的位置;reset 是將 position 重置到 mark 的位置;
    ByteBuffer buffer = ByteBuffer.allocate(16);
    buffer.put(new byte[]{'s', 'i', 'd', 'i', 'o', 't'});
    buffer.flip();
    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());
    debugAll(buffer);
    buffer.reset();     // 將 position 重置到索引2;
    debugAll(buffer);
    System.out.println((char) buffer.get());
    System.out.println((char) buffer.get());
}

運行結果:

s
i
d
i

+--------+-------------------- all ------------------------+----------------+
position: [4], limit: [6]
         +-------------------------------------------------+
         |  0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f |
+--------+-------------------------------------------------+----------------+
|00000000| 73 69 64 69 6f 74 00 00 00 00 00 00 00 00 00 00 |sidiot..........|
+--------+-------------------------------------------------+----------------+

// position 從4重置為2;
+--------+-------------------- all ------------------------+----------------+
position: [2], limit: [6]
         +-------------------------------------------------+
         |  0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f |
+--------+-------------------------------------------------+----------------+
|00000000| 73 69 64 69 6f 74 00 00 00 00 00 00 00 00 00 00 |sidiot..........|
+--------+-------------------------------------------------+----------------+

d
i

Process finished with exit code 0

get(i)

get(i) 不會改變讀索引的位置;

@Test
public void testGet_i() {
    // get(i) 不會改變讀索引的位置;
    ByteBuffer buffer = ByteBuffer.allocate(16);
    buffer.put(new byte[]{'s', 'i', 'd', 'i', 'o', 't'});
    buffer.flip();
    System.out.println((char) buffer.get(2));
    debugAll(buffer);
}

運行結果:

d
+--------+-------------------- all ------------------------+----------------+
position: [0], limit: [6]
         +-------------------------------------------------+
         |  0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f |
+--------+-------------------------------------------------+----------------+
|00000000| 73 69 64 69 6f 74 00 00 00 00 00 00 00 00 00 00 |sidiot..........|
+--------+-------------------------------------------------+----------------+

Process finished with exit code 0

字符串與 ByteBuffer 的相互轉換

getBytes

public byte[] getBytes() {
    return StringCoding.encode(coder(), value);
}

字符串調用 getByte() 方法獲得 byte 數(shù)組,將 byte 數(shù)組放入 ByteBuffer 中:

@Test
public void testGetBytes() {
    ByteBuffer buffer = ByteBuffer.allocate(16);
    buffer.put("sidiot".getBytes());
    debugAll(buffer);
}

運行結果:

+--------+-------------------- all ------------------------+----------------+
position: [6], limit: [16]
         +-------------------------------------------------+
         |  0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f |
+--------+-------------------------------------------------+----------------+
|00000000| 73 69 64 69 6f 74 00 00 00 00 00 00 00 00 00 00 |sidiot..........|
+--------+-------------------------------------------------+----------------+

Process finished with exit code 0

charset

public final ByteBuffer encode(String str) {
    return encode(CharBuffer.wrap(str));
}

通過 StandardCharsets 的 encode() 方法獲得 ByteBuffer,此時獲得的 ByteBuffer 為讀模式,無需通過 flip() 切換模式:

@Test
public void testCharset() {
    ByteBuffer buffer = StandardCharsets.UTF_8.encode("sidiot");
    debugAll(buffer);

    System.out.println(StandardCharsets.UTF_8.decode(buffer));
}

運行結果:

+--------+-------------------- all ------------------------+----------------+
position: [0], limit: [6]
         +-------------------------------------------------+
         |  0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f |
+--------+-------------------------------------------------+----------------+
|00000000| 73 69 64 69 6f 74                               |sidiot          |
+--------+-------------------------------------------------+----------------+

sidiot

Process finished with exit code 0

wrap

public static ByteBuffer wrap(byte[] array,
                                int offset, int length)
{
    try {
        return new HeapByteBuffer(array, offset, length, null);
    } catch (IllegalArgumentException x) {
        throw new IndexOutOfBoundsException();
    }
}

將字節(jié)數(shù)組傳給 wrap() 方法,通過該方法獲得 ByteBuffer,此時的 ByteBuffer 同樣為讀模式:

@Test
public void testWrap() {
    ByteBuffer buffer = ByteBuffer.wrap("sidiot".getBytes());
    debugAll(buffer);
}

運行結果:

+--------+-------------------- all ------------------------+----------------+
position: [0], limit: [6]
         +-------------------------------------------------+
         |  0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f |
+--------+-------------------------------------------------+----------------+
|00000000| 73 69 64 69 6f 74                               |sidiot          |
+--------+-------------------------------------------------+----------------+

Process finished with exit code 0

后記

參考:

以上就是Netty NIO之ByteBuffer類基礎學習的詳細內容,更多關于Netty框架ByteBuffer類的資料請關注腳本之家其它相關文章!

相關文章

  • SpringSecurity?表單登錄的實現(xiàn)

    SpringSecurity?表單登錄的實現(xiàn)

    本文主要介紹了SpringSecurity?表單登錄的實現(xiàn),文中通過示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2021-12-12
  • Java繪圖技術基礎(實例講解)

    Java繪圖技術基礎(實例講解)

    下面小編就為大家?guī)硪黄狫ava繪圖技術基礎(實例講解)。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2017-08-08
  • 詳解Java中Javassist的使用

    詳解Java中Javassist的使用

    常用的一些操作字節(jié)碼的技術有?ASM、AspectJ、Javassist?等。本文主要為大家介紹了Javassist使用的相關知識,感興趣的小伙伴可以了解一下
    2023-04-04
  • 使用Java Minio搭建自己的文件系統(tǒng)詳解

    使用Java Minio搭建自己的文件系統(tǒng)詳解

    這篇文章主要介紹了使用Java Minio搭建自己的文件系統(tǒng)的方法,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下
    2021-09-09
  • Java多線程之CAS算法實現(xiàn)線程安全

    Java多線程之CAS算法實現(xiàn)線程安全

    這篇文章主要介紹了java中如何通過CAS算法實現(xiàn)線程安全,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,下面小編和大家一起來學習一下吧
    2019-05-05
  • spring boot metrics監(jiān)控指標使用教程

    spring boot metrics監(jiān)控指標使用教程

    這篇文章主要為大家介紹了針對應用監(jiān)控指標暴露spring boot metrics監(jiān)控指標的使用教程,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步
    2022-02-02
  • Java獲取Excel中圖片所在的行和列坐標位置

    Java獲取Excel中圖片所在的行和列坐標位置

    這篇文章主要介紹了Java獲取Excel中圖片所在的行和列坐標位置,文章圍繞主題展開詳細的內容介紹,具有一定的參考價值,需要的朋友可以參考一下
    2022-08-08
  • java中extends與implements的區(qū)別淺談

    java中extends與implements的區(qū)別淺談

    java中extends與implements的區(qū)別淺談,需要的朋友可以參考一下
    2013-03-03
  • java IO 文件操作方法總結

    java IO 文件操作方法總結

    這篇文章主要介紹了java IO 文件操作方法總結的相關資料,需要的朋友可以參考下
    2017-04-04
  • Springboot整合junit過程解析

    Springboot整合junit過程解析

    這篇文章主要介紹了Springboot整合junit過程解析,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下
    2020-05-05

最新評論