【C++框架#2】gflags 和 gtest 安裝使用

spdlog 安裝和使用

1. 概述

介紹:spdlog 是一個高性能、超快速、零配置的 C++ 日志庫,它旨在提供簡潔的 API 和豐富的功能,同時保持高性能的日志記錄。它支持多種輸出目標、格式化選項、線程安全以及異步日志記錄。

  • github 鏈接:https://github.com/gabime/spdlog

特點

  • 高性能:spdlog 專為速度而設計,即使在高負載情況下也能保持良好的性能
  • 零配置:無需復雜的配置,只需包含頭文件即可在項目中使用。
  • 異步日志:支持異步日志記錄,減少對主線程的影響。
  • 格式化:支持自定義日志消息的格式化,包括時間、線程ID、日志級別等
  • 多平臺:跨平臺兼容,支持 Windows、Linux、macOS 等操作系統。
  • 豐富的 API:提供豐富的日志級別和操作符重載,方便記錄各種類型的日志。

安裝

sudo apt-get install libspdlog-dev

2. 使用

日志輸出等級枚舉

namespace level { enum level_enum : int { trace = SPDLOG_LEVEL_TRACE, debug = SPDLOG_LEVEL_DEBUG, info = SPDLOG_LEVEL_INFO, warn = SPDLOG_LEVEL_WARN, err = SPDLOG_LEVEL_ERROR, critical = SPDLOG_LEVEL_CRITICAL, off = SPDLOG_LEVEL_OFF, n_levels }; 
} 

日志輸出格式自定義

logger->set_pattern("%Y-%m-%d %H:%M:%S [%t] [%-7l] %v"); %t - 線程 ID(Thread ID)
%n - 日志器名稱(Logger name)
%l - 日志級別名稱(Level name),如 INFO, DEBUG, ERROR 等
%v - 日志內容(message)
%Y - 年(Year)。 
%m - 月(Month)。 
%d - 日(Day)。 
%H - 小時(24-hour format)。 
%M - 分鐘(Minute)。 
%S - 秒(Second)

① 日志記錄器類:創建一個基本的日志記錄器,并設置 日志級別輸出模式

