当前位置: 首页 > news >正文

Rust: 获取 MAC 地址方法大全

在 Rust 中获取 MAC 地址(物理地址)需要依赖第三方库,因为标准库不提供此功能。以下是全面的解决方案:

📦 添加依赖

使用 mac_address 库:

[dependencies]
mac_address = "1.1.0"

🔧 基础方法

1. 获取所有网络接口的 MAC 地址

use mac_address::get_mac_addresses;fn main() {match get_mac_addresses() {Ok(interfaces) => {println!("所有网络接口的 MAC 地址:");for interface in interfaces {println!("接口: {}, MAC: {}", interface.name, interface.address);}}Err(e) => eprintln!("获取 MAC 地址失败: {}", e),}
}

2. 获取主网络接口的 MAC 地址

use mac_address::get_mac_address;fn main() {match get_mac_address() {Ok(Some(mac)) => println!("主 MAC 地址: {}", mac),Ok(None) => println!("未找到 MAC 地址"),Err(e) => eprintln!("获取 MAC 地址失败: {}", e),}
}

🌐 高级用法

1. 获取特定接口的 MAC 地址

use mac_address::mac_address_by_name;fn main() {let interface_name = "eth0"; // 替换为你的接口名match mac_address_by_name(interface_name) {Ok(Some(mac)) => println!("{} 的 MAC 地址: {}", interface_name, mac),Ok(None) => println!("未找到 {} 的 MAC 地址", interface_name),Err(e) => eprintln!("获取 MAC 地址失败: {}", e),}
}

2. 过滤虚拟接口

