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博客 物理层、数据链路层、网络层及传输层…