namespace spdlog { 
class logger { logger(std::string name); logger(std::string name, sink_ptr single_sink) logger(std::string name, sinks_init_list sinks) void set_level(level::level_enum log_level); void set_formatter(std::unique_ptr<formatter> f); template<typename... Args> void trace(fmt::format_string<Args...> fmt, Args &&...args) template<typename... Args> void debug(fmt::format_string<Args...> fmt, Args &&...args) template<typename... Args> void info(fmt::format_string<Args...> fmt, Args &&...args) template<typename... Args> void warn(fmt::format_string<Args...> fmt, Args &&...args) template<typename... Args> void error(fmt::format_string<Args...> fmt, Args &&...args) template<typename... Args> void critical(fmt::format_string<Args...> fmt, Args &&...args) void flush(); //刷新日志 //策略刷新--觸發指定等級日志的時候立即刷新日志的輸出 void flush_on(level::level_enum log_level); 
};

② 異步日志記錄類:為了異步記錄日志,可以使用 spdlog::async_logger

class async_logger final : public logger { async_logger(std::string logger_name, sinks_init_list sinks_list, std::weak_ptr<details::thread_pool> tp, async_overflow_policy overflow_policy = async_overflow_policy::block);async_logger(std::string logger_name, sink_ptr single_sink, std::weak_ptr<details::thread_pool> tp, async_overflow_policy overflow_policy = async_overflow_policy::block); // 異步日志輸出需要異步工作線程的支持,這里是線程池類 
class SPDLOG_API thread_pool { thread_pool(size_t q_max_items, size_t threads_n, std::function<void()> on_thread_start, std::function<void()> on_thread_stop); thread_pool(size_t q_max_items, size_t threads_n, std::function<void()> on_thread_start); thread_pool(size_t q_max_items, size_t threads_n); }; 
} std::shared_ptr<spdlog::details::thread_pool> thread_pool() { return details::registry::instance().get_tp(); 
} // 默認線程池的初始化接口 
inline void init_thread_pool(size_t q_size, size_t thread_count) 
auto async_logger = spdlog::async_logger_mt("async_logger", "logs/async_log.txt"); 
async_logger->info("This is an asynchronous info message"); 

③ 日志記錄器工廠類

using async_factory = async_factory_impl<async_overflow_policy::block>; template<typename Sink, typename... SinkArgs> 
inline std::shared_ptr<spdlog::logger> create_async( std::string logger_name, SinkArgs &&...sink_args) // 創建一個彩色輸出到標準輸出的日志記錄器,默認工廠創建同步日志記錄器 
template<typename Factory = spdlog::synchronous_factory> 
std::shared_ptr<logger> stdout_color_mt(const std::string &logger_name, color_mode mode = color_mode::automatic); 
// 標準錯誤 
template<typename Factory = spdlog::synchronous_factory> 
std::shared_ptr<logger> stderr_color_mt(const std::string &logger_name, color_mode mode = color_mode::automatic); 
// 指定文件 
template<typename Factory = spdlog::synchronous_factory> 
std::shared_ptr<logger> basic_logger_mt(const std::string &logger_name, const filename_t &filename, bool truncate = false, const file_event_handlers &event_handlers = {}) 
// 循環文件 
template<typename Factory = spdlog::synchronous_factory> 
std::shared_ptr<logger> rotating_logger_mt(const std::string &logger_name, const filename_t &filename, size_t max_file_size, size_t max_files, bool rotate_on_open = false) 
... 

④ 日志落地類

namespace spdlog { 
namespace sinks { 
class SPDLOG_API sink 
{ 
public: virtual ~sink() = default; virtual void log(const details::log_msg &msg) = 0; virtual void flush() = 0; virtual void set_pattern(const std::string &pattern) = 0; virtual void set_formatter(std::unique_ptr<spdlog::formatter> sink_formatter) = 0; void set_level(level::level_enum log_level); 
}; using stdout_sink_mt; 
using stderr_sink_mt; 
using stdout_color_sink_mt; 
using stderr_color_sink_mt; // 滾動日志文件-超過一定大小則自動重新創建新的日志文件 sink_ptr rotating_file_sink(filename_t base_filename, std::size_t max_size, std::size_t max_files, bool rotate_on_open = false, const file_event_handlers &event_handlers = ); 
using rotating_file_sink_mt = rotating_file_sink<std::mutex>; // 普通的文件落地對啊 ing 
sink_ptr basic_file_sink(const filename_t &filename, bool truncate = false, const file_event_handlers &event_handlers = {}); using basic_file_sink_mt = basic_file_sink<std::mutex>; 
using kafka_sink_mt = kafka_sink<std::mutex>; 
using mongo_sink_mt = mongo_sink<std::mutex>; 
using tcp_sink_mt = tcp_sink<std::mutex>; 
using udp_sink_mt = udp_sink<std::mutex>; ..... //*_st:單線程版本,不用加鎖,效率更高。 //*_mt:多線程版本,用于多線程程序是線程安全的。 
} 
}

全局接口

void set_level(level::level_enum log_level); 		// 輸出等級設置接口 
void flush_every(std::chrono::seconds interval); 	// 日志刷新策略-每隔 N 秒刷新一次 
void flush_on(level::level_enum log_level);			// 日志刷新策略-觸發指定等級立即刷新  

3. 代碼樣例

3.1 同步日志

樣例代碼

#include <spdlog/spdlog.h>
#include <spdlog/sinks/stdout_color_sinks.h> 
#include <spdlog/sinks/basic_file_sink.h> 
#include <iostream>int main(){// 1. 設置全局刷新策略 spdlog::flush_every(std::chrono::seconds(1)); // 每秒刷新spdlog::flush_on(spdlog::level::debug);       // debug 及以上級別立即刷新spdlog::set_level(spdlog::level::debug);      // 全局日志等級// 2. 創建同步日志器 -- 工廠接口默認創建的就是同步日志器// auto logger = spdlog::stdout_color_mt("default-logger"); // 標準輸出auto logger = spdlog::basic_logger_mt("file-logger", "sync.log");   // 文件輸出// %v: 日志的輸出內容logger->set_pattern("[%n][%H:%M:%S][%t][%-7l] %v");logger->trace("你好! {}", "千璇"); // trace 默認不會輸出(因為 level >= debug 才輸出)logger->debug("你好! {}", "千璇");logger->info("你好! {}", "千璇");logger->warn("你好! {}", "千璇");logger->error("你好! {}", "千璇");logger->critical("你好! {}", "千璇");std::cout << "日志輸出演示完畢!\n";// 3. 清理所有 logger,確保 flush 并釋放資源spdlog::drop_all(); return 0;
}

結果