use mac_address::get_mac_addresses;fn get_physical_macs() -> Vec<mac_address::MacAddress> {get_mac_addresses().unwrap_or_default().into_iter().filter(|iface| {// 过滤掉虚拟接口!iface.name.starts_with("docker") &&!iface.name.starts_with("veth") &&!iface.name.starts_with("br-") &&!iface.name.starts_with("lo")}).map(|iface| iface.address).collect()
}fn main() {println!("物理接口的 MAC 地址:");for mac in get_physical_macs() {println!("- {}", mac);}
}

3. 格式化 MAC 地址

use mac_address::MacAddress;fn format_mac(mac: &MacAddress) -> String {mac.bytes().iter().map(|b| format!("{:02X}", b)).collect::<Vec<_>>().join(":")
}fn main() {if let Ok(Some(mac)) = get_mac_address() {println!("格式化 MAC 地址: {}", format_mac(&mac));}
}

🧪 跨平台兼容性处理

1. 处理不同平台的接口命名

use mac_address::get_mac_addresses;fn get_primary_mac() -> Option<mac_address::MacAddress> {let interfaces = get_mac_addresses().ok()?;// 不同平台的优先级let preferred_names = if cfg!(target_os = "windows") {vec!["Ethernet", "Wi-Fi"]} else if cfg!(target_os = "macos") {vec!["en0", "en1"]} else {vec!["eth0", "wlan0"]};for name in preferred_names {if let Some(iface) = interfaces.iter().find(|i| i.name.contains(name)) {return Some(iface.address.clone());}}// 返回第一个非虚拟接口interfaces.into_iter().find(|iface| !iface.name.starts_with("lo")).map(|iface| iface.address)
}

2. Windows 特定处理

#[cfg(windows)]
fn get_windows_mac() -> Option<mac_address::MacAddress> {use winreg::enums::*;use winreg::RegKey;let hklm = RegKey::predef(HKEY_LOCAL_MACHINE);let interfaces = hklm.open_subkey("SYSTEM\\CurrentControlSet\\Control\\Class\\{4d36e972-e325-11ce-bfc1-08002be10318}").ok()?;for subkey_name in interfaces.enum_keys().filter_map(|k| k.ok()) {if let Ok(subkey) = interfaces.open_subkey(subkey_name) {if let Ok(addr) = subkey.get_value::<String, _>("NetCfgInstanceId") {if let Ok(Some(mac)) = mac_address_by_name(&addr) {return Some(mac);}}}}None
}

🛡️ 错误处理与日志

1. 自定义错误类型

use thiserror::Error;#[derive(Error, Debug)]
enum MacError {#[error("MAC 地址获取失败")]RetrievalError(#[from] mac_address::MacAddressError),#[error("未找到 MAC 地址")]NotFound,#[error("无效的 MAC 地址")]InvalidFormat,
}fn get_safe_mac() -> Result<String, MacError> {let mac = get_mac_address()?.ok_or(MacError::NotFound)?;if mac.bytes().iter().all(|&b| b == 0) {return Err(MacError::InvalidFormat);}Ok(format_mac(&mac))
}

2. 带重试的 MAC 获取

use std::thread;
use std::time::Duration;fn get_mac_with_retry(max_retries: u8) -> Option<mac_address::MacAddress> {for attempt in 0..=max_retries {match get_mac_address() {Ok(Some(mac)) => return Some(mac),Ok(None) => log::warn!("未找到 MAC 地址 (尝试 {}/{})", attempt + 1, max_retries),Err(e) => log::error!("获取失败: {} (尝试 {}/{})", e, attempt + 1, max_retries),}if attempt < max_retries {thread::sleep(Duration::from_secs(1));}}None
}

📊 MAC 地址实用函数

1. 验证 MAC 地址格式

fn is_valid_mac(mac: &str) -> bool {let parts: Vec<&str> = mac.split(':').collect();parts.len() == 6 && parts.iter().all(|p| p.len() == 2 && p.chars().all(|c| c.is_ascii_hexdigit()))
}fn main() {println!("AA:BB:CC:DD:EE:FF 是否有效? {}", is_valid_mac("AA:BB:CC:DD:EE:FF"));println!("无效MAC是否有效? {}", is_valid_mac("GG:HH:II:JJ:KK:LL"));
}

2. MAC 地址转换

fn mac_to_bytes(mac: &str) -> Option<[u8; 6]> {let parts: Vec<&str> = mac.split(':').collect();if parts.len() != 6 {return None;}let mut bytes = [0u8; 6];for (i, part) in parts.iter().enumerate() {bytes[i] = u8::from_str_radix(part, 16).ok()?;}Some(bytes)
}fn bytes_to_mac(bytes: &[u8; 6]) -> String {bytes.iter().map(|b| format!("{:02X}", b)).collect::<Vec<_>>().join(":")
}

📝 完整示例:系统信息工具

use mac_address::{get_mac_address, get_mac_addresses};
use sysinfo::{System, SystemExt};fn main() {let mut sys = System::new_all();sys.refresh_all();println!("系统信息:");println!("操作系统: {}", sys.name().unwrap_or_default());println!("内核版本: {}", sys.kernel_version().unwrap_or_default());println!("主机名: {}", sys.host_name().unwrap_or_default());match get_mac_address() {Ok(Some(mac)) => {println!("主 MAC 地址: {}", mac);}_ => println!("无法获取 MAC 地址"),}println!("\n所有网络接口:");for interface in sys.networks().iter() {println!("接口: {}", interface.0);println!("  IP地址: {:?}", interface.1.addresses);println!("  接收数据: {} MB", interface.1.total_received() / 1024 / 1024);println!("  发送数据: {} MB", interface.1.total_transmitted() / 1024 / 1024);if let Ok(Some(mac)) = mac_address_by_name(interface.0) {println!("  MAC地址: {}", mac);}}
}

⚠️ 注意事项

  1. 权限要求

    • Linux/macOS:需要 root 权限或 CAP_NET_ADMIN 能力
    • Windows:普通用户权限通常足够
  2. 虚拟接口

    • 虚拟接口(如 Docker、VPN)也会有 MAC 地址
    • 可能需要过滤掉非物理接口
  3. 隐私考虑

    • MAC 地址是敏感信息
    • 遵守 GDPR 等隐私法规
    • 必要时进行哈希处理
  4. 平台差异

    • macOS 的 en0 通常是主接口
    • Linux 的 eth0 是常见物理接口
    • Windows 的接口名可能包含 GUID

💡 最佳实践

  1. 明确接口名称

    // 优先选择已知物理接口
    let physical_interfaces = &["eth0", "en0", "wlan0"];for name in physical_interfaces {if let Ok(Some(mac)) = mac_address_by_name(name) {return Some(mac);}
    }
    
  2. 错误处理

    • 处理可能的 None 结果
    • 记录无法获取 MAC 的原因
    • 提供备用方案(如生成随机设备 ID)
  3. 格式化一致性

    // 统一使用大写带冒号格式
    let formatted = mac.to_string().to_uppercase();
    

通过以上方法,您可以在 Rust 应用中可靠地获取 MAC 地址。根据目标平台和具体需求选择最合适的实现方案。

http://www.lryc.cn/news/608973.html

相关文章:

  • 【MySQL进阶】------MySQL程序
  • 机器学习第三课之逻辑回归(三)LogisticRegression
  • 2025H1具身智能产业十大数据
  • Python训练营打卡 Day27
  • 【网络安全】日志文件格式
  • Linux 系统调用 stat 完全用例
  • Web前端文件上传安全与敏感数据安全处理
  • HiveMQ核心架构思维导图2024.9(Community Edition)
  • 反向代理+网关部署架构
  • 动态置信度调优实战:YOLOv11多目标追踪精度跃迁方案(附完整代码)
  • 关于corn
  • Android 之 图片加载(Fresco/Picasso/Glide)
  • 禁闭求生2 免安 中文 离线运行版
  • 【数据结构与算法】数据结构初阶:排序内容加餐(二)——文件归并排序思路详解(附代码实现)
  • 【LeetCode 热题 100】84. 柱状图中最大的矩形——(解法一)单调栈+三次遍历
  • 二叉树的锯齿形层次遍历
  • 9.苹果ios逆向-FridaHook-ios中的算法(CCCrypt)
  • CCF-GESP 等级考试 2025年6月认证C++一级真题解析
  • wordpress登陆前登陆后显示不同的顶部菜单
  • 最简单的零基础软件测试学习路线
  • Libevent(5)之使用教程(4)工具
  • k8s黑马教程笔记
  • 快速搭建一个非生产k8s环境
  • 【运维基础】Linux 硬盘分区管理
  • k8s+isulad 国产化技术栈云原生技术栈搭建4-添加worker节点
  • Hyper-V + Centos stream 9 搭建K8s集群(二)
  • k8s+isulad 国产化技术栈云原生技术栈搭建3-master节点安装
  • [硬件电路-148]:数字电路 - 什么是CMOS电平、TTL电平?还有哪些其他电平标准?发展历史?
  • Go语言实战案例:TCP服务器与客户端通信
  • 案例介绍|JSON数据格式的转换|pyecharts模块简介