lighthouse@VM-8-10-ubuntu:spdlog$ ./sync
日志輸出演示完畢!# 對應日志文件 sync.log 內容如下
[file-logger][21:35:34][1909301][debug  ] 你好! 千璇
[file-logger][21:35:34][1909301][info   ] 你好! 千璇
[file-logger][21:35:34][1909301][warning] 你好! 千璇
[file-logger][21:35:34][1909301][error  ] 你好! 千璇
[file-logger][21:35:34][1909301][critical] 你好! 千璇
3.2 異步日志演示
#include <spdlog/spdlog.h>
#include <spdlog/sinks/stdout_color_sinks.h> 
#include <spdlog/async.h>
#include <iostream>int main(){// 1. 設置全局刷新策略 spdlog::flush_every(std::chrono::seconds(1)); // 每秒刷新spdlog::flush_on(spdlog::level::debug);       // debug 及以上級別立即刷新spdlog::set_level(spdlog::level::debug);      // 全局日志等級// 2. 初始化異步日志輸出線程配置// void init_thread_pool(size_t q_size, size_t thread_count)spdlog::init_thread_pool(3072, 1);// 3. 創建異步日志器auto logger = spdlog::stdout_color_mt<spdlog::async_factory>("async-logger");logger->set_pattern("[%n][%H:%M:%S][%t][%-7l] %v");logger->trace("你好! {}", "千璇"); // trace 默認不會輸出(因為 level >= debug 才輸出)logger->debug("你好! {}", "千璇");logger->info("你好! {}", "千璇");logger->warn("你好! {}", "千璇");logger->error("你好! {}", "千璇");logger->critical("你好! {}", "千璇");std::cout << "日志輸出演示完畢!\n";// 3. 清理所有 logger,確保 flush 并釋放資源spdlog::drop_all(); return 0;
}

輸出

日志輸出演示完畢!
[async-logger][22:02:34][1915206][debug  ] 你好! 千璇
[async-logger][22:02:34][1915206][info   ] 你好! 千璇
[async-logger][22:02:34][1915206][warning] 你好! 千璇
[async-logger][22:02:34][1915206][error  ] 你好! 千璇
[async-logger][22:02:34][1915206][critical] 你好! 千璇

4. 二次封裝

原因:

  1. 避免單例鎖沖突:spdlog 內部有一個單例模式,其有一個默認的日志器,我們可以通過該單例獲取日志器再進行日志的輸出,但是 如果每次日志輸出都需要從單例獲取對象,容易造成一些沖突導致性能下降,因此一般不直接使用其默認日期,而是創建一個 全局的線程安全日志器 進行使用
  2. spdlog 的日志輸出沒有關于行號、文件名的輸出,使用 普通接口 沒有無法實現,因此需要使用 宏 進行一次二次封裝對 文件名和行號進行輸出
  3. 封裝初始化接口,便于使用:假設當前一個程序運行的時候,如果它是一個我們的調試模式,那就直接在我們的標準輸出里邊去進行一個日志的輸出,但是如果已經線上運行了,那我們就不需要再去進行標準輸出的輸出了,而是希望能夠將其通過 配置文件 的配置,將日志輸出到文件當中,而這里的操作希望能夠封裝起來(操作:調試模式輸出到 標準輸出 / 文件),便于我們代碼當中的使用

思想

  1. 封裝出一個全局接口,用戶進行日志器的創建和初始化,初始化接口接收參數如下:
    1. 運行模式 – bool
    2. 輸出文件名 – 用于發布模式
    3. 輸出日志等級 – 用于發布模式
  2. 對日志輸出的接口,進行宏的封裝,加入文件名行號的輸出

logger.hpp 代碼如下

#include <spdlog/spdlog.h>
#include <spdlog/sinks/stdout_color_sinks.h> 
#include <spdlog/sinks/basic_file_sink.h> 
#include <spdlog/async.h>
#include <iostream>// mode: true 發布 / false 調試
std::shared_ptr<spdlog::logger> g_default_logger;
void init_logger(bool mode, const std::string& filename, int32_t level){// 如果是調試, 則標準輸出日志器輸出等級最低if(!mode){g_default_logger = spdlog::stdout_color_mt("default-logger");g_default_logger->set_level(spdlog::level::level_enum::trace);g_default_logger->flush_on(spdlog::level::level_enum::trace);}else{// 發布: 輸出等級根據參數而定g_default_logger = spdlog::basic_logger_mt("file-logger", filename);g_default_logger->set_level((spdlog::level::level_enum)level);g_default_logger->flush_on((spdlog::level::level_enum)level);}g_default_logger->set_pattern("[%n][%H:%M:%S][%t][%-8l]%v");
}#define LOG_TRACE(format, ...) g_default_logger->trace(std::string("[{}:{} ") + format, __FILE__, __LINE__, ##__VA_ARGS__)
#define LOG_DEBUG(format, ...) g_default_logger->debug(std::string("[{}:{}] ") + format, __FILE__, __LINE__, ##__VA_ARGS__)
#define LOG_INFO(format, ...) g_default_logger->info(std::string("[{}:{}] ") + format, __FILE__, __LINE__, ##__VA_ARGS__)
#define LOG_WARN(format, ...) g_default_logger->warn(std::string("[{}:{}] ") + format, __FILE__, __LINE__, ##__VA_ARGS__)
#define LOG_ERROR(format, ...) g_default_logger->error(std::string("[{}:{}] ") + format, __FILE__, __LINE__, ##__VA_ARGS__)
#define LOG_FATAL(format, ...) g_default_logger->critical(std::string("[{}:{}] ") + format, __FILE__, __LINE__, ##__VA_ARGS__)

測試代碼如下

#include "logger.hpp"
#include <gflags/gflags.h>DEFINE_bool(run_mode, false, "程序運行模式: false-調試; true 發布");
DEFINE_string(log_file, "", "發布模式下指定日志的輸出文件");
DEFINE_int32(log_level, 0, "發布模式下指定日志的輸出等級");int main(int argc, char* argv[]){google::ParseCommandLineFlags(&argc, &argv, true);init_logger(FLAGS_run_mode, FLAGS_log_file, FLAGS_log_level);LOG_DEBUG("你好: {}", "island");LOG_INFO("你好: {}", "island");LOG_WARN("你好: {}", "island");LOG_ERROR("你好: {}", "island");LOG_FATAL("你好: {}", "island");LOG_DEBUG("這是一個測試");  // 這里用 ##__VA_ARGS__ 可以省略參數, 但是如果用 __VA_ARGS__ 就不行return -1;
}

結果輸出

lighthouse@VM-8-10-ubuntu:spdlog$ ./main
[default-logger][23:27:45][1934415][debug   ][main.cc:12] 你好: island
[default-logger][23:27:45][1934415][info    ][main.cc:13] 你好: island
[default-logger][23:27:45][1934415][warning ][main.cc:14] 你好: island
[default-logger][23:27:45][1934415][error   ][main.cc:15] 你好: island
[default-logger][23:27:45][1934415][critical][main.cc:16] 你好: island
[default-logger][23:27:45][1934415][debug   ][main.cc:17] 這是一個測試
lighthouse@VM-8-10-ubuntu:spdlog$ ./main --run_mode=true --log_file=./main.log --log_level=3

5. spdlog vs glog

glog 和 spdlog 都是流行的 C++ 日志庫,它們各自具有不同的特點和優勢。以下是對這兩個庫的對比分析,包括性能測試的結果和使用場景的考量。

glog:glog 是由 Google 開發的一個開源 C++ 日志庫,它提供了豐富的日志功能,包括多種日志級別、條件日志記錄、日志文件管理、信號處理、自定義日志格式等。glog 默認情況下是同步記錄日志的,這意味著每次寫日志操作都會阻塞直到日志數據被寫入磁盤。

  • 性能:根據性能對比測試分析,glog 在同步調用的場景下的性能較spdlog 慢。在一臺低配的服務器上,glog 耗時 1.027 秒處理十萬筆日志數據,而在固態硬盤上的耗時為 0.475 秒。

spdlog:spdlog 是一個開源的、高性能的 C++ 日志庫,它支持異步日志記錄,允許在不影響主線程的情況下進行日志寫入。spdlog 旨在提供零配置的用戶體驗,只需包含頭文件即可使用。它還支持多種輸出目標、格式化選項和線程安全。

  • 性能:在同樣的性能測試中,spdlog 在同步調用的場景下比 glog 快。在低配服務器上的耗時為 0.135 秒,而在固態硬盤上的耗時為0.057秒。此外,spdlog 還提供了異步日志記錄的功能,其簡單異步模式的耗時為 0.158 秒。

對比總結

  • 性能:從性能測試結果來看,spdlog 在同步調用場景下的性能優于 glog。當涉及到大量日志數據時,spdlog 顯示出更快的處理速度。
  • 異步日志spdlog 支持異步日志記錄,這在處理高負載應用程序時非常有用,可以減少日志操作對主線程的影響。
  • 易用性spdlog 提供了更簡單的集成和配置方式,只需包含頭文件即可使用,而glog 可能需要額外的編譯和配置步驟。
  • 功能glog 提供了一些特定的功能,如條件日志記錄和信號處理,這些在某些場景下可能非常有用。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/95392.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/95392.shtml
英文地址,請注明出處:http://en.pswp.cn/web/95392.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

平衡掌控者-游戲數值戰斗設計

一、有效生命值1、計算公式有效生命生命值/&#xff08;1-傷害減免率&#xff09;/&#xff08;1-閃避率&#xff09;2、前搖和后搖對數值來說&#xff0c;戰斗由兩大模塊組成&#xff0c;一個是戰斗公式生效前的戰斗攻擊流程&#xff0c;一個是戰斗公式與自身流程。比如說&…

使用DataLoader加載本地數據 食物分類案例

目錄 一.食物分類案例 1..整合訓練集測試集文檔 2.導入相關的庫 3.設置圖片數據的格式轉換 3.數據處理 4.數據打包 5.定義卷積神經網絡 6.創建模型 7.訓練和測試方法定義 8.損失函數和優化器 9.訓練模型&#xff0c;測試準確率 10.測試模型 之前我們DataLoader加載…

從零開始的python學習——函數(2)

? ? ? ? ? づ?ど &#x1f389; 歡迎點贊支持&#x1f389; 個人主頁&#xff1a;勵志不掉頭發的內向程序員&#xff1b; 專欄主頁&#xff1a;python學習專欄&#xff1b; 文章目錄 前言 一、變量作用域 二、函數執行過程 三、鏈式調用 四、嵌套調用 五、函數遞歸 六、…

RAG 的完整流程是怎么樣的?

RAG&#xff08;檢索增強生成&#xff09;的完整流程可分為5個核心階段&#xff1a;數據準備&#xff1a;清洗文檔、分塊處理&#xff08;如PDF轉文本切片&#xff09;&#xff1b;向量化&#xff1a;使用嵌入模型&#xff08;如BERT、BGE&#xff09;將文本轉為向量&#xff1…

研發文檔版本混亂的根本原因是什么,怎么辦

研發文檔版本混亂的根本原因通常包括缺乏統一的版本控制制度、團隊協作不暢、文檔管理工具使用不當以及項目需求頻繁變化等因素。這些問題使得研發團隊在日常工作中容易出現文檔版本混亂的情況&#xff0c;導致信息的不一致性、溝通不暢以及開發進度的延誤。為了解決這一問題&a…

ChartView的基本使用

Qt ChartView&#xff08;準確類名 QChartView&#xff09;是 Qt Charts 模塊里最常用的圖表顯示控件。一句話概括&#xff1a;“它把 QChart 畫出來&#xff0c;并自帶縮放、平移、抗鋸齒等交互能力”。QML ChartView 簡介&#xff08;一句話先記住&#xff1a;ChartView 是 Q…

系統擴展策略

1、核心指導思想&#xff1a;擴展立方體 在討論具體策略前&#xff0c;先了解著名的擴展立方體&#xff08;Scale Cube&#xff09;&#xff0c;它定義了三種擴展維度&#xff1a; X軸&#xff1a;水平復制&#xff08;克隆&#xff09; 策略&#xff1a;通過負載均衡器&#…

HBuilder X 4.76 開發微信小程序集成 uview-plus

簡介 本文記錄了在HBuilder中創建并配置uni-app項目的完整流程。 首先創建項目并測試運行&#xff0c;確認無報錯后添加uView-Plus組件庫。 隨后修改了main.js、uni.scss、App.vue等核心文件&#xff0c;配置manifest.json并安裝dayjs、clipboard等依賴庫。 通過調整vite.c…

第4章:內存分析與堆轉儲

本章概述內存分析是 Java 應用性能調優的核心環節之一。本章將深入探討如何使用 VisualVM 進行內存分析&#xff0c;包括堆內存監控、堆轉儲生成與分析、內存泄漏檢測以及內存優化策略。通過本章的學習&#xff0c;你將掌握識別和解決內存相關問題的專業技能。學習目標理解 Jav…

面經分享一:分布式環境下的事務難題:理論邊界、實現路徑與選型邏輯

一、什么是分布式事務? 分布式事務是指事務的參與者、支持事務的服務器、資源服務器以及事務管理器分別位于不同的分布式系統的不同節點之上。 一個典型的例子就是跨行轉賬: 用戶從銀行A的賬戶向銀行B的賬戶轉賬100元。 這個操作包含兩個步驟: 從A賬戶扣減100元。 向B賬戶…

C++的演化歷史

C是一門這樣的編程語言&#xff1a; 兼顧底層計算機硬件系統和高層應用抽象機制從實際問題出發&#xff0c;注重零成本抽象、性能、可移植性、與C兼容語言特性和細節很多&#xff0c;學習成本較高&#xff0c;是一門讓程序員很難敢說精通的語言 C是自由的&#xff0c;支持5種…

Qt6實現繪圖工具:12種繪圖工具全家桶!這個項目滿足全部2D場景

項目概述 一個基于Qt框架開發的專業繪圖工具,實現了完整的2D圖形繪制、編輯和管理功能。該項目采用模塊化設計,包含圖形繪制、圖層管理、命令模式撤銷重做、用戶界面等多個子系統,是學習現代C++和Qt框架的最佳實踐。 核心功能特性 12種專業繪圖工具 多圖層繪制系統 完整的…

Linux驅動開發學習筆記

第1章 Linux驅動開發的方式mmap映射型設計方法。【不推薦】將芯片上的物理地址映射到用戶空間的虛擬地址上&#xff0c;用戶操作虛擬地址來操作硬件。使用文件操作集(file_operatiopns)設計方法。【極致推薦】platfrom總線型設置方法。【比較流行】設備樹。【推薦】第2章 Linux…

mac中進行適用于IOS的靜態庫構建

前沿: 進行C開發完成之后,需要將代碼編譯成靜態庫,并且在IOS的手機系統中執行,因此記錄該實現過程. 1主要涉及內容 1.1 整體文件架構 gongyonglocalhost Attention % tree -L 2 . ├── build │ ├── __.SYMDEF │ ├── cmake_install.cmake │ ├── CMakeCache…

C++二維數組的前綴和

C二維數組的前綴和的方法很簡單&#xff0c;可以利用公式res[i][j]arr[i][j]res[i-1][j]prefix[i][j-1]-res[i-1][j-1]。輸入4 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16輸出1 3 6 10 6 14 24 36 15 33 54 78 28 60 96 136#include<bits/stdc.h> using namespace std; int…

Wifi開發上層學習1:實現一個wifi搜索以及打開的app

Wifi開發上層學習1&#xff1a;實現一個wifi搜索以及打開的app 文章目錄Wifi開發上層學習1&#xff1a;實現一個wifi搜索以及打開的app背景demo實現1.添加系統權限以及系統簽名2.布局配置3.邏輯設計3.1 wifi開關的實現3.2 wifi掃描功能3.3 連接wifi總結一、WiFi 狀態控制接口二…

【DSP28335 入門教程】定時器中斷:為你的系統注入精準的“心跳”

大家好&#xff0c;歡迎來到 DSP28335 的核心精講系列。我們已經掌握了如何通過外部中斷來響應“外部事件”&#xff0c;但系統內部同樣需要一個精準的節拍器來處理“內部周期性任務”。單純依靠 DELAY_US() 這樣的軟件延時&#xff0c;不僅精度差&#xff0c;而且會在延時期間…

從零開始:用代碼解析區塊鏈的核心工作原理

區塊鏈技術被譽為信任的機器&#xff0c;它正在重塑金融、供應鏈、數字身份等眾多領域。但對于許多開發者來說&#xff0c;它仍然像一個神秘的黑盒子。今天&#xff0c;我們將拋開炒作的泡沫&#xff0c;深入技術本質&#xff0c;用大約100行Python代碼構建一個簡易的區塊鏈&am…

網絡通信IP細節

目錄 1.通信的NAT技術 2.代理服務器 3.內網穿透和內網打洞 1.通信的NAT技術 NAT技術產生的背景是我們為了解決IPV4不夠用的問題&#xff0c;NAT在通信的時候可以對IP將私網IP轉化為公網IP&#xff0c;全局IP要求唯一&#xff0c;但是私人IP不是唯一的。 將報文發給路由器進行…

國內真實的交換機、路由器和分組情況

一、未考慮擁擠情況理想狀態的網絡通信 前面我對骨干網&#xff1a; 宜春城區SDH網圖分析-CSDN博客 數據鏈路層MAC傳輸&#xff1a; 無線通信網卡底層原理&#xff08;Inter Wi-Fi AX201&#xff09;_ax201ngw是cnvio轉pci-e-CSDN博客 物理層、數據鏈路層、網絡層及傳輸